surf.c 52 KB

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