surf.c.orig 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768
  1. /* See LICENSE file for copyright and license details.
  2. *
  3. * To understand surf, start reading main().
  4. */
  5. #include <sys/file.h>
  6. #include <sys/types.h>
  7. #include <sys/wait.h>
  8. #include <libgen.h>
  9. #include <limits.h>
  10. #include <pwd.h>
  11. #include <regex.h>
  12. #include <signal.h>
  13. #include <stdarg.h>
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <string.h>
  17. #include <unistd.h>
  18. #include <gdk/gdk.h>
  19. #include <gdk/gdkkeysyms.h>
  20. #include <gdk/gdkx.h>
  21. #include <glib/gstdio.h>
  22. #include <gtk/gtk.h>
  23. #include <gtk/gtkx.h>
  24. #include <JavaScriptCore/JavaScript.h>
  25. #include <webkit2/webkit2.h>
  26. #include <X11/X.h>
  27. #include <X11/Xatom.h>
  28. #include "arg.h"
  29. #define LENGTH(x) (sizeof(x) / sizeof(x[0]))
  30. #define CLEANMASK(mask) (mask & (MODKEY|GDK_SHIFT_MASK))
  31. #define SETB(p, s) [p] = { { .b = s }, }
  32. #define SETI(p, s) [p] = { { .i = s }, }
  33. #define SETV(p, s) [p] = { { .v = s }, }
  34. #define SETF(p, s) [p] = { { .f = s }, }
  35. #define FSETB(p, s) [p] = { { .b = s }, 1 }
  36. #define FSETI(p, s) [p] = { { .i = s }, 1 }
  37. #define FSETV(p, s) [p] = { { .v = s }, 1 }
  38. #define FSETF(p, s) [p] = { { .f = s }, 1 }
  39. #define CSETB(p, s) [p] = (Parameter){ { .b = s }, 1 }
  40. #define CSETI(p, s) [p] = (Parameter){ { .i = s }, 1 }
  41. #define CSETV(p, s) [p] = (Parameter){ { .v = s }, 1 }
  42. #define CSETF(p, s) [p] = (Parameter){ { .f = s }, 1 }
  43. enum { AtomFind, AtomGo, AtomUri, AtomLast };
  44. enum {
  45. OnDoc = WEBKIT_HIT_TEST_RESULT_CONTEXT_DOCUMENT,
  46. OnLink = WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK,
  47. OnImg = WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE,
  48. OnMedia = WEBKIT_HIT_TEST_RESULT_CONTEXT_MEDIA,
  49. OnEdit = WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE,
  50. OnBar = WEBKIT_HIT_TEST_RESULT_CONTEXT_SCROLLBAR,
  51. OnSel = WEBKIT_HIT_TEST_RESULT_CONTEXT_SELECTION,
  52. OnAny = OnDoc | OnLink | OnImg | OnMedia | OnEdit | OnBar | OnSel,
  53. };
  54. typedef enum {
  55. AcceleratedCanvas,
  56. CaretBrowsing,
  57. CookiePolicies,
  58. DiskCache,
  59. DNSPrefetch,
  60. FontSize,
  61. FrameFlattening,
  62. Geolocation,
  63. HideBackground,
  64. Inspector,
  65. JavaScript,
  66. KioskMode,
  67. LoadImages,
  68. MediaManualPlay,
  69. Plugins,
  70. PreferredLanguages,
  71. RunInFullscreen,
  72. ScrollBars,
  73. ShowIndicators,
  74. SiteQuirks,
  75. SpellChecking,
  76. SpellLanguages,
  77. StrictSSL,
  78. Style,
  79. ZoomLevel,
  80. ParameterLast,
  81. } ParamName;
  82. typedef union {
  83. int b;
  84. int i;
  85. float f;
  86. const void *v;
  87. } Arg;
  88. typedef struct {
  89. Arg val;
  90. int force;
  91. } Parameter;
  92. typedef struct Client {
  93. GtkWidget *win;
  94. WebKitWebView *view;
  95. WebKitWebInspector *inspector;
  96. WebKitFindController *finder;
  97. WebKitHitTestResult *mousepos;
  98. GTlsCertificateFlags tlsflags;
  99. Window xid;
  100. int progress, fullscreen;
  101. const char *title, *overtitle, *targeturi;
  102. const char *needle;
  103. struct Client *next;
  104. } Client;
  105. typedef struct {
  106. guint mod;
  107. guint keyval;
  108. void (*func)(Client *c, const Arg *a);
  109. const Arg arg;
  110. } Key;
  111. typedef struct {
  112. unsigned int target;
  113. unsigned int mask;
  114. guint button;
  115. void (*func)(Client *c, const Arg *a, WebKitHitTestResult *h);
  116. const Arg arg;
  117. unsigned int stopevent;
  118. } Button;
  119. typedef struct {
  120. const char *uri;
  121. Parameter config[ParameterLast];
  122. regex_t re;
  123. } UriParameters;
  124. typedef struct {
  125. char *regex;
  126. char *style;
  127. regex_t re;
  128. } SiteStyle;
  129. /* Surf */
  130. static void usage(void);
  131. static void die(const char *errstr, ...);
  132. static void setup(void);
  133. static void sigchld(int unused);
  134. static void sighup(int unused);
  135. static char *buildfile(const char *path);
  136. static char *buildpath(const char *path);
  137. static const char *getuserhomedir(const char *user);
  138. static const char *getcurrentuserhomedir(void);
  139. static Client *newclient(Client *c);
  140. static void loaduri(Client *c, const Arg *a);
  141. static const char *geturi(Client *c);
  142. static void setatom(Client *c, int a, const char *v);
  143. static const char *getatom(Client *c, int a);
  144. static void updatetitle(Client *c);
  145. static void gettogglestats(Client *c);
  146. static void getpagestats(Client *c);
  147. static WebKitCookieAcceptPolicy cookiepolicy_get(void);
  148. static char cookiepolicy_set(const WebKitCookieAcceptPolicy p);
  149. static void seturiparameters(Client *c, const char *uri);
  150. static void setparameter(Client *c, int refresh, ParamName p, const Arg *a);
  151. static const char *getstyle(const char *uri);
  152. static void setstyle(Client *c, const char *stylefile);
  153. static void runscript(Client *c);
  154. static void evalscript(Client *c, const char *jsstr, ...);
  155. static void updatewinid(Client *c);
  156. static void handleplumb(Client *c, const char *uri);
  157. static void newwindow(Client *c, const Arg *a, int noembed);
  158. static void spawn(Client *c, const Arg *a);
  159. static void destroyclient(Client *c);
  160. static void cleanup(void);
  161. /* GTK/WebKit */
  162. static WebKitWebView *newview(Client *c, WebKitWebView *rv);
  163. static void initwebextensions(WebKitWebContext *wc, Client *c);
  164. static GtkWidget *createview(WebKitWebView *v, WebKitNavigationAction *a,
  165. Client *c);
  166. static gboolean buttonreleased(GtkWidget *w, GdkEvent *e, Client *c);
  167. static GdkFilterReturn processx(GdkXEvent *xevent, GdkEvent *event,
  168. gpointer d);
  169. static gboolean winevent(GtkWidget *w, GdkEvent *e, Client *c);
  170. static void showview(WebKitWebView *v, Client *c);
  171. static GtkWidget *createwindow(Client *c);
  172. static void loadchanged(WebKitWebView *v, WebKitLoadEvent e, Client *c);
  173. static void progresschanged(WebKitWebView *v, GParamSpec *ps, Client *c);
  174. static void titlechanged(WebKitWebView *view, GParamSpec *ps, Client *c);
  175. static void mousetargetchanged(WebKitWebView *v, WebKitHitTestResult *h,
  176. guint modifiers, Client *c);
  177. static gboolean permissionrequested(WebKitWebView *v,
  178. WebKitPermissionRequest *r, Client *c);
  179. static gboolean decidepolicy(WebKitWebView *v, WebKitPolicyDecision *d,
  180. WebKitPolicyDecisionType dt, Client *c);
  181. static void decidenavigation(WebKitPolicyDecision *d, Client *c);
  182. static void decidenewwindow(WebKitPolicyDecision *d, Client *c);
  183. static void decideresource(WebKitPolicyDecision *d, Client *c);
  184. static void downloadstarted(WebKitWebContext *wc, WebKitDownload *d,
  185. Client *c);
  186. static void responsereceived(WebKitDownload *d, GParamSpec *ps, Client *c);
  187. static void download(Client *c, WebKitURIResponse *r);
  188. static void closeview(WebKitWebView *v, Client *c);
  189. static void destroywin(GtkWidget* w, Client *c);
  190. /* Hotkeys */
  191. static void pasteuri(GtkClipboard *clipboard, const char *text, gpointer d);
  192. static void reload(Client *c, const Arg *a);
  193. static void print(Client *c, const Arg *a);
  194. static void clipboard(Client *c, const Arg *a);
  195. static void zoom(Client *c, const Arg *a);
  196. static void scroll(Client *c, const Arg *a);
  197. static void navigate(Client *c, const Arg *a);
  198. static void stop(Client *c, const Arg *a);
  199. static void toggle(Client *c, const Arg *a);
  200. static void togglefullscreen(Client *c, const Arg *a);
  201. static void togglecookiepolicy(Client *c, const Arg *a);
  202. static void toggleinspector(Client *c, const Arg *a);
  203. static void find(Client *c, const Arg *a);
  204. /* Buttons */
  205. static void clicknavigate(Client *c, const Arg *a, WebKitHitTestResult *h);
  206. static void clicknewwindow(Client *c, const Arg *a, WebKitHitTestResult *h);
  207. static void clickexternplayer(Client *c, const Arg *a, WebKitHitTestResult *h);
  208. static char winid[64];
  209. static char togglestats[10];
  210. static char pagestats[2];
  211. static Atom atoms[AtomLast];
  212. static Window embed;
  213. static int showxid;
  214. static int cookiepolicy;
  215. static Display *dpy;
  216. static Client *clients;
  217. static GdkDevice *gdkkb;
  218. static char *stylefile;
  219. static const char *useragent;
  220. static Parameter *curconfig;
  221. char *argv0;
  222. /* configuration, allows nested code to access above variables */
  223. #include "config.h"
  224. void
  225. usage(void)
  226. {
  227. die("usage: %s [-bBdDfFgGiIkKmMnNpPsSvx] [-a cookiepolicies ] "
  228. "[-c cookiefile] [-e xid] [-r scriptfile] [-t stylefile] "
  229. "[-u useragent] [-z zoomlevel] [uri]\n", basename(argv0));
  230. }
  231. void
  232. die(const char *errstr, ...)
  233. {
  234. va_list ap;
  235. va_start(ap, errstr);
  236. vfprintf(stderr, errstr, ap);
  237. va_end(ap);
  238. exit(1);
  239. }
  240. void
  241. setup(void)
  242. {
  243. GdkDisplay *gdpy;
  244. int i, j;
  245. /* clean up any zombies immediately */
  246. sigchld(0);
  247. if (signal(SIGHUP, sighup) == SIG_ERR)
  248. die("Can't install SIGHUP handler");
  249. if (!(dpy = XOpenDisplay(NULL)))
  250. die("Can't open default display");
  251. /* atoms */
  252. atoms[AtomFind] = XInternAtom(dpy, "_SURF_FIND", False);
  253. atoms[AtomGo] = XInternAtom(dpy, "_SURF_GO", False);
  254. atoms[AtomUri] = XInternAtom(dpy, "_SURF_URI", False);
  255. gtk_init(NULL, NULL);
  256. gdpy = gdk_display_get_default();
  257. curconfig = defconfig;
  258. /* dirs and files */
  259. cookiefile = buildfile(cookiefile);
  260. scriptfile = buildfile(scriptfile);
  261. cachedir = buildpath(cachedir);
  262. gdkkb = gdk_seat_get_keyboard(gdk_display_get_default_seat(gdpy));
  263. if (!stylefile) {
  264. styledir = buildpath(styledir);
  265. for (i = 0; i < LENGTH(styles); ++i) {
  266. if (regcomp(&(styles[i].re), styles[i].regex,
  267. REG_EXTENDED)) {
  268. fprintf(stderr,
  269. "Could not compile regex: %s\n",
  270. styles[i].regex);
  271. styles[i].regex = NULL;
  272. }
  273. styles[i].style = g_strconcat(styledir, "/",
  274. styles[i].style, NULL);
  275. }
  276. g_free(styledir);
  277. } else {
  278. stylefile = buildfile(stylefile);
  279. }
  280. for (i = 0; i < LENGTH(uriparams); ++i) {
  281. if (!regcomp(&(uriparams[i].re), uriparams[i].uri,
  282. REG_EXTENDED)) {
  283. /* copy default parameters if they are not already set
  284. * or if they are forced */
  285. for (j = 0; j < ParameterLast; ++j) {
  286. if (!uriparams[i].config[j].force ||
  287. defconfig[j].force)
  288. uriparams[i].config[j] = defconfig[j];
  289. }
  290. } else {
  291. fprintf(stderr,
  292. "Could not compile regex: %s\n",
  293. uriparams[i].uri);
  294. uriparams[i].uri = NULL;
  295. }
  296. }
  297. }
  298. void
  299. sigchld(int unused)
  300. {
  301. if (signal(SIGCHLD, sigchld) == SIG_ERR)
  302. die("Can't install SIGCHLD handler");
  303. while (waitpid(-1, NULL, WNOHANG) > 0)
  304. ;
  305. }
  306. void
  307. sighup(int unused)
  308. {
  309. Arg a = { .b = 0 };
  310. Client *c;
  311. for (c = clients; c; c = c->next)
  312. reload(c, &a);
  313. }
  314. char *
  315. buildfile(const char *path)
  316. {
  317. char *dname, *bname, *bpath, *fpath;
  318. FILE *f;
  319. dname = g_path_get_dirname(path);
  320. bname = g_path_get_basename(path);
  321. bpath = buildpath(dname);
  322. g_free(dname);
  323. fpath = g_build_filename(bpath, bname, NULL);
  324. g_free(bpath);
  325. g_free(bname);
  326. if (!(f = fopen(fpath, "a")))
  327. die("Could not open file: %s\n", fpath);
  328. g_chmod(fpath, 0600); /* always */
  329. fclose(f);
  330. return fpath;
  331. }
  332. static const char*
  333. getuserhomedir(const char *user)
  334. {
  335. struct passwd *pw = getpwnam(user);
  336. if (!pw)
  337. die("Can't get user %s login information.\n", user);
  338. return pw->pw_dir;
  339. }
  340. static const char*
  341. getcurrentuserhomedir(void)
  342. {
  343. const char *homedir;
  344. const char *user;
  345. struct passwd *pw;
  346. homedir = getenv("HOME");
  347. if (homedir)
  348. return homedir;
  349. user = getenv("USER");
  350. if (user)
  351. return getuserhomedir(user);
  352. pw = getpwuid(getuid());
  353. if (!pw)
  354. die("Can't get current user home directory\n");
  355. return pw->pw_dir;
  356. }
  357. char *
  358. buildpath(const char *path)
  359. {
  360. char *apath, *name, *p, *fpath;
  361. const char *homedir;
  362. if (path[0] == '~') {
  363. if (path[1] == '/' || path[1] == '\0') {
  364. p = (char *)&path[1];
  365. homedir = getcurrentuserhomedir();
  366. } else {
  367. if ((p = strchr(path, '/')))
  368. name = g_strndup(&path[1], --p - path);
  369. else
  370. name = g_strdup(&path[1]);
  371. homedir = getuserhomedir(name);
  372. g_free(name);
  373. }
  374. apath = g_build_filename(homedir, p, NULL);
  375. } else {
  376. apath = g_strdup(path);
  377. }
  378. /* creating directory */
  379. if (g_mkdir_with_parents(apath, 0700) < 0)
  380. die("Could not access directory: %s\n", apath);
  381. fpath = realpath(apath, NULL);
  382. g_free(apath);
  383. return fpath;
  384. }
  385. Client *
  386. newclient(Client *rc)
  387. {
  388. Client *c;
  389. if (!(c = calloc(1, sizeof(Client))))
  390. die("Cannot malloc!\n");
  391. c->next = clients;
  392. clients = c;
  393. c->progress = 100;
  394. c->tlsflags = G_TLS_CERTIFICATE_VALIDATE_ALL + 1;
  395. c->view = newview(c, rc ? rc->view : NULL);
  396. return c;
  397. }
  398. void
  399. loaduri(Client *c, const Arg *a)
  400. {
  401. struct stat st;
  402. char *url, *path;
  403. const char *uri = a->v;
  404. if (g_strcmp0(uri, "") == 0)
  405. return;
  406. if (g_str_has_prefix(uri, "http://") ||
  407. g_str_has_prefix(uri, "https://") ||
  408. g_str_has_prefix(uri, "file://") ||
  409. g_str_has_prefix(uri, "about:")) {
  410. url = g_strdup(uri);
  411. } else if (!stat(uri, &st) && (path = realpath(uri, NULL))) {
  412. url = g_strdup_printf("file://%s", path);
  413. free(path);
  414. } else {
  415. url = g_strdup_printf("http://%s", uri);
  416. }
  417. setatom(c, AtomUri, url);
  418. if (strcmp(url, geturi(c)) == 0) {
  419. reload(c, a);
  420. } else {
  421. webkit_web_view_load_uri(c->view, url);
  422. updatetitle(c);
  423. }
  424. g_free(url);
  425. }
  426. const char *
  427. geturi(Client *c)
  428. {
  429. const char *uri;
  430. if (!(uri = webkit_web_view_get_uri(c->view)))
  431. uri = "about:blank";
  432. return uri;
  433. }
  434. void
  435. setatom(Client *c, int a, const char *v)
  436. {
  437. XSync(dpy, False);
  438. XChangeProperty(dpy, c->xid,
  439. atoms[a], XA_STRING, 8, PropModeReplace,
  440. (unsigned char *)v, strlen(v) + 1);
  441. }
  442. const char *
  443. getatom(Client *c, int a)
  444. {
  445. static char buf[BUFSIZ];
  446. Atom adummy;
  447. int idummy;
  448. unsigned long ldummy;
  449. unsigned char *p = NULL;
  450. XGetWindowProperty(dpy, c->xid, atoms[a], 0L, BUFSIZ, False, XA_STRING,
  451. &adummy, &idummy, &ldummy, &ldummy, &p);
  452. if (p)
  453. strncpy(buf, (char *)p, LENGTH(buf) - 1);
  454. else
  455. buf[0] = '\0';
  456. XFree(p);
  457. return buf;
  458. }
  459. void
  460. updatetitle(Client *c)
  461. {
  462. char *title;
  463. const char *name = c->overtitle ? c->overtitle :
  464. c->title ? c->title : "";
  465. if (curconfig[ShowIndicators].val.b) {
  466. gettogglestats(c);
  467. getpagestats(c);
  468. if (c->progress != 100)
  469. title = g_strdup_printf("[%i%%] %s:%s | %s",
  470. c->progress, togglestats, pagestats, name);
  471. else
  472. title = g_strdup_printf("%s:%s | %s",
  473. togglestats, pagestats, name);
  474. gtk_window_set_title(GTK_WINDOW(c->win), title);
  475. g_free(title);
  476. } else {
  477. gtk_window_set_title(GTK_WINDOW(c->win), name);
  478. }
  479. }
  480. void
  481. gettogglestats(Client *c)
  482. {
  483. togglestats[0] = cookiepolicy_set(cookiepolicy_get());
  484. togglestats[1] = curconfig[CaretBrowsing].val.b ? 'C' : 'c';
  485. togglestats[2] = curconfig[Geolocation].val.b ? 'G' : 'g';
  486. togglestats[3] = curconfig[DiskCache].val.b ? 'D' : 'd';
  487. togglestats[4] = curconfig[LoadImages].val.b ? 'I' : 'i';
  488. togglestats[5] = curconfig[JavaScript].val.b ? 'S' : 's';
  489. togglestats[6] = curconfig[Plugins].val.b ? 'V' : 'v';
  490. togglestats[7] = curconfig[Style].val.b ? 'M' : 'm';
  491. togglestats[8] = curconfig[FrameFlattening].val.b ? 'F' : 'f';
  492. togglestats[9] = '\0';
  493. }
  494. void
  495. getpagestats(Client *c)
  496. {
  497. pagestats[0] = c->tlsflags > G_TLS_CERTIFICATE_VALIDATE_ALL ? '-' :
  498. c->tlsflags > 0 ? 'U' : 'T';
  499. pagestats[1] = '\0';
  500. }
  501. WebKitCookieAcceptPolicy
  502. cookiepolicy_get(void)
  503. {
  504. switch (((char *)curconfig[CookiePolicies].val.v)[cookiepolicy]) {
  505. case 'a':
  506. return WEBKIT_COOKIE_POLICY_ACCEPT_NEVER;
  507. case '@':
  508. return WEBKIT_COOKIE_POLICY_ACCEPT_NO_THIRD_PARTY;
  509. default: /* fallthrough */
  510. case 'A':
  511. return WEBKIT_COOKIE_POLICY_ACCEPT_ALWAYS;
  512. }
  513. }
  514. char
  515. cookiepolicy_set(const WebKitCookieAcceptPolicy p)
  516. {
  517. switch (p) {
  518. case WEBKIT_COOKIE_POLICY_ACCEPT_NEVER:
  519. return 'a';
  520. case WEBKIT_COOKIE_POLICY_ACCEPT_NO_THIRD_PARTY:
  521. return '@';
  522. default: /* fallthrough */
  523. case WEBKIT_COOKIE_POLICY_ACCEPT_ALWAYS:
  524. return 'A';
  525. }
  526. }
  527. void
  528. seturiparameters(Client *c, const char *uri)
  529. {
  530. int i;
  531. for (i = 0; i < LENGTH(uriparams); ++i) {
  532. if (uriparams[i].uri &&
  533. !regexec(&(uriparams[i].re), uri, 0, NULL, 0)) {
  534. curconfig = uriparams[i].config;
  535. break;
  536. }
  537. }
  538. for (i = 0; i < ParameterLast; ++i)
  539. setparameter(c, 0, i, &curconfig[i].val);
  540. }
  541. void
  542. setparameter(Client *c, int refresh, ParamName p, const Arg *a)
  543. {
  544. GdkRGBA bgcolor = { 0 };
  545. WebKitSettings *s = webkit_web_view_get_settings(c->view);
  546. switch (p) {
  547. case AcceleratedCanvas:
  548. webkit_settings_set_enable_accelerated_2d_canvas(s, a->b);
  549. break;
  550. case CaretBrowsing:
  551. webkit_settings_set_enable_caret_browsing(s, a->b);
  552. refresh = 0;
  553. break;
  554. case CookiePolicies:
  555. webkit_cookie_manager_set_accept_policy(
  556. webkit_web_context_get_cookie_manager(
  557. webkit_web_view_get_context(c->view)),
  558. cookiepolicy_get());
  559. refresh = 0;
  560. break;
  561. case DiskCache:
  562. webkit_web_context_set_cache_model(
  563. webkit_web_view_get_context(c->view), a->b ?
  564. WEBKIT_CACHE_MODEL_WEB_BROWSER :
  565. WEBKIT_CACHE_MODEL_DOCUMENT_VIEWER);
  566. return; /* do not update */
  567. case DNSPrefetch:
  568. webkit_settings_set_enable_dns_prefetching(s, a->b);
  569. return; /* do not update */
  570. case FontSize:
  571. webkit_settings_set_default_font_size(s, a->i);
  572. return; /* do not update */
  573. case FrameFlattening:
  574. webkit_settings_set_enable_frame_flattening(s, a->b);
  575. break;
  576. case Geolocation:
  577. refresh = 0;
  578. break;
  579. case HideBackground:
  580. if (a->b)
  581. webkit_web_view_set_background_color(c->view, &bgcolor);
  582. return; /* do not update */
  583. case Inspector:
  584. webkit_settings_set_enable_developer_extras(s, a->b);
  585. return; /* do not update */
  586. case JavaScript:
  587. webkit_settings_set_enable_javascript(s, a->b);
  588. break;
  589. case KioskMode:
  590. return; /* do nothing */
  591. case LoadImages:
  592. webkit_settings_set_auto_load_images(s, a->b);
  593. break;
  594. case MediaManualPlay:
  595. webkit_settings_set_media_playback_requires_user_gesture(s, a->b);
  596. break;
  597. case Plugins:
  598. webkit_settings_set_enable_plugins(s, a->b);
  599. break;
  600. case PreferredLanguages:
  601. return; /* do nothing */
  602. case RunInFullscreen:
  603. return; /* do nothing */
  604. case ScrollBars:
  605. /* Disabled until we write some WebKitWebExtension for
  606. * manipulating the DOM directly.
  607. enablescrollbars = !enablescrollbars;
  608. evalscript(c, "document.documentElement.style.overflow = '%s'",
  609. enablescrollbars ? "auto" : "hidden");
  610. */
  611. return; /* do not update */
  612. case ShowIndicators:
  613. break;
  614. case SiteQuirks:
  615. webkit_settings_set_enable_site_specific_quirks(s, a->b);
  616. break;
  617. case SpellChecking:
  618. webkit_web_context_set_spell_checking_enabled(
  619. webkit_web_view_get_context(c->view), a->b);
  620. return; /* do not update */
  621. case SpellLanguages:
  622. return; /* do nothing */
  623. case StrictSSL:
  624. webkit_web_context_set_tls_errors_policy(
  625. webkit_web_view_get_context(c->view), a->b ?
  626. WEBKIT_TLS_ERRORS_POLICY_FAIL :
  627. WEBKIT_TLS_ERRORS_POLICY_IGNORE);
  628. return; /* do not update */
  629. case Style:
  630. if (a->b)
  631. setstyle(c, getstyle(geturi(c)));
  632. else
  633. webkit_user_content_manager_remove_all_style_sheets(
  634. webkit_web_view_get_user_content_manager(c->view));
  635. refresh = 0;
  636. break;
  637. case ZoomLevel:
  638. webkit_web_view_set_zoom_level(c->view, a->f);
  639. return; /* do not update */
  640. default:
  641. return; /* do nothing */
  642. }
  643. updatetitle(c);
  644. if (refresh)
  645. reload(c, a);
  646. }
  647. const char *
  648. getstyle(const char *uri)
  649. {
  650. int i;
  651. if (stylefile)
  652. return stylefile;
  653. for (i = 0; i < LENGTH(styles); ++i) {
  654. if (styles[i].regex &&
  655. !regexec(&(styles[i].re), uri, 0, NULL, 0))
  656. return styles[i].style;
  657. }
  658. return "";
  659. }
  660. void
  661. setstyle(Client *c, const char *stylefile)
  662. {
  663. gchar *style;
  664. if (!g_file_get_contents(stylefile, &style, NULL, NULL)) {
  665. fprintf(stderr, "Could not read style file: %s\n", stylefile);
  666. return;
  667. }
  668. webkit_user_content_manager_add_style_sheet(
  669. webkit_web_view_get_user_content_manager(c->view),
  670. webkit_user_style_sheet_new(style,
  671. WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES,
  672. WEBKIT_USER_STYLE_LEVEL_USER,
  673. NULL, NULL));
  674. g_free(style);
  675. }
  676. void
  677. runscript(Client *c)
  678. {
  679. gchar *script;
  680. gsize l;
  681. if (g_file_get_contents(scriptfile, &script, &l, NULL) && l)
  682. evalscript(c, script);
  683. g_free(script);
  684. }
  685. void
  686. evalscript(Client *c, const char *jsstr, ...)
  687. {
  688. va_list ap;
  689. gchar *script;
  690. va_start(ap, jsstr);
  691. script = g_strdup_vprintf(jsstr, ap);
  692. va_end(ap);
  693. webkit_web_view_run_javascript(c->view, script, NULL, NULL, NULL);
  694. g_free(script);
  695. }
  696. void
  697. updatewinid(Client *c)
  698. {
  699. snprintf(winid, LENGTH(winid), "%lu", c->xid);
  700. }
  701. void
  702. handleplumb(Client *c, const char *uri)
  703. {
  704. Arg a = (Arg)PLUMB(uri);
  705. spawn(c, &a);
  706. }
  707. void
  708. newwindow(Client *c, const Arg *a, int noembed)
  709. {
  710. int i = 0;
  711. char tmp[64];
  712. const char *cmd[26], *uri;
  713. const Arg arg = { .v = cmd };
  714. cmd[i++] = argv0;
  715. cmd[i++] = "-a";
  716. cmd[i++] = curconfig[CookiePolicies].val.v;
  717. cmd[i++] = curconfig[ScrollBars].val.b ? "-B" : "-b";
  718. if (cookiefile && g_strcmp0(cookiefile, "")) {
  719. cmd[i++] = "-c";
  720. cmd[i++] = cookiefile;
  721. }
  722. cmd[i++] = curconfig[DiskCache].val.b ? "-D" : "-d";
  723. if (embed && !noembed) {
  724. cmd[i++] = "-e";
  725. snprintf(tmp, LENGTH(tmp), "%lu", embed);
  726. cmd[i++] = tmp;
  727. }
  728. cmd[i++] = curconfig[RunInFullscreen].val.b ? "-F" : "-f" ;
  729. cmd[i++] = curconfig[Geolocation].val.b ? "-G" : "-g" ;
  730. cmd[i++] = curconfig[LoadImages].val.b ? "-I" : "-i" ;
  731. cmd[i++] = curconfig[KioskMode].val.b ? "-K" : "-k" ;
  732. cmd[i++] = curconfig[Style].val.b ? "-M" : "-m" ;
  733. cmd[i++] = curconfig[Inspector].val.b ? "-N" : "-n" ;
  734. cmd[i++] = curconfig[Plugins].val.b ? "-P" : "-p" ;
  735. if (scriptfile && g_strcmp0(scriptfile, "")) {
  736. cmd[i++] = "-r";
  737. cmd[i++] = scriptfile;
  738. }
  739. cmd[i++] = curconfig[JavaScript].val.b ? "-S" : "-s";
  740. if (stylefile && g_strcmp0(stylefile, "")) {
  741. cmd[i++] = "-t";
  742. cmd[i++] = stylefile;
  743. }
  744. if (fulluseragent && g_strcmp0(fulluseragent, "")) {
  745. cmd[i++] = "-u";
  746. cmd[i++] = fulluseragent;
  747. }
  748. if (showxid)
  749. cmd[i++] = "-x";
  750. /* do not keep zoom level */
  751. cmd[i++] = "--";
  752. if ((uri = a->v))
  753. cmd[i++] = uri;
  754. cmd[i] = NULL;
  755. spawn(c, &arg);
  756. }
  757. void
  758. spawn(Client *c, const Arg *a)
  759. {
  760. if (fork() == 0) {
  761. if (dpy)
  762. close(ConnectionNumber(dpy));
  763. setsid();
  764. execvp(((char **)a->v)[0], (char **)a->v);
  765. fprintf(stderr, "%s: execvp %s", argv0, ((char **)a->v)[0]);
  766. perror(" failed");
  767. exit(1);
  768. }
  769. }
  770. void
  771. destroyclient(Client *c)
  772. {
  773. Client *p;
  774. webkit_web_view_stop_loading(c->view);
  775. /* Not needed, has already been called
  776. gtk_widget_destroy(c->win);
  777. */
  778. for (p = clients; p && p->next != c; p = p->next)
  779. ;
  780. if (p)
  781. p->next = c->next;
  782. else
  783. clients = c->next;
  784. free(c);
  785. }
  786. void
  787. cleanup(void)
  788. {
  789. while (clients)
  790. destroyclient(clients);
  791. g_free(cookiefile);
  792. g_free(scriptfile);
  793. g_free(stylefile);
  794. g_free(cachedir);
  795. XCloseDisplay(dpy);
  796. }
  797. WebKitWebView *
  798. newview(Client *c, WebKitWebView *rv)
  799. {
  800. WebKitWebView *v;
  801. WebKitSettings *settings;
  802. WebKitUserContentManager *contentmanager;
  803. WebKitWebContext *context;
  804. /* Webview */
  805. if (rv) {
  806. v = WEBKIT_WEB_VIEW(
  807. webkit_web_view_new_with_related_view(rv));
  808. } else {
  809. settings = webkit_settings_new_with_settings(
  810. "auto-load-images", curconfig[LoadImages].val.b,
  811. "default-font-size", curconfig[FontSize].val.i,
  812. "enable-caret-browsing", curconfig[CaretBrowsing].val.b,
  813. "enable-developer-extras", curconfig[Inspector].val.b,
  814. "enable-dns-prefetching", curconfig[DNSPrefetch].val.b,
  815. "enable-frame-flattening", curconfig[FrameFlattening].val.b,
  816. "enable-html5-database", curconfig[DiskCache].val.b,
  817. "enable-html5-local-storage", curconfig[DiskCache].val.b,
  818. "enable-javascript", curconfig[JavaScript].val.b,
  819. "enable-plugins", curconfig[Plugins].val.b,
  820. "enable-accelerated-2d-canvas", curconfig[AcceleratedCanvas].val.b,
  821. "enable-site-specific-quirks", curconfig[SiteQuirks].val.b,
  822. "media-playback-requires-user-gesture", curconfig[MediaManualPlay].val.b,
  823. NULL);
  824. /* For mor interesting settings, have a look at
  825. * http://webkitgtk.org/reference/webkit2gtk/stable/WebKitSettings.html */
  826. if (strcmp(fulluseragent, "")) {
  827. webkit_settings_set_user_agent(settings, fulluseragent);
  828. } else if (surfuseragent) {
  829. webkit_settings_set_user_agent_with_application_details(
  830. settings, "Surf", VERSION);
  831. }
  832. useragent = webkit_settings_get_user_agent(settings);
  833. contentmanager = webkit_user_content_manager_new();
  834. context = webkit_web_context_new_with_website_data_manager(
  835. webkit_website_data_manager_new(
  836. "base-cache-directory", cachedir,
  837. "base-data-directory", cachedir,
  838. NULL));
  839. /* rendering process model, can be a shared unique one
  840. * or one for each view */
  841. webkit_web_context_set_process_model(context,
  842. WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES);
  843. /* ssl */
  844. webkit_web_context_set_tls_errors_policy(context,
  845. curconfig[StrictSSL].val.b ? WEBKIT_TLS_ERRORS_POLICY_FAIL :
  846. WEBKIT_TLS_ERRORS_POLICY_IGNORE);
  847. /* disk cache */
  848. webkit_web_context_set_cache_model(context,
  849. curconfig[DiskCache].val.b ? WEBKIT_CACHE_MODEL_WEB_BROWSER :
  850. WEBKIT_CACHE_MODEL_DOCUMENT_VIEWER);
  851. /* Currently only works with text file to be compatible with curl */
  852. webkit_cookie_manager_set_persistent_storage(
  853. webkit_web_context_get_cookie_manager(context), cookiefile,
  854. WEBKIT_COOKIE_PERSISTENT_STORAGE_TEXT);
  855. /* cookie policy */
  856. webkit_cookie_manager_set_accept_policy(
  857. webkit_web_context_get_cookie_manager(context),
  858. cookiepolicy_get());
  859. /* languages */
  860. webkit_web_context_set_preferred_languages(context,
  861. curconfig[PreferredLanguages].val.v);
  862. webkit_web_context_set_spell_checking_languages(context,
  863. curconfig[SpellLanguages].val.v);
  864. webkit_web_context_set_spell_checking_enabled(context,
  865. curconfig[SpellChecking].val.b);
  866. g_signal_connect(G_OBJECT(context), "download-started",
  867. G_CALLBACK(downloadstarted), c);
  868. g_signal_connect(G_OBJECT(context), "initialize-web-extensions",
  869. G_CALLBACK(initwebextensions), c);
  870. v = g_object_new(WEBKIT_TYPE_WEB_VIEW,
  871. "settings", settings,
  872. "user-content-manager", contentmanager,
  873. "web-context", context,
  874. NULL);
  875. }
  876. g_signal_connect(G_OBJECT(v), "notify::estimated-load-progress",
  877. G_CALLBACK(progresschanged), c);
  878. g_signal_connect(G_OBJECT(v), "notify::title",
  879. G_CALLBACK(titlechanged), c);
  880. g_signal_connect(G_OBJECT(v), "button-release-event",
  881. G_CALLBACK(buttonreleased), c);
  882. g_signal_connect(G_OBJECT(v), "close",
  883. G_CALLBACK(closeview), c);
  884. g_signal_connect(G_OBJECT(v), "create",
  885. G_CALLBACK(createview), c);
  886. g_signal_connect(G_OBJECT(v), "decide-policy",
  887. G_CALLBACK(decidepolicy), c);
  888. g_signal_connect(G_OBJECT(v), "load-changed",
  889. G_CALLBACK(loadchanged), c);
  890. g_signal_connect(G_OBJECT(v), "mouse-target-changed",
  891. G_CALLBACK(mousetargetchanged), c);
  892. g_signal_connect(G_OBJECT(v), "permission-request",
  893. G_CALLBACK(permissionrequested), c);
  894. g_signal_connect(G_OBJECT(v), "ready-to-show",
  895. G_CALLBACK(showview), c);
  896. return v;
  897. }
  898. void
  899. initwebextensions(WebKitWebContext *wc, Client *c)
  900. {
  901. webkit_web_context_set_web_extensions_directory(wc, WEBEXTDIR);
  902. }
  903. GtkWidget *
  904. createview(WebKitWebView *v, WebKitNavigationAction *a, Client *c)
  905. {
  906. Client *n;
  907. switch (webkit_navigation_action_get_navigation_type(a)) {
  908. case WEBKIT_NAVIGATION_TYPE_OTHER: /* fallthrough */
  909. /*
  910. * popup windows of type “other” are almost always triggered
  911. * by user gesture, so inverse the logic here
  912. */
  913. /* instead of this, compare destination uri to mouse-over uri for validating window */
  914. if (webkit_navigation_action_is_user_gesture(a))
  915. return NULL;
  916. case WEBKIT_NAVIGATION_TYPE_LINK_CLICKED: /* fallthrough */
  917. case WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED: /* fallthrough */
  918. case WEBKIT_NAVIGATION_TYPE_BACK_FORWARD: /* fallthrough */
  919. case WEBKIT_NAVIGATION_TYPE_RELOAD: /* fallthrough */
  920. case WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED:
  921. n = newclient(c);
  922. break;
  923. default:
  924. return NULL;
  925. }
  926. return GTK_WIDGET(n->view);
  927. }
  928. gboolean
  929. buttonreleased(GtkWidget *w, GdkEvent *e, Client *c)
  930. {
  931. WebKitHitTestResultContext element;
  932. int i;
  933. element = webkit_hit_test_result_get_context(c->mousepos);
  934. for (i = 0; i < LENGTH(buttons); ++i) {
  935. if (element & buttons[i].target &&
  936. e->button.button == buttons[i].button &&
  937. CLEANMASK(e->button.state) == CLEANMASK(buttons[i].mask) &&
  938. buttons[i].func) {
  939. buttons[i].func(c, &buttons[i].arg, c->mousepos);
  940. return buttons[i].stopevent;
  941. }
  942. }
  943. return FALSE;
  944. }
  945. GdkFilterReturn
  946. processx(GdkXEvent *e, GdkEvent *event, gpointer d)
  947. {
  948. Client *c = (Client *)d;
  949. XPropertyEvent *ev;
  950. Arg a;
  951. if (((XEvent *)e)->type == PropertyNotify) {
  952. ev = &((XEvent *)e)->xproperty;
  953. if (ev->state == PropertyNewValue) {
  954. if (ev->atom == atoms[AtomFind]) {
  955. find(c, NULL);
  956. return GDK_FILTER_REMOVE;
  957. } else if (ev->atom == atoms[AtomGo]) {
  958. a.v = getatom(c, AtomGo);
  959. loaduri(c, &a);
  960. return GDK_FILTER_REMOVE;
  961. }
  962. }
  963. }
  964. return GDK_FILTER_CONTINUE;
  965. }
  966. gboolean
  967. winevent(GtkWidget *w, GdkEvent *e, Client *c)
  968. {
  969. int i;
  970. switch (e->type) {
  971. case GDK_ENTER_NOTIFY:
  972. c->overtitle = c->targeturi;
  973. updatetitle(c);
  974. break;
  975. case GDK_KEY_PRESS:
  976. if (!curconfig[KioskMode].val.b) {
  977. for (i = 0; i < LENGTH(keys); ++i) {
  978. if (gdk_keyval_to_lower(e->key.keyval) ==
  979. keys[i].keyval &&
  980. CLEANMASK(e->key.state) == keys[i].mod &&
  981. keys[i].func) {
  982. updatewinid(c);
  983. keys[i].func(c, &(keys[i].arg));
  984. return TRUE;
  985. }
  986. }
  987. }
  988. case GDK_LEAVE_NOTIFY:
  989. c->overtitle = NULL;
  990. updatetitle(c);
  991. break;
  992. case GDK_WINDOW_STATE:
  993. if (e->window_state.changed_mask ==
  994. GDK_WINDOW_STATE_FULLSCREEN)
  995. c->fullscreen = e->window_state.new_window_state &
  996. GDK_WINDOW_STATE_FULLSCREEN;
  997. break;
  998. default:
  999. break;
  1000. }
  1001. return FALSE;
  1002. }
  1003. void
  1004. showview(WebKitWebView *v, Client *c)
  1005. {
  1006. GdkRGBA bgcolor = { 0 };
  1007. GdkWindow *gwin;
  1008. c->finder = webkit_web_view_get_find_controller(c->view);
  1009. c->inspector = webkit_web_view_get_inspector(c->view);
  1010. c->win = createwindow(c);
  1011. gtk_container_add(GTK_CONTAINER(c->win), GTK_WIDGET(c->view));
  1012. gtk_widget_show_all(c->win);
  1013. gtk_widget_grab_focus(GTK_WIDGET(c->view));
  1014. gwin = gtk_widget_get_window(GTK_WIDGET(c->win));
  1015. c->xid = gdk_x11_window_get_xid(gwin);
  1016. updatewinid(c);
  1017. if (showxid) {
  1018. gdk_display_sync(gtk_widget_get_display(c->win));
  1019. puts(winid);
  1020. }
  1021. if (curconfig[HideBackground].val.b)
  1022. webkit_web_view_set_background_color(c->view, &bgcolor);
  1023. if (!curconfig[KioskMode].val.b) {
  1024. gdk_window_set_events(gwin, GDK_ALL_EVENTS_MASK);
  1025. gdk_window_add_filter(gwin, processx, c);
  1026. }
  1027. if (curconfig[RunInFullscreen].val.b)
  1028. togglefullscreen(c, NULL);
  1029. if (curconfig[ZoomLevel].val.f != 1.0)
  1030. webkit_web_view_set_zoom_level(c->view,
  1031. curconfig[ZoomLevel].val.f);
  1032. setatom(c, AtomFind, "");
  1033. setatom(c, AtomUri, "about:blank");
  1034. }
  1035. GtkWidget *
  1036. createwindow(Client *c)
  1037. {
  1038. char *wmstr;
  1039. GtkWidget *w;
  1040. if (embed) {
  1041. w = gtk_plug_new(embed);
  1042. } else {
  1043. w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  1044. wmstr = g_path_get_basename(argv0);
  1045. gtk_window_set_wmclass(GTK_WINDOW(w), wmstr, "Surf");
  1046. g_free(wmstr);
  1047. wmstr = g_strdup_printf("%s[%lu]", "Surf",
  1048. webkit_web_view_get_page_id(c->view));
  1049. gtk_window_set_role(GTK_WINDOW(w), wmstr);
  1050. g_free(wmstr);
  1051. gtk_window_set_default_size(GTK_WINDOW(w), 800, 600);
  1052. }
  1053. g_signal_connect(G_OBJECT(w), "destroy",
  1054. G_CALLBACK(destroywin), c);
  1055. g_signal_connect(G_OBJECT(w), "enter-notify-event",
  1056. G_CALLBACK(winevent), c);
  1057. g_signal_connect(G_OBJECT(w), "key-press-event",
  1058. G_CALLBACK(winevent), c);
  1059. g_signal_connect(G_OBJECT(w), "leave-notify-event",
  1060. G_CALLBACK(winevent), c);
  1061. g_signal_connect(G_OBJECT(w), "window-state-event",
  1062. G_CALLBACK(winevent), c);
  1063. return w;
  1064. }
  1065. void
  1066. loadchanged(WebKitWebView *v, WebKitLoadEvent e, Client *c)
  1067. {
  1068. const char *title = geturi(c);
  1069. switch (e) {
  1070. case WEBKIT_LOAD_STARTED:
  1071. curconfig = defconfig;
  1072. setatom(c, AtomUri, title);
  1073. c->title = title;
  1074. c->tlsflags = G_TLS_CERTIFICATE_VALIDATE_ALL + 1;
  1075. seturiparameters(c, geturi(c));
  1076. break;
  1077. case WEBKIT_LOAD_REDIRECTED:
  1078. setatom(c, AtomUri, title);
  1079. c->title = title;
  1080. seturiparameters(c, geturi(c));
  1081. break;
  1082. case WEBKIT_LOAD_COMMITTED:
  1083. if (!webkit_web_view_get_tls_info(c->view, NULL,
  1084. &(c->tlsflags)))
  1085. c->tlsflags = G_TLS_CERTIFICATE_VALIDATE_ALL + 1;
  1086. break;
  1087. case WEBKIT_LOAD_FINISHED:
  1088. /* Disabled until we write some WebKitWebExtension for
  1089. * manipulating the DOM directly.
  1090. evalscript(c, "document.documentElement.style.overflow = '%s'",
  1091. enablescrollbars ? "auto" : "hidden");
  1092. */
  1093. runscript(c);
  1094. break;
  1095. }
  1096. updatetitle(c);
  1097. }
  1098. void
  1099. progresschanged(WebKitWebView *v, GParamSpec *ps, Client *c)
  1100. {
  1101. c->progress = webkit_web_view_get_estimated_load_progress(c->view) *
  1102. 100;
  1103. updatetitle(c);
  1104. }
  1105. void
  1106. titlechanged(WebKitWebView *view, GParamSpec *ps, Client *c)
  1107. {
  1108. c->title = webkit_web_view_get_title(c->view);
  1109. updatetitle(c);
  1110. }
  1111. void
  1112. mousetargetchanged(WebKitWebView *v, WebKitHitTestResult *h, guint modifiers,
  1113. Client *c)
  1114. {
  1115. WebKitHitTestResultContext hc = webkit_hit_test_result_get_context(h);
  1116. /* Keep the hit test to know where is the pointer on the next click */
  1117. c->mousepos = h;
  1118. if (hc & OnLink)
  1119. c->targeturi = webkit_hit_test_result_get_link_uri(h);
  1120. else if (hc & OnImg)
  1121. c->targeturi = webkit_hit_test_result_get_image_uri(h);
  1122. else if (hc & OnMedia)
  1123. c->targeturi = webkit_hit_test_result_get_media_uri(h);
  1124. else
  1125. c->targeturi = NULL;
  1126. c->overtitle = c->targeturi;
  1127. updatetitle(c);
  1128. }
  1129. gboolean
  1130. permissionrequested(WebKitWebView *v, WebKitPermissionRequest *r, Client *c)
  1131. {
  1132. if (WEBKIT_IS_GEOLOCATION_PERMISSION_REQUEST(r)) {
  1133. if (curconfig[Geolocation].val.b)
  1134. webkit_permission_request_allow(r);
  1135. else
  1136. webkit_permission_request_deny(r);
  1137. return TRUE;
  1138. }
  1139. return FALSE;
  1140. }
  1141. gboolean
  1142. decidepolicy(WebKitWebView *v, WebKitPolicyDecision *d,
  1143. WebKitPolicyDecisionType dt, Client *c)
  1144. {
  1145. switch (dt) {
  1146. case WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION:
  1147. decidenavigation(d, c);
  1148. break;
  1149. case WEBKIT_POLICY_DECISION_TYPE_NEW_WINDOW_ACTION:
  1150. decidenewwindow(d, c);
  1151. break;
  1152. case WEBKIT_POLICY_DECISION_TYPE_RESPONSE:
  1153. decideresource(d, c);
  1154. break;
  1155. default:
  1156. webkit_policy_decision_ignore(d);
  1157. break;
  1158. }
  1159. return TRUE;
  1160. }
  1161. void
  1162. decidenavigation(WebKitPolicyDecision *d, Client *c)
  1163. {
  1164. WebKitNavigationAction *a =
  1165. webkit_navigation_policy_decision_get_navigation_action(
  1166. WEBKIT_NAVIGATION_POLICY_DECISION(d));
  1167. switch (webkit_navigation_action_get_navigation_type(a)) {
  1168. case WEBKIT_NAVIGATION_TYPE_LINK_CLICKED: /* fallthrough */
  1169. case WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED: /* fallthrough */
  1170. case WEBKIT_NAVIGATION_TYPE_BACK_FORWARD: /* fallthrough */
  1171. case WEBKIT_NAVIGATION_TYPE_RELOAD: /* fallthrough */
  1172. case WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED: /* fallthrough */
  1173. case WEBKIT_NAVIGATION_TYPE_OTHER: /* fallthrough */
  1174. default:
  1175. /* Do not navigate to links with a "_blank" target (popup) */
  1176. if (webkit_navigation_policy_decision_get_frame_name(
  1177. WEBKIT_NAVIGATION_POLICY_DECISION(d))) {
  1178. webkit_policy_decision_ignore(d);
  1179. } else {
  1180. /* Filter out navigation to different domain ? */
  1181. /* get action→urirequest, copy and load in new window+view
  1182. * on Ctrl+Click ? */
  1183. webkit_policy_decision_use(d);
  1184. }
  1185. break;
  1186. }
  1187. }
  1188. void
  1189. decidenewwindow(WebKitPolicyDecision *d, Client *c)
  1190. {
  1191. Arg arg;
  1192. WebKitNavigationAction *a =
  1193. webkit_navigation_policy_decision_get_navigation_action(
  1194. WEBKIT_NAVIGATION_POLICY_DECISION(d));
  1195. switch (webkit_navigation_action_get_navigation_type(a)) {
  1196. case WEBKIT_NAVIGATION_TYPE_LINK_CLICKED: /* fallthrough */
  1197. case WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED: /* fallthrough */
  1198. case WEBKIT_NAVIGATION_TYPE_BACK_FORWARD: /* fallthrough */
  1199. case WEBKIT_NAVIGATION_TYPE_RELOAD: /* fallthrough */
  1200. case WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED:
  1201. /* Filter domains here */
  1202. /* If the value of “mouse-button” is not 0, then the navigation was triggered by a mouse event.
  1203. * test for link clicked but no button ? */
  1204. arg.v = webkit_uri_request_get_uri(
  1205. webkit_navigation_action_get_request(a));
  1206. newwindow(c, &arg, 0);
  1207. break;
  1208. case WEBKIT_NAVIGATION_TYPE_OTHER: /* fallthrough */
  1209. default:
  1210. break;
  1211. }
  1212. webkit_policy_decision_ignore(d);
  1213. }
  1214. void
  1215. decideresource(WebKitPolicyDecision *d, Client *c)
  1216. {
  1217. int i, isascii = 1;
  1218. WebKitResponsePolicyDecision *r = WEBKIT_RESPONSE_POLICY_DECISION(d);
  1219. WebKitURIResponse *res =
  1220. webkit_response_policy_decision_get_response(r);
  1221. const gchar *uri = webkit_uri_response_get_uri(res);
  1222. if (g_str_has_suffix(uri, "/favicon.ico")) {
  1223. webkit_policy_decision_ignore(d);
  1224. return;
  1225. }
  1226. if (!g_str_has_prefix(uri, "http://")
  1227. && !g_str_has_prefix(uri, "https://")
  1228. && !g_str_has_prefix(uri, "about:")
  1229. && !g_str_has_prefix(uri, "file://")
  1230. && !g_str_has_prefix(uri, "data:")
  1231. && !g_str_has_prefix(uri, "blob:")
  1232. && strlen(uri) > 0) {
  1233. for (i = 0; i < strlen(uri); i++) {
  1234. if (!g_ascii_isprint(uri[i])) {
  1235. isascii = 0;
  1236. break;
  1237. }
  1238. }
  1239. if (isascii) {
  1240. handleplumb(c, uri);
  1241. webkit_policy_decision_ignore(d);
  1242. return;
  1243. }
  1244. }
  1245. if (webkit_response_policy_decision_is_mime_type_supported(r)) {
  1246. webkit_policy_decision_use(d);
  1247. } else {
  1248. webkit_policy_decision_ignore(d);
  1249. download(c, res);
  1250. }
  1251. }
  1252. void
  1253. downloadstarted(WebKitWebContext *wc, WebKitDownload *d, Client *c)
  1254. {
  1255. g_signal_connect(G_OBJECT(d), "notify::response",
  1256. G_CALLBACK(responsereceived), c);
  1257. }
  1258. void
  1259. responsereceived(WebKitDownload *d, GParamSpec *ps, Client *c)
  1260. {
  1261. download(c, webkit_download_get_response(d));
  1262. webkit_download_cancel(d);
  1263. }
  1264. void
  1265. download(Client *c, WebKitURIResponse *r)
  1266. {
  1267. Arg a = (Arg)DOWNLOAD(webkit_uri_response_get_uri(r), geturi(c));
  1268. spawn(c, &a);
  1269. }
  1270. void
  1271. closeview(WebKitWebView *v, Client *c)
  1272. {
  1273. gtk_widget_destroy(c->win);
  1274. }
  1275. void
  1276. destroywin(GtkWidget* w, Client *c)
  1277. {
  1278. destroyclient(c);
  1279. if (!clients)
  1280. gtk_main_quit();
  1281. }
  1282. void
  1283. pasteuri(GtkClipboard *clipboard, const char *text, gpointer d)
  1284. {
  1285. Arg a = {.v = text };
  1286. if (text)
  1287. loaduri((Client *) d, &a);
  1288. }
  1289. void
  1290. reload(Client *c, const Arg *a)
  1291. {
  1292. if (a->b)
  1293. webkit_web_view_reload_bypass_cache(c->view);
  1294. else
  1295. webkit_web_view_reload(c->view);
  1296. }
  1297. void
  1298. print(Client *c, const Arg *a)
  1299. {
  1300. webkit_print_operation_run_dialog(webkit_print_operation_new(c->view),
  1301. GTK_WINDOW(c->win));
  1302. }
  1303. void
  1304. clipboard(Client *c, const Arg *a)
  1305. {
  1306. if (a->b) { /* load clipboard uri */
  1307. gtk_clipboard_request_text(gtk_clipboard_get(
  1308. GDK_SELECTION_PRIMARY),
  1309. pasteuri, c);
  1310. } else { /* copy uri */
  1311. gtk_clipboard_set_text(gtk_clipboard_get(
  1312. GDK_SELECTION_PRIMARY), c->targeturi
  1313. ? c->targeturi : geturi(c), -1);
  1314. }
  1315. }
  1316. void
  1317. zoom(Client *c, const Arg *a)
  1318. {
  1319. if (a->i > 0)
  1320. webkit_web_view_set_zoom_level(c->view,
  1321. curconfig[ZoomLevel].val.f + 0.1);
  1322. else if (a->i < 0)
  1323. webkit_web_view_set_zoom_level(c->view,
  1324. curconfig[ZoomLevel].val.f - 0.1);
  1325. else
  1326. webkit_web_view_set_zoom_level(c->view, 1.0);
  1327. curconfig[ZoomLevel].val.f = webkit_web_view_get_zoom_level(c->view);
  1328. }
  1329. void
  1330. scroll(Client *c, const Arg *a)
  1331. {
  1332. GdkEvent *ev = gdk_event_new(GDK_KEY_PRESS);
  1333. gdk_event_set_device(ev, gdkkb);
  1334. ev->key.window = gtk_widget_get_window(GTK_WIDGET(c->win));
  1335. ev->key.state = GDK_CONTROL_MASK;
  1336. ev->key.time = GDK_CURRENT_TIME;
  1337. switch (a->i) {
  1338. case 'd':
  1339. ev->key.keyval = GDK_KEY_Down;
  1340. break;
  1341. case 'D':
  1342. ev->key.keyval = GDK_KEY_Page_Down;
  1343. break;
  1344. case 'l':
  1345. ev->key.keyval = GDK_KEY_Left;
  1346. break;
  1347. case 'r':
  1348. ev->key.keyval = GDK_KEY_Right;
  1349. break;
  1350. case 'U':
  1351. ev->key.keyval = GDK_KEY_Page_Up;
  1352. break;
  1353. case 'u':
  1354. ev->key.keyval = GDK_KEY_Up;
  1355. break;
  1356. }
  1357. gdk_event_put(ev);
  1358. }
  1359. void
  1360. navigate(Client *c, const Arg *a)
  1361. {
  1362. if (a->i < 0)
  1363. webkit_web_view_go_back(c->view);
  1364. else if (a->i > 0)
  1365. webkit_web_view_go_forward(c->view);
  1366. }
  1367. void
  1368. stop(Client *c, const Arg *a)
  1369. {
  1370. webkit_web_view_stop_loading(c->view);
  1371. }
  1372. void
  1373. toggle(Client *c, const Arg *a)
  1374. {
  1375. curconfig[a->i].val.b ^= 1;
  1376. setparameter(c, 1, (ParamName)a->i, &curconfig[a->i].val);
  1377. }
  1378. void
  1379. togglefullscreen(Client *c, const Arg *a)
  1380. {
  1381. /* toggling value is handled in winevent() */
  1382. if (c->fullscreen)
  1383. gtk_window_unfullscreen(GTK_WINDOW(c->win));
  1384. else
  1385. gtk_window_fullscreen(GTK_WINDOW(c->win));
  1386. }
  1387. void
  1388. togglecookiepolicy(Client *c, const Arg *a)
  1389. {
  1390. ++cookiepolicy;
  1391. cookiepolicy %= strlen(curconfig[CookiePolicies].val.v);
  1392. setparameter(c, 0, CookiePolicies, NULL);
  1393. }
  1394. void
  1395. toggleinspector(Client *c, const Arg *a)
  1396. {
  1397. if (webkit_web_inspector_is_attached(c->inspector))
  1398. webkit_web_inspector_close(c->inspector);
  1399. else if (curconfig[Inspector].val.b)
  1400. webkit_web_inspector_show(c->inspector);
  1401. }
  1402. void
  1403. find(Client *c, const Arg *a)
  1404. {
  1405. const char *s, *f;
  1406. if (a && a->i) {
  1407. if (a->i > 0)
  1408. webkit_find_controller_search_next(c->finder);
  1409. else
  1410. webkit_find_controller_search_previous(c->finder);
  1411. } else {
  1412. s = getatom(c, AtomFind);
  1413. f = webkit_find_controller_get_search_text(c->finder);
  1414. if (g_strcmp0(f, s) == 0) /* reset search */
  1415. webkit_find_controller_search(c->finder, "", findopts,
  1416. G_MAXUINT);
  1417. webkit_find_controller_search(c->finder, s, findopts,
  1418. G_MAXUINT);
  1419. if (strcmp(s, "") == 0)
  1420. webkit_find_controller_search_finish(c->finder);
  1421. }
  1422. }
  1423. void
  1424. clicknavigate(Client *c, const Arg *a, WebKitHitTestResult *h)
  1425. {
  1426. navigate(c, a);
  1427. }
  1428. void
  1429. clicknewwindow(Client *c, const Arg *a, WebKitHitTestResult *h)
  1430. {
  1431. Arg arg;
  1432. arg.v = webkit_hit_test_result_get_link_uri(h);
  1433. newwindow(c, &arg, a->b);
  1434. }
  1435. void
  1436. clickexternplayer(Client *c, const Arg *a, WebKitHitTestResult *h)
  1437. {
  1438. Arg arg;
  1439. arg = (Arg)VIDEOPLAY(webkit_hit_test_result_get_media_uri(h));
  1440. spawn(c, &arg);
  1441. }
  1442. int
  1443. main(int argc, char *argv[])
  1444. {
  1445. Arg arg;
  1446. Client *c;
  1447. memset(&arg, 0, sizeof(arg));
  1448. /* command line args */
  1449. ARGBEGIN {
  1450. case 'a':
  1451. defconfig CSETV(CookiePolicies, EARGF(usage()));
  1452. break;
  1453. case 'b':
  1454. defconfig CSETB(ScrollBars, 0);
  1455. break;
  1456. case 'B':
  1457. defconfig CSETB(ScrollBars, 1);
  1458. break;
  1459. case 'c':
  1460. cookiefile = EARGF(usage());
  1461. break;
  1462. case 'd':
  1463. defconfig CSETB(DiskCache, 0);
  1464. break;
  1465. case 'D':
  1466. defconfig CSETB(DiskCache, 1);
  1467. break;
  1468. case 'e':
  1469. embed = strtol(EARGF(usage()), NULL, 0);
  1470. break;
  1471. case 'f':
  1472. defconfig CSETB(RunInFullscreen, 0);
  1473. break;
  1474. case 'F':
  1475. defconfig CSETB(RunInFullscreen, 1);
  1476. break;
  1477. case 'g':
  1478. defconfig CSETB(Geolocation, 0);
  1479. break;
  1480. case 'G':
  1481. defconfig CSETB(Geolocation, 1);
  1482. break;
  1483. case 'i':
  1484. defconfig CSETB(LoadImages, 0);
  1485. break;
  1486. case 'I':
  1487. defconfig CSETB(LoadImages, 1);
  1488. break;
  1489. case 'k':
  1490. defconfig CSETB(KioskMode, 0);
  1491. break;
  1492. case 'K':
  1493. defconfig CSETB(KioskMode, 1);
  1494. break;
  1495. case 'm':
  1496. defconfig CSETB(Style, 0);
  1497. break;
  1498. case 'M':
  1499. defconfig CSETB(Style, 1);
  1500. break;
  1501. case 'n':
  1502. defconfig CSETB(Inspector, 0);
  1503. break;
  1504. case 'N':
  1505. defconfig CSETB(Inspector, 1);
  1506. break;
  1507. case 'p':
  1508. defconfig CSETB(Plugins, 0);
  1509. break;
  1510. case 'P':
  1511. defconfig CSETB(Plugins, 1);
  1512. break;
  1513. case 'r':
  1514. scriptfile = EARGF(usage());
  1515. break;
  1516. case 's':
  1517. defconfig CSETB(JavaScript, 0);
  1518. break;
  1519. case 'S':
  1520. defconfig CSETB(JavaScript, 1);
  1521. break;
  1522. case 't':
  1523. stylefile = EARGF(usage());
  1524. break;
  1525. case 'u':
  1526. fulluseragent = EARGF(usage());
  1527. break;
  1528. case 'v':
  1529. die("surf-"VERSION", ©2009-2015 surf engineers, "
  1530. "see LICENSE for details\n");
  1531. case 'x':
  1532. showxid = 1;
  1533. break;
  1534. case 'z':
  1535. defconfig CSETF(ZoomLevel, strtof(EARGF(usage()), NULL));
  1536. break;
  1537. default:
  1538. usage();
  1539. } ARGEND;
  1540. if (argc > 0)
  1541. arg.v = argv[0];
  1542. else
  1543. arg.v = "about:blank";
  1544. setup();
  1545. c = newclient(NULL);
  1546. showview(NULL, c);
  1547. loaduri(c, &arg);
  1548. updatetitle(c);
  1549. gtk_main();
  1550. cleanup();
  1551. return 0;
  1552. }