From ba01fa15dc0b66f9c84e5fbee72b251c90ff544b Mon Sep 17 00:00:00 2001 From: TolaMironcenko Date: Sun, 14 Jul 2024 14:06:44 +0700 Subject: [PATCH] surf added to dwm desktop --- dwm/surf/FAQ.md | 10 + dwm/surf/LICENSE | 57 ++ dwm/surf/Makefile | 76 ++ dwm/surf/README | 40 + dwm/surf/TODO.md | 10 + dwm/surf/arg.h | 48 + dwm/surf/common.h | 1 + dwm/surf/config.def.h | 193 ++++ dwm/surf/config.h | 193 ++++ dwm/surf/config.mk | 32 + dwm/surf/surf | Bin 0 -> 74784 bytes dwm/surf/surf-open.sh | 32 + dwm/surf/surf.1 | 305 ++++++ dwm/surf/surf.c | 2131 +++++++++++++++++++++++++++++++++++++++ dwm/surf/surf.o | Bin 0 -> 71816 bytes dwm/surf/surf.png | Bin 0 -> 240 bytes dwm/surf/webext-surf.c | 166 +++ dwm/surf/webext-surf.o | Bin 0 -> 6560 bytes dwm/surf/webext-surf.so | Bin 0 -> 16728 bytes 19 files changed, 3294 insertions(+) create mode 100644 dwm/surf/FAQ.md create mode 100644 dwm/surf/LICENSE create mode 100644 dwm/surf/Makefile create mode 100644 dwm/surf/README create mode 100644 dwm/surf/TODO.md create mode 100644 dwm/surf/arg.h create mode 100644 dwm/surf/common.h create mode 100644 dwm/surf/config.def.h create mode 100644 dwm/surf/config.h create mode 100644 dwm/surf/config.mk create mode 100755 dwm/surf/surf create mode 100755 dwm/surf/surf-open.sh create mode 100644 dwm/surf/surf.1 create mode 100644 dwm/surf/surf.c create mode 100644 dwm/surf/surf.o create mode 100644 dwm/surf/surf.png create mode 100644 dwm/surf/webext-surf.c create mode 100644 dwm/surf/webext-surf.o create mode 100755 dwm/surf/webext-surf.so diff --git a/dwm/surf/FAQ.md b/dwm/surf/FAQ.md new file mode 100644 index 0000000..48e6097 --- /dev/null +++ b/dwm/surf/FAQ.md @@ -0,0 +1,10 @@ +# Frequently Asked Questions + +## Surf is starting up slowly. What might be causing this? + +The first suspect for such behaviour is the plugin handling. Run surf on +the commandline and see if there are errors because of “nspluginwrapper” +or failed RPCs to them. If that is true, go to ~/.mozilla/plugins and +try removing stale links to plugins not on your system anymore. This +will stop surf from trying to load them. + diff --git a/dwm/surf/LICENSE b/dwm/surf/LICENSE new file mode 100644 index 0000000..809d284 --- /dev/null +++ b/dwm/surf/LICENSE @@ -0,0 +1,57 @@ +MIT/X Consortium License + +© 2009-2010 Enno Boland +© 2009 Thomas Menari +© 2009 Simon Rozet +© 2009 Andrew Antle +© 2010-2011 pancake +© 2011-2013 Anselm R Garbe +© 2011-2012 Troels Henriksen +© 2011 Connor Lane Smith +© 2012-2017 Christoph Lohmann <20h@r-36.net> +© 2013 Shayan Pooya +© 2013 Jens Nyberg +© 2013 Carlos J. Torres +© 2013 Alexander Sedov +© 2013 Nick White +© 2013 David Dufberg +© 2014-2021 Quentin Rameau +© 2014-2016 Markus Teich +© 2015 Jakukyo Friel +© 2015 Ben Woolley +© 2015 Greg Reagle +© 2015 GhostAV +© 2015-2017 Ivan Tham +© 2015 Alexander Huemer +© 2015 Michael Stevens +© 2015 Felix Janda +© 2016 Charles Lehner +© 2016 Dmitry Bogatov +© 2017 Hiltjo Posthuma +© 2017 ssd +© 2017 Constantine Bytensky +© 2017 Eon S. Jeon +© 2017 Jochen Sprickerhof +© 2018 nzl +© 2018 Eddie Thieda +© 2018 Leonardo Taccari +© 2019 efe + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + diff --git a/dwm/surf/Makefile b/dwm/surf/Makefile new file mode 100644 index 0000000..1edf820 --- /dev/null +++ b/dwm/surf/Makefile @@ -0,0 +1,76 @@ +# surf - simple browser +# See LICENSE file for copyright and license details. +.POSIX: + +include config.mk + +SRC = surf.c +WSRC = webext-surf.c +OBJ = $(SRC:.c=.o) +WOBJ = $(WSRC:.c=.o) +WLIB = $(WSRC:.c=.so) + +all: options surf $(WLIB) + +options: + @echo surf build options: + @echo "CC = $(CC)" + @echo "CFLAGS = $(SURFCFLAGS) $(CFLAGS)" + @echo "WEBEXTCFLAGS = $(WEBEXTCFLAGS) $(CFLAGS)" + @echo "LDFLAGS = $(LDFLAGS)" + +surf: $(OBJ) + $(CC) $(SURFLDFLAGS) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) + +$(OBJ) $(WOBJ): config.h common.h config.mk + +config.h: + cp config.def.h $@ + +$(OBJ): $(SRC) + $(CC) $(SURFCFLAGS) $(CFLAGS) -c $(SRC) + +$(WLIB): $(WOBJ) + $(CC) -shared -Wl,-soname,$@ $(LDFLAGS) -o $@ $? $(WEBEXTLIBS) + +$(WOBJ): $(WSRC) + $(CC) $(WEBEXTCFLAGS) $(CFLAGS) -c $(WSRC) + +clean: + rm -f surf $(OBJ) + rm -f $(WLIB) $(WOBJ) + +distclean: clean + rm -f config.h surf-$(VERSION).tar.gz + +dist: distclean + mkdir -p surf-$(VERSION) + cp -R LICENSE Makefile config.mk config.def.h README \ + surf-open.sh arg.h TODO.md surf.png \ + surf.1 common.h $(SRC) $(WSRC) surf-$(VERSION) + tar -cf surf-$(VERSION).tar surf-$(VERSION) + gzip surf-$(VERSION).tar + rm -rf surf-$(VERSION) + +install: all + mkdir -p $(DESTDIR)$(PREFIX)/bin + cp -f surf $(DESTDIR)$(PREFIX)/bin + chmod 755 $(DESTDIR)$(PREFIX)/bin/surf + mkdir -p $(DESTDIR)$(LIBDIR) + cp -f $(WLIB) $(DESTDIR)$(LIBDIR) + for wlib in $(WLIB); do \ + chmod 644 $(DESTDIR)$(LIBDIR)/$$wlib; \ + done + mkdir -p $(DESTDIR)$(MANPREFIX)/man1 + sed "s/VERSION/$(VERSION)/g" < surf.1 > $(DESTDIR)$(MANPREFIX)/man1/surf.1 + chmod 644 $(DESTDIR)$(MANPREFIX)/man1/surf.1 + +uninstall: + rm -f $(DESTDIR)$(PREFIX)/bin/surf + rm -f $(DESTDIR)$(MANPREFIX)/man1/surf.1 + for wlib in $(WLIB); do \ + rm -f $(DESTDIR)$(LIBDIR)/$$wlib; \ + done + - rmdir $(DESTDIR)$(LIBDIR) + +.PHONY: all options distclean clean dist install uninstall diff --git a/dwm/surf/README b/dwm/surf/README new file mode 100644 index 0000000..da4577f --- /dev/null +++ b/dwm/surf/README @@ -0,0 +1,40 @@ +surf - simple webkit-based browser +================================== +surf is a simple Web browser based on WebKit/GTK+. + +Requirements +------------ +In order to build surf you need GTK+ and Webkit/GTK+ header files. + +In order to use the functionality of the url-bar, also install dmenu[0]. + +Installation +------------ +Edit config.mk to match your local setup (surf is installed into +the /usr/local namespace by default). + +Afterwards enter the following command to build and install surf (if +necessary as root): + + make clean install + +Running surf +------------ +run + surf [URI] + +See the manpage for further options. + +Running surf in tabbed +---------------------- +For running surf in tabbed[1] there is a script included in the distribution, +which is run like this: + + surf-open.sh [URI] + +Further invocations of the script will run surf with the specified URI in this +instance of tabbed. + +[0] http://tools.suckless.org/dmenu +[1] http://tools.suckless.org/tabbed + diff --git a/dwm/surf/TODO.md b/dwm/surf/TODO.md new file mode 100644 index 0000000..da5f44d --- /dev/null +++ b/dwm/surf/TODO.md @@ -0,0 +1,10 @@ +# TODO + +* suckless adblocking +* replace twitch() with proper gtk calls to make scrollbars reappear +* replace webkit with something sane +* add video player options + * play in plugin + * play in video player + * call command with URI (quvi + cclive) + diff --git a/dwm/surf/arg.h b/dwm/surf/arg.h new file mode 100644 index 0000000..ba3fb3f --- /dev/null +++ b/dwm/surf/arg.h @@ -0,0 +1,48 @@ +/* + * Copy me if you can. + * by 20h + */ + +#ifndef ARG_H__ +#define ARG_H__ + +extern char *argv0; + +/* use main(int argc, char *argv[]) */ +#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ + argv[0] && argv[0][0] == '-'\ + && argv[0][1];\ + argc--, argv++) {\ + char argc_;\ + char **argv_;\ + int brk_;\ + if (argv[0][1] == '-' && argv[0][2] == '\0') {\ + argv++;\ + argc--;\ + break;\ + }\ + for (brk_ = 0, argv[0]++, argv_ = argv;\ + argv[0][0] && !brk_;\ + argv[0]++) {\ + if (argv_ != argv)\ + break;\ + argc_ = argv[0][0];\ + switch (argc_) +#define ARGEND }\ + } + +#define ARGC() argc_ + +#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\ + ((x), abort(), (char *)0) :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\ + (char *)0 :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#endif diff --git a/dwm/surf/common.h b/dwm/surf/common.h new file mode 100644 index 0000000..3990c42 --- /dev/null +++ b/dwm/surf/common.h @@ -0,0 +1 @@ +#define MSGBUFSZ 8 diff --git a/dwm/surf/config.def.h b/dwm/surf/config.def.h new file mode 100644 index 0000000..c5370b9 --- /dev/null +++ b/dwm/surf/config.def.h @@ -0,0 +1,193 @@ +/* modifier 0 means no modifier */ +static int surfuseragent = 1; /* Append Surf version to default WebKit user agent */ +static char *fulluseragent = ""; /* Or override the whole user agent string */ +static char *scriptfile = "~/.surf/script.js"; +static char *styledir = "~/.surf/styles/"; +static char *certdir = "~/.surf/certificates/"; +static char *cachedir = "~/.surf/cache/"; +static char *cookiefile = "~/.surf/cookies.txt"; + +/* Webkit default features */ +/* Highest priority value will be used. + * Default parameters are priority 0 + * Per-uri parameters are priority 1 + * Command parameters are priority 2 + */ +static Parameter defconfig[ParameterLast] = { + /* parameter Arg value priority */ + [AccessMicrophone] = { { .i = 0 }, }, + [AccessWebcam] = { { .i = 0 }, }, + [Certificate] = { { .i = 0 }, }, + [CaretBrowsing] = { { .i = 0 }, }, + [CookiePolicies] = { { .v = "@Aa" }, }, + [DarkMode] = { { .i = 0 }, }, + [DefaultCharset] = { { .v = "UTF-8" }, }, + [DiskCache] = { { .i = 1 }, }, + [DNSPrefetch] = { { .i = 0 }, }, + [Ephemeral] = { { .i = 0 }, }, + [FileURLsCrossAccess] = { { .i = 0 }, }, + [FontSize] = { { .i = 12 }, }, + [Geolocation] = { { .i = 0 }, }, + [HideBackground] = { { .i = 0 }, }, + [Inspector] = { { .i = 0 }, }, + [JavaScript] = { { .i = 1 }, }, + [KioskMode] = { { .i = 0 }, }, + [LoadImages] = { { .i = 1 }, }, + [MediaManualPlay] = { { .i = 1 }, }, + [PreferredLanguages] = { { .v = (char *[]){ NULL } }, }, + [RunInFullscreen] = { { .i = 0 }, }, + [ScrollBars] = { { .i = 1 }, }, + [ShowIndicators] = { { .i = 1 }, }, + [SiteQuirks] = { { .i = 1 }, }, + [SmoothScrolling] = { { .i = 0 }, }, + [SpellChecking] = { { .i = 0 }, }, + [SpellLanguages] = { { .v = ((char *[]){ "en_US", NULL }) }, }, + [StrictTLS] = { { .i = 1 }, }, + [Style] = { { .i = 1 }, }, + [WebGL] = { { .i = 0 }, }, + [ZoomLevel] = { { .f = 1.0 }, }, +}; + +static UriParameters uriparams[] = { + { "(://|\\.)suckless\\.org(/|$)", { + [JavaScript] = { { .i = 0 }, 1 }, + }, }, +}; + +/* default window size: width, height */ +static int winsize[] = { 800, 600 }; + +static WebKitFindOptions findopts = WEBKIT_FIND_OPTIONS_CASE_INSENSITIVE | + WEBKIT_FIND_OPTIONS_WRAP_AROUND; + +#define PROMPT_GO "Go:" +#define PROMPT_FIND "Find:" + +/* SETPROP(readprop, setprop, prompt)*/ +#define SETPROP(r, s, p) { \ + .v = (const char *[]){ "/bin/sh", "-c", \ + "prop=\"$(printf '%b' \"$(xprop -id $1 "r" " \ + "| sed -e 's/^"r"(UTF8_STRING) = \"\\(.*\\)\"/\\1/' " \ + " -e 's/\\\\\\(.\\)/\\1/g')\" " \ + "| dmenu -p '"p"' -w $1)\" " \ + "&& xprop -id $1 -f "s" 8u -set "s" \"$prop\"", \ + "surf-setprop", winid, NULL \ + } \ +} + +/* DOWNLOAD(URI, referer) */ +#define DOWNLOAD(u, r) { \ + .v = (const char *[]){ "st", "-e", "/bin/sh", "-c",\ + "curl -g -L -J -O -A \"$1\" -b \"$2\" -c \"$2\"" \ + " -e \"$3\" \"$4\"; read", \ + "surf-download", useragent, cookiefile, r, u, NULL \ + } \ +} + +/* PLUMB(URI) */ +/* This called when some URI which does not begin with "about:", + * "http://" or "https://" should be opened. + */ +#define PLUMB(u) {\ + .v = (const char *[]){ "/bin/sh", "-c", \ + "xdg-open \"$0\"", u, NULL \ + } \ +} + +/* VIDEOPLAY(URI) */ +#define VIDEOPLAY(u) {\ + .v = (const char *[]){ "/bin/sh", "-c", \ + "mpv --really-quiet \"$0\"", u, NULL \ + } \ +} + +/* styles */ +/* + * The iteration will stop at the first match, beginning at the beginning of + * the list. + */ +static SiteSpecific styles[] = { + /* regexp file in $styledir */ + { ".*", "default.css" }, +}; + +/* certificates */ +/* + * Provide custom certificate for urls + */ +static SiteSpecific certs[] = { + /* regexp file in $certdir */ + { "://suckless\\.org/", "suckless.org.crt" }, +}; + +#define MODKEY GDK_CONTROL_MASK + +/* hotkeys */ +/* + * If you use anything else but MODKEY and GDK_SHIFT_MASK, don't forget to + * edit the CLEANMASK() macro. + */ +static Key keys[] = { + /* modifier keyval function arg */ + { MODKEY, GDK_KEY_g, spawn, SETPROP("_SURF_URI", "_SURF_GO", PROMPT_GO) }, + { MODKEY, GDK_KEY_f, spawn, SETPROP("_SURF_FIND", "_SURF_FIND", PROMPT_FIND) }, + { MODKEY, GDK_KEY_slash, spawn, SETPROP("_SURF_FIND", "_SURF_FIND", PROMPT_FIND) }, + + { 0, GDK_KEY_Escape, stop, { 0 } }, + { MODKEY, GDK_KEY_c, stop, { 0 } }, + + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_r, reload, { .i = 1 } }, + { MODKEY, GDK_KEY_r, reload, { .i = 0 } }, + + { MODKEY, GDK_KEY_l, navigate, { .i = +1 } }, + { MODKEY, GDK_KEY_h, navigate, { .i = -1 } }, + + /* vertical and horizontal scrolling, in viewport percentage */ + { MODKEY, GDK_KEY_j, scrollv, { .i = +10 } }, + { MODKEY, GDK_KEY_k, scrollv, { .i = -10 } }, + { MODKEY, GDK_KEY_space, scrollv, { .i = +50 } }, + { MODKEY, GDK_KEY_b, scrollv, { .i = -50 } }, + { MODKEY, GDK_KEY_i, scrollh, { .i = +10 } }, + { MODKEY, GDK_KEY_u, scrollh, { .i = -10 } }, + + + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_j, zoom, { .i = -1 } }, + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_k, zoom, { .i = +1 } }, + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_q, zoom, { .i = 0 } }, + { MODKEY, GDK_KEY_minus, zoom, { .i = -1 } }, + { MODKEY, GDK_KEY_plus, zoom, { .i = +1 } }, + + { MODKEY, GDK_KEY_p, clipboard, { .i = 1 } }, + { MODKEY, GDK_KEY_y, clipboard, { .i = 0 } }, + + { MODKEY, GDK_KEY_n, find, { .i = +1 } }, + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_n, find, { .i = -1 } }, + + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_p, print, { 0 } }, + { MODKEY, GDK_KEY_t, showcert, { 0 } }, + + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_a, togglecookiepolicy, { 0 } }, + { 0, GDK_KEY_F11, togglefullscreen, { 0 } }, + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_o, toggleinspector, { 0 } }, + + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_c, toggle, { .i = CaretBrowsing } }, + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_g, toggle, { .i = Geolocation } }, + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_s, toggle, { .i = JavaScript } }, + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_i, toggle, { .i = LoadImages } }, + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_b, toggle, { .i = ScrollBars } }, + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_t, toggle, { .i = StrictTLS } }, + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_m, toggle, { .i = Style } }, + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_d, toggle, { .i = DarkMode } }, +}; + +/* button definitions */ +/* target can be OnDoc, OnLink, OnImg, OnMedia, OnEdit, OnBar, OnSel, OnAny */ +static Button buttons[] = { + /* target event mask button function argument stop event */ + { OnLink, 0, 2, clicknewwindow, { .i = 0 }, 1 }, + { OnLink, MODKEY, 2, clicknewwindow, { .i = 1 }, 1 }, + { OnLink, MODKEY, 1, clicknewwindow, { .i = 1 }, 1 }, + { OnAny, 0, 8, clicknavigate, { .i = -1 }, 1 }, + { OnAny, 0, 9, clicknavigate, { .i = +1 }, 1 }, + { OnMedia, MODKEY, 1, clickexternplayer, { 0 }, 1 }, +}; diff --git a/dwm/surf/config.h b/dwm/surf/config.h new file mode 100644 index 0000000..c5370b9 --- /dev/null +++ b/dwm/surf/config.h @@ -0,0 +1,193 @@ +/* modifier 0 means no modifier */ +static int surfuseragent = 1; /* Append Surf version to default WebKit user agent */ +static char *fulluseragent = ""; /* Or override the whole user agent string */ +static char *scriptfile = "~/.surf/script.js"; +static char *styledir = "~/.surf/styles/"; +static char *certdir = "~/.surf/certificates/"; +static char *cachedir = "~/.surf/cache/"; +static char *cookiefile = "~/.surf/cookies.txt"; + +/* Webkit default features */ +/* Highest priority value will be used. + * Default parameters are priority 0 + * Per-uri parameters are priority 1 + * Command parameters are priority 2 + */ +static Parameter defconfig[ParameterLast] = { + /* parameter Arg value priority */ + [AccessMicrophone] = { { .i = 0 }, }, + [AccessWebcam] = { { .i = 0 }, }, + [Certificate] = { { .i = 0 }, }, + [CaretBrowsing] = { { .i = 0 }, }, + [CookiePolicies] = { { .v = "@Aa" }, }, + [DarkMode] = { { .i = 0 }, }, + [DefaultCharset] = { { .v = "UTF-8" }, }, + [DiskCache] = { { .i = 1 }, }, + [DNSPrefetch] = { { .i = 0 }, }, + [Ephemeral] = { { .i = 0 }, }, + [FileURLsCrossAccess] = { { .i = 0 }, }, + [FontSize] = { { .i = 12 }, }, + [Geolocation] = { { .i = 0 }, }, + [HideBackground] = { { .i = 0 }, }, + [Inspector] = { { .i = 0 }, }, + [JavaScript] = { { .i = 1 }, }, + [KioskMode] = { { .i = 0 }, }, + [LoadImages] = { { .i = 1 }, }, + [MediaManualPlay] = { { .i = 1 }, }, + [PreferredLanguages] = { { .v = (char *[]){ NULL } }, }, + [RunInFullscreen] = { { .i = 0 }, }, + [ScrollBars] = { { .i = 1 }, }, + [ShowIndicators] = { { .i = 1 }, }, + [SiteQuirks] = { { .i = 1 }, }, + [SmoothScrolling] = { { .i = 0 }, }, + [SpellChecking] = { { .i = 0 }, }, + [SpellLanguages] = { { .v = ((char *[]){ "en_US", NULL }) }, }, + [StrictTLS] = { { .i = 1 }, }, + [Style] = { { .i = 1 }, }, + [WebGL] = { { .i = 0 }, }, + [ZoomLevel] = { { .f = 1.0 }, }, +}; + +static UriParameters uriparams[] = { + { "(://|\\.)suckless\\.org(/|$)", { + [JavaScript] = { { .i = 0 }, 1 }, + }, }, +}; + +/* default window size: width, height */ +static int winsize[] = { 800, 600 }; + +static WebKitFindOptions findopts = WEBKIT_FIND_OPTIONS_CASE_INSENSITIVE | + WEBKIT_FIND_OPTIONS_WRAP_AROUND; + +#define PROMPT_GO "Go:" +#define PROMPT_FIND "Find:" + +/* SETPROP(readprop, setprop, prompt)*/ +#define SETPROP(r, s, p) { \ + .v = (const char *[]){ "/bin/sh", "-c", \ + "prop=\"$(printf '%b' \"$(xprop -id $1 "r" " \ + "| sed -e 's/^"r"(UTF8_STRING) = \"\\(.*\\)\"/\\1/' " \ + " -e 's/\\\\\\(.\\)/\\1/g')\" " \ + "| dmenu -p '"p"' -w $1)\" " \ + "&& xprop -id $1 -f "s" 8u -set "s" \"$prop\"", \ + "surf-setprop", winid, NULL \ + } \ +} + +/* DOWNLOAD(URI, referer) */ +#define DOWNLOAD(u, r) { \ + .v = (const char *[]){ "st", "-e", "/bin/sh", "-c",\ + "curl -g -L -J -O -A \"$1\" -b \"$2\" -c \"$2\"" \ + " -e \"$3\" \"$4\"; read", \ + "surf-download", useragent, cookiefile, r, u, NULL \ + } \ +} + +/* PLUMB(URI) */ +/* This called when some URI which does not begin with "about:", + * "http://" or "https://" should be opened. + */ +#define PLUMB(u) {\ + .v = (const char *[]){ "/bin/sh", "-c", \ + "xdg-open \"$0\"", u, NULL \ + } \ +} + +/* VIDEOPLAY(URI) */ +#define VIDEOPLAY(u) {\ + .v = (const char *[]){ "/bin/sh", "-c", \ + "mpv --really-quiet \"$0\"", u, NULL \ + } \ +} + +/* styles */ +/* + * The iteration will stop at the first match, beginning at the beginning of + * the list. + */ +static SiteSpecific styles[] = { + /* regexp file in $styledir */ + { ".*", "default.css" }, +}; + +/* certificates */ +/* + * Provide custom certificate for urls + */ +static SiteSpecific certs[] = { + /* regexp file in $certdir */ + { "://suckless\\.org/", "suckless.org.crt" }, +}; + +#define MODKEY GDK_CONTROL_MASK + +/* hotkeys */ +/* + * If you use anything else but MODKEY and GDK_SHIFT_MASK, don't forget to + * edit the CLEANMASK() macro. + */ +static Key keys[] = { + /* modifier keyval function arg */ + { MODKEY, GDK_KEY_g, spawn, SETPROP("_SURF_URI", "_SURF_GO", PROMPT_GO) }, + { MODKEY, GDK_KEY_f, spawn, SETPROP("_SURF_FIND", "_SURF_FIND", PROMPT_FIND) }, + { MODKEY, GDK_KEY_slash, spawn, SETPROP("_SURF_FIND", "_SURF_FIND", PROMPT_FIND) }, + + { 0, GDK_KEY_Escape, stop, { 0 } }, + { MODKEY, GDK_KEY_c, stop, { 0 } }, + + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_r, reload, { .i = 1 } }, + { MODKEY, GDK_KEY_r, reload, { .i = 0 } }, + + { MODKEY, GDK_KEY_l, navigate, { .i = +1 } }, + { MODKEY, GDK_KEY_h, navigate, { .i = -1 } }, + + /* vertical and horizontal scrolling, in viewport percentage */ + { MODKEY, GDK_KEY_j, scrollv, { .i = +10 } }, + { MODKEY, GDK_KEY_k, scrollv, { .i = -10 } }, + { MODKEY, GDK_KEY_space, scrollv, { .i = +50 } }, + { MODKEY, GDK_KEY_b, scrollv, { .i = -50 } }, + { MODKEY, GDK_KEY_i, scrollh, { .i = +10 } }, + { MODKEY, GDK_KEY_u, scrollh, { .i = -10 } }, + + + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_j, zoom, { .i = -1 } }, + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_k, zoom, { .i = +1 } }, + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_q, zoom, { .i = 0 } }, + { MODKEY, GDK_KEY_minus, zoom, { .i = -1 } }, + { MODKEY, GDK_KEY_plus, zoom, { .i = +1 } }, + + { MODKEY, GDK_KEY_p, clipboard, { .i = 1 } }, + { MODKEY, GDK_KEY_y, clipboard, { .i = 0 } }, + + { MODKEY, GDK_KEY_n, find, { .i = +1 } }, + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_n, find, { .i = -1 } }, + + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_p, print, { 0 } }, + { MODKEY, GDK_KEY_t, showcert, { 0 } }, + + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_a, togglecookiepolicy, { 0 } }, + { 0, GDK_KEY_F11, togglefullscreen, { 0 } }, + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_o, toggleinspector, { 0 } }, + + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_c, toggle, { .i = CaretBrowsing } }, + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_g, toggle, { .i = Geolocation } }, + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_s, toggle, { .i = JavaScript } }, + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_i, toggle, { .i = LoadImages } }, + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_b, toggle, { .i = ScrollBars } }, + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_t, toggle, { .i = StrictTLS } }, + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_m, toggle, { .i = Style } }, + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_d, toggle, { .i = DarkMode } }, +}; + +/* button definitions */ +/* target can be OnDoc, OnLink, OnImg, OnMedia, OnEdit, OnBar, OnSel, OnAny */ +static Button buttons[] = { + /* target event mask button function argument stop event */ + { OnLink, 0, 2, clicknewwindow, { .i = 0 }, 1 }, + { OnLink, MODKEY, 2, clicknewwindow, { .i = 1 }, 1 }, + { OnLink, MODKEY, 1, clicknewwindow, { .i = 1 }, 1 }, + { OnAny, 0, 8, clicknavigate, { .i = -1 }, 1 }, + { OnAny, 0, 9, clicknavigate, { .i = +1 }, 1 }, + { OnMedia, MODKEY, 1, clickexternplayer, { 0 }, 1 }, +}; diff --git a/dwm/surf/config.mk b/dwm/surf/config.mk new file mode 100644 index 0000000..2eb9fb0 --- /dev/null +++ b/dwm/surf/config.mk @@ -0,0 +1,32 @@ +# surf version +VERSION = 2.1 + +# Customize below to fit your system + +# paths +PREFIX = /usr/local +MANPREFIX = $(PREFIX)/share/man +LIBPREFIX = $(PREFIX)/lib +LIBDIR = $(LIBPREFIX)/surf + +X11INC = `pkg-config --cflags x11` +X11LIB = `pkg-config --libs x11` + +GTKINC = `pkg-config --cflags gtk+-3.0 gcr-3 webkit2gtk-4.0` +GTKLIB = `pkg-config --libs gtk+-3.0 gcr-3 webkit2gtk-4.0` +WEBEXTINC = `pkg-config --cflags webkit2gtk-4.0 webkit2gtk-web-extension-4.0 gio-2.0` +WEBEXTLIBS = `pkg-config --libs webkit2gtk-4.0 webkit2gtk-web-extension-4.0 gio-2.0` + +# includes and libs +INCS = $(X11INC) $(GTKINC) +LIBS = $(X11LIB) $(GTKLIB) -lgthread-2.0 + +# flags +CPPFLAGS = -DVERSION=\"$(VERSION)\" -DGCR_API_SUBJECT_TO_CHANGE \ + -DLIBPREFIX=\"$(LIBPREFIX)\" -DWEBEXTDIR=\"$(LIBDIR)\" \ + -D_DEFAULT_SOURCE +SURFCFLAGS = -fPIC $(INCS) $(CPPFLAGS) +WEBEXTCFLAGS = -fPIC $(WEBEXTINC) + +# compiler +#CC = c99 diff --git a/dwm/surf/surf b/dwm/surf/surf new file mode 100755 index 0000000000000000000000000000000000000000..e15a0fd0dbae4c1075e5613417545c31eb00f685 GIT binary patch literal 74784 zcmeFa33yaR+BSR=(hw2SaRE_LTMV1p5D*a1Xn+8XMvZKPij#CYouna2x80o(7B$*{ zv~5OQ2X}NF#O0k~bYzfGh@gP$AS$BcNZjzms4%FAxa7O7~kD=}5$7$xI22E|AREhc}(bzLGX9X_ZdNe0Eo#i)4x=jckdT&%xUwiBsjc zOC@hfJ)bCVjmnoIzc@kimeljHZ9K2abkF-8Wo0jk`p`I^(tSgEKvHEWEs4seHtzM|59F&%C~HTw38|`)Obls zsb*Y?d~BEh?xnijn5xRN>xXr!-j=k++0d#$>8KGytIFI}ftuQScm3#5?olI#M1n(x z3l~`TTzojDCSN?mNN+Hj$XWT*j5L9d0LLp`VJn@w$Ku1{ng4J5Px*BHo|A4&>$agh z>acxQU+PM8`7;KQ+l9|R@LB)yt&8RyF=6L~ ziEn=~=i0j$4KMxsmp#V*_Q)qL{J81et7i3m@s<;uQ0w&i0sV1wCn>+_(K(+1hda}+ zgL9qnYmVuhZ|K%Je;+E^nVrYFch1Lb>}T27c@f4tv;Qd?x-)(;I=D0bVH^8F8~a0T z^t;*kKM8%(S-DGX{CovH+?oC!n|?XMru{#)>EByz{4{OsgfRF!^XJ;E&iOZO>ic=m z&h_`(__NBUeJ;hM>dc>Xn|8a@#{Yy(x#Miw?Ry*jrx2h!^XDZS{iQbT^B0@;tg)%@ zIGg$Ykj*%8pdF6tBtDD)*_r>pwiyrOq1>7Ni8gWOXdC~ZwrQUsHh!LB(>~K|=Hpl! z`}=JC588~M-`b3ar)=y*ZTjU!n|6M{W?qc5;fG=&>#Y5MwwW(~u<`#G8$Soy)azZF zaX#0k+-^4G@DDa|c%e-{K58>x-m&T5u#Nt1oA$rlW_+G!Q{OL-?7aOSv#~SQrhSIk z%rCD^yxL&XUuib&+1I9hdf3=M&8EJW*vyN0Hg=A)X`fsh{!H|j3!l`h2}Eb(`~sVD z2inY+Yi;!FZ06C2Hv1EY&G=kl(~qlc=FtZ>{nFRQ&yQ{V++bs;#%8{sU^7m(*^JxE zZQ3DWqko3Y_`J%-Pc&zz{r-(jzpt`s|Hp00eZ;2S+HCAxX4C$Ev6;X3qTGJ)HTAmL zrr+POY5(mu?em$9od;~<*e5o2M%&EmOdI=`+SobYW_@2|qrVmngCY*Q-nA+BH#Xy~ zpUrsYLs@6`SKIg#u+cvW9=ZP(bv8avvl&;X+3c5ouxZcdZS79~v5o!PY~o&58~#h1@piP$ICBlQ<#@j14^?l7|oF{DheTNP2w(0kyZ0!4N+Bs;`?_b%Jn`zT-^)}$yfMqMG8GDat+GXi6zJAoPznniY@k zkXP}P=@%83`NRH-KqTr9PrqnFRj|fC-CJ7aH;RiZs)IGfk*GHuEiN{MYNv{`E(!T- z3IdT(m3O`|Yf23a)r^YtO6N!Y#oln(JHNQLCYczhsZiOV zN#G5^#j@fme+~202CB-6%L72(YQHomtw9?%#UbR0s&a}ey%7-p@<2T`0~Iyis$yTT zriQMSd81yi-iR*{D2|F&OL|)2k0L2LKjbg=Rr-B%ivu-i${L@)I1njju0Rk8UKpx^ z>w)@qhhg34WoG4kIh!nLSF0v{t<#-lRZ$FDT2&PmeAS^`qoQnX@jTHJ#ojVecXX0q z=lbW@p+?bQaaC}h;>u)S7NZIn9Od5HDtH465x+MoNMtGvdc$Rc?L=E>>V-yN$zSKM ziADrlKWv!g4$G(ybhH%VcqlIR)qBxPXz#!R)IZ7^lu4IRt>Ao}ic|*Y6??0y6vrV4 zAClgq-)O=bQAp7|yJ9J65tx@3%7yHoq zf${))&~G^`Mlg~hf$C5dhJ&Ud2Y0k8lFAmBhlAB(kjjA|#$lwkT#Q4}7NU~nwN+IS z9|i~pb~{^Jlft9MQ4Eeq$1W`nd83trD)mOJ$xuDFED(n0NC|nvvdh{n3xC;2Re_Kg z0>xqfRki*|v^eUom#rG832^XoehJ%bPhn=BzjST@ZRIa5t_%3*iA2jWLnYQ?@D^A5 zBN1tjg zFpa)IGD1>>ajzzdsTK@_w=7}&)Kmq%Wx~g>KN1RJR#}Oas6>=iMgpdW&Cx-Wwg4=(t-D^lBlbwlvbS@Z(X25)+DtLWJ4#Dy*}2r zqfI$NsHmB=x?XezMjzH3bZni!ij7u`(G>PZEHg=6VS{4?&0Q0CVPxeVU77h#(ONmWs^&3Y~B;>EclE4_x_*;cltQOuK z?t9XpRU6%+oDsp|Qm=3BVLV1>2=Uolh5`;tOlj8Cfu{|E&YP)IgN#Uln)0BYh?OOT zRWFDb$K~5fQnL<&n-i)!5Ro%8f{==Csx8Ahhi(n{k{Oa?(T|m9CA~j8Rz+p6}@q!@M+bYJN!;o5*vk6?FLhm{JuG!9-2 zoaEL3Jz143sq@**y=z?{h`o(!i_WL3HOW$|+QA*Q7v#1zj0DSKhvrD}Lf6rofjeq{ zILuZ{jh8gn9%{H*jz&3ClD*l%Cu`wATMC;|U!^uzQoIpE6}wn24r0He+N6U;tCr>1 zx5$m7((33Kx13h(IvwWRXlvu!(uI}7W~`X>oOo!GU>G&&5S==Br|Nrn#H4p(5rMi` zMJmFS3WuvUsW3j&rQzQPW7vULF~}6Xky2s4fqbaa;4&9Id>I^ zyR_?%6blEjLy6Y5@12vwhrN@WetH8W_w0v9%H*!Gv%R5}XxTcvPcW1^b1AtA_wlhg z8I~=3YL;jZ1CiqD06JDYb4O}JpJlKY+YSk;94iBGbbN#uOxiBH*+k$F5#B(M|4I-i1{^UySXn z!G@(6O9tnvuX1j2If9+xuSW&&+RYcN4nZ03g~EPs6(8JU_nbWsq}oI9}SgM@YkGoh_;jqP^OYU~LEmizBt79K4b(tF7{PbS4n& zfZO*5KD->!N*Fd;lz_^1&{uWt$VDqL&dNG)KGtWHfhmr0I^_}!4NP=KZVp@V!a@ud zfKqHy)xBT5BlU(W>T-?Ag;T~)C>}oKtP$M9Isg_2&3C<`md#)S87^}lwFfP5?g81>|%6FVXSmZt38Kg zx+L>k8qCqowtIRw*D*)Cru0POcAd_!m1@b3HvTnY-*q+45Z_VAuOI$=%boaUtD6y2 zT=e(=z7gqaEKvBEKmgzDIETKJ=rftm;ulp&TyMj#wBfg<@W)q6JNs<-K4!;qU$2nZlfs|n zlYCJMpZ|sAOHz1CzcPh4lzx2*Z|OIr@J+4K{_+(5k*g%X+J_jCt$Abl|Kwk!|JzdZ?^68s6yEc+)ZdlL zKP&m>6y9ZhZ>{EcYPp#yyj5;?3U8I$CzV&_x>9(nTq_Qxmg`B;x5_O_;azKGy=JBG z9#w8h3U8I$kiySU`pZ)I3ai~x_(gW^}G@b9dX z^;x}JN-Xk*Ti%MFYczg@#;?YmmmX?W`-eS>DkfeynSBv%Di` zjR`XO&HO2KkC39v0>UWp7;%8B_1musDx8molW(mmmkhkJzNwWmxu~AE2mCf>& zJdP?RuTZnRCGSkKhEcEaIMtH88Z`c>Bx@MUG+saFw_M|KR4aL{(0ClNN?t29zE_gP z?+0qU+DeMlH5z}MroT?(`)GWV#viZo+caMNYOF}zuJI>o`nxo~uf{iPJkQcvm)R`u zxC2!@=52YY~s>Ww(d_RrP*7(yjzK_P|XuM0~`)m9l zjUS-#xf(xE<40@!AdS!0_`w?Q(fHFfzDVQG(D+#zf2PKlX#5b3uhjUV8Xwa5T#c{S z_+c8~pz*^sewoILhhmksT;oS-`YSYkl*X^r__H;BwZ@Ou_%#|oM&s9M{8)`|()e>U zew)UhtMS`4K2PI!Y5aK_->mWHYrLuP`5M1Zs__jPKTYG8Y5a7JU#{^pG=7D~&(!#p8viSeU#;=8G=7c7 zU#9WvH2!jpZ_@ZHG=7`L&(`?u8n0eSh}2ygU##gjYkY~un;P%c_Z(Q#?R6CT#cWr@uM}qO5^i2zFOlw8egOF zMH(N}_*ohs()bdMze?jPHC{c+iqw$CM>YL=jjz@C292-N_+=VDPve(se7(l6(D?Zp zzf$8DX#8r8U!?JCH2!LhU#IccXnd2#U#s!kG`>OOw`=@$8ox{9uh;lyjgM)(squ?7 zexJrK(fETJzf|LmPbA>@zd_?OHGY}KXKQ?1mWZ8oylQJsN+D#usV)tr|Z|<8RaW5{){5Fk$P~*32{6iYQ zOXGj1@y#0lu*RDj|9g$!r}3*a{-DM`qVdM35^(&l*7!_~e^leMHU2S;@1yaLYrIS2 zpV0V08vkD!pR4hI(D>0B|3{6_*Z4IW@6q@_X?&5!KdJGvH2x`#FVXlvYkZ}~Kdtd0 zjbE$r^&0;djc?HSbsE1+Up0QE#y_j^t2O>PjbEej8#I2M#&6X4 zCXH{>_-z{hyvA?W_)QwWOXD|de6z;Cpz)^0zo_y1G=7W5AJq7*8gJ~8fa8Cg#%F5$ zOB$c8@h@w9AB}%S<6Rp6s>Tn}_}4T(SL0vT_|Y2wH;vEN`0X0+(fBtszDVPDX#6aV ze^cXsx&5UD{$FT;ubn3!^2EN#^duZFVSnXW(iBZ^+wO_IkSU{4+sKzexAlJ&Kb@!K zdc7ewdA5GjO@NnW|h}Q^wI`Of@D+TUH+>3a*z$Xy* zCTCM{EeZgZO;neLsl)C(b8s7I+i!IO6RBuO}W)+$8Xm#1n|u2>cjv0r5(K zA0nPeyj^2z)E?Wa5y(Hxhe@O9Z~2cnWcmzzc{kAkG&!Onf16uE2AM3yEC< zml9t@oGtL>#1|7A0#7Brgm~W}(f`Cn#LWVaC%%+;yTD_KrxG^_Je+tM@fv|oC!S8c zQs92XGl-W9d;;-I;s$|_CjJ$1NZ{_ovxrLsPA9&MxJckbHvwNxoGo`|#0|vF0&gO|j(EGk>xr)?ZW8!O;u!H7 zfgdAYOuSOyhlrODFBkZ3;-$n50^dq}193>;8;O?@mk4}4ah$kF;044t66XsXCdLQ# z7kCaa+6?s|()L-DK#8_id{{y1`iI)>M3p}3q7UJy!k0rj9xJlsQ z#J3Tz5%_fC+lf~S+>iJU;^hLLKzt{0gTO}<|CTr;aChPr#3cf!6W>K#B=8~Bz4C72 ze1Z29-$R@$@aM$$61xQci1f$t_>Mcg3pt;CNIhXlTncr|f}z}FK$N?auH z0^-Mr^92qQKTe!0@EqbNh+P7g68{%*w!oJY|AE*Lcq;K9iT8aY`k#0WakIeViT^~r zUEs0APZBo?Je>F`;xz)FPW)%$l>+x8ewui>z$Xx|C2kP-XyU&RhXn3UypFg;;B?|= zh>HY1gt<_;o;Y9N{ltGI&K3A`;%A9n0)IsO9C5b5?-6exHU!>5yped{*P{Q4n~0kQ z-bDO7@pggN6K^7J68K5t&BSX2evJ49;*|nFMEoN0a)IwA-a_0U@U6sKi9-V4NW6`> zMBwX*Um`9NcmeUt#Q6e;iC-bk1?Kb4urH^?-}tpB{*fp4$-biL6Nfbod&$$7w`VLK zbb39G(DhCE5a%fjQFtI~%!^~PdKw+qQ;azn?S)6B-$03vN=LLcJ5On#-ex7Cf0@u9 zIhA7Y&z|@`&+}iM?|J@U7f;$2&l^8SkA;CeWgxSyx!ie5L3@7s*O1pAd5zk$W_V(G zr$f{5#6OMp^d$1uBJQ>I>I2I3p)a;LenenNyK-~8znjb3@A%Q0}%8up?^2Dd-H zYk{0-+O6i9*rRWxyXFq;R>YHRW8P3~$2da_b2Mv?$J^L>Ml{QlxHi*VDAe$HE07x@ zzIES>Y0&A@!Kut)O&)kfBc8-bzrpuT;$bw)f z(S@_f;Te+~ok6P~NSBICKSEE6LJulDClsQvw%Z&Ao%klv0!wz(o=WN(Ggds7;tFZS`o z_l|4ZJ|*q_35kMFU8n_>*t1t$Id1kiw7Gb1gE4&^>VSrXFU5F}@Hh)LiTpnqiG6PD4+k=Rbr(-u)2f_n0Gc7$ou< zNmSBK?1gN)!9ixliJvge{_}w%8y@O`Vjn$5c<2x)_Lvbd-Z3t(&YWY+F|7JQS$39Z z>n2V(3^KExvk`s2?@L)PPGv*`Ybtm5T=&8*WaDhE{;I7_4CM?AWxgxJs7W~HVSB)V zoSCT4HgCkROyoU*9fWzUxcxnEqqsG9BR9u9NB(FtjgbK;0nZ)Nf5Udq#>`ngjdzY? zutkTK=yP)|Mj|HRJxDU=z>^y~eLt01X1|pkdokU!0*lv=|3zZ@3v~KECMJ5l3o5ptL^$VI3a;@=kvm@S zRyKz6=SZHo_BtZcLfNZ`d0Xg+>$n z74}CCr^bcp9^Vd6JPP}bBL@qOY|F;T-oi!~kMAXDyh@FpLPH$0Wdn{pgypI6!VJyE zKf%P-H6?9)K#ggRb{jq478=c#A3ZlwV{TWEZxcL4Yb5e^;5mSeIRYj8gc8j+Fi_A7 zIUe7aXslVlSehzJw=zr6*rx$MQyS za~?g~3olPX&X@N2j{x@q zN571^<9@wa#88J<*hkwHHvTz>or|ea1a3nC!I?)!j$Qr)}E`x&61F$iswlPlY%J!qhn=){j)Z4e?y zw>!}l%R&AXo6U>igQ!)#tW}39RSG9=z<{uvI9YR|zvToRGSvMb+@I!=`nkA2Rq>-S zU|JuQd_L~~vP|B4%ogr2P=O+>o#ygZ31+c2B{sDgqH0swUK=)PJ)`}#rWRmD%OWnk z)}c~p+1R19=)~BebmwA>N@K|m=Mq;J`tzN+n+qlOT-a=V!e9@dZepKeR(5Ag-V6s@ zPv_m!7!Iv2=6M`>ro>-ho;6}OlTkN>8a`I0L#sQNoQQI)O;~<%cyc87LPyZVNupR0 z^3i*IDuiQu%`I5*S|4R`vr)w<+|q3|z3_o0FKKcvp2YDC?jN6#dl7rpBkN9{(zqz& zoAumLA;kRdB24T*z$vP~4fQFppSn74*d_0})%9I3wi1maZ-ydWx?bt-kQxqXh-IL0 zWS!C|Q5p;_v<8iCvalTuY-H4(x85N&1}lw2s3v{B zz|!bzY4lVYyOhQtXpCz(H^*=;J_)z1#A|!luFqr4{Vs}TNaPmAn+wxE^o-f&Tzock zoNKc^*iRkEn3-SudCOStAvN%j`LrZ219@taTq(%{kVhxUgtWW33pPisgE{hA zFhp9eL(sB%fTitFZ)+vF+#w1bT4@~Fb^$sufEE&@^+165 z{kGYKEGN=l^AJM{s-4flg4_rtLKTljKgGZ95IzR+F%!?X=tTJxh=qJGMu(a4De`i! z?idS*z4|UsqP9pp3#*?(Akbk~ms`{}@(idYH@UDUo=9Nr?+?S;0PbuPv!NY(A(s!b zW7oHx!uI|E3+4A1LgvFCNuQsf&j>Sv+QZ>-aN=#~An#~YWs74g$6V_hW(gXqy$zNe zh@Q-LjrO_bNVMZ1^C-6CCE|7fW@T%Ee2759d?g0Vi=M>7LFNx%u;8tDsy1D?MgG&s zKf}%W`lC<~Q@X-b!`0XooTl0{MWu1%k?fh>s6;C!C(JHpi94`W{{arT%o!ijbnVfY zxR3pgd#z?zHfnov`@Ya+E=8?dzZSQxyfxL+UUrAh;yZ!F)G^db@s1nZ>eytRPe;#!(6Bxh!$^zldXR@!{Wuj zi>-fQgL4pE+pB&Co!IZpb{usv6c=OqwdSDt&v?i%%AI?gzII;T(2R(=-#N?i83M0x z`PZDJiG`WwiwGb4otLA%9fuw?@D}SRp)8)ZT0nHPFx&Ba%R*n4^K2~d3@91SWxvJG z_GfoI9bu~GJ2!mbTzko^0_TSARu7j5Vh3M&NOcnqs`F@zgqEViH$Dz{E6i& zV8Aoxz1lB4iQB||TvC{S1K|LhGA4P)ADMTLh?Lk8JR}Ouy9=a&cQbU#?8RB}4K-rd zmKl5(dA|7}_ZY%ufE?E8pUI|1s5>msvCo`?GjjOO5Dy&JWSTc~XvZhuK@wfQ=^p~P z1n`ToI{~ekcRKvQbB%NH^B67W6JiLyn2dDr(ZlBLbsoKL9C^z`5_`>p53qF;U8mlW z#qpY@|Adp-iyuMnDy7%A6Fs5eQwl%8tyDuoVX9Ji&rYF0DLf-8c7`Vr!S;Et`8lhW zILF+9xhm@V==+kmg@oKK4ru1>^Jp8y_g;r4sHX}9>#7H=QB;b?i{*6#Hk?ZaplvYn zUcaBQZ96D%<0ZFVaN_CHx%hm^trA>50Q4@A+^yigc4mut<{aYq>>Q-s$bUB6xkTh@ z?DYuVQ*WScE^ZHSNonl$)p&4trWRa6EHnDz;at*-*2T&pCW#Dwy*YnS)keo`M0Ezq z#k)le#01=g+7`w?S8M6}2u15O z4RR~K*PMRMvm5ITTZ|4%y9b7Z3AL6Btz?My zjEjHM$2j}HYpFUi@P90>;;c}ZrvQa8YAfM<{&OAeBjLc zkYg_Tk__K+5e9+A*@7wiHWJN!Xaa2NRuv}P&jDYAoy^^0=3)IdBPd!v%|a(AT|R(HCREJp5f!-oTiD0E7$kOB7>;jY7`A+f@s_v}?@M78 zw5Vj90c8UW%0c`EOL`u9Y@dAR9b53V-2t?1A9DvXwLF3pJQ&Ex2?6DQHCJ;;X5L?_I>MEd*fpMhB~{&EMgldf3Sa zi2wEvJ$x&=)_rD>fi6=2}O(gLM4q*JAL`A)8J zbW2Z{@GT1bZ?5&jU?o`RsO%!*eQmXA# zX_-SPr7NYPPL+zk7sY*ku570>I#s$7VFo_ErhH4W#h;{ zDWzvSRl4SA;rK&J>9VJLD&`w zVPPGP+oHlo$9XUs|0f3dPs7+0c_+hDxgHdvXZS)kG4gYS27EKY&3?mq*rdncC~~wR zE82h2Xb8N({o)Na?OyX6G_fb{c=}e%#?_eceCLbre*k0amcs-BKA!{3z6f2Ip4>z~ zC{`sD<(Mbb#I6kicPj!eY<^~zVEsiI1@WKFTHYoc7gMpmB)L#g>vVX4q_gy*`m(4w zh?w$)*;Z5vD%nu+B-Z32ks04WqyT&97QwDm>_W^=u%CcUtXZze-iWe?hmqGAxEe>E zfEJ;>VR)N&xKcm9W@#MRRVnp=lKBYBh;PNW#?uQMdpeMG5l-!vf&Xu>SM<==7k`nf%WJO_SCH?6fNI!9rCSb zk0A30K?w9-SJbcN6(m?Y&9B8*`rnz2SSDfzGwOPa9I=CrT5MT%n_FNDJEPvrdn)5( zq6qUw{8|S#ltQ<4Z*nKdc+b6KEYCd`cT&S3Q9@gOy| z_nMX8V!1+Q=A(fju|pa2UdLA|^IrG(p6B*+Tq2s0iK81|!qz#V%tdR#k2@#2#8~u1 zE8D)~k%*pKpyu&)WzN`$w6sKTeoxB7A8F5vLoVilZ_p@wcOhS+IPPGEM18*b#p~>i z=M#>{!Qf54XNByG@BSI@wF1;L?3wjg3oP&Pf)2K>*sR_dy7*Pzn;l*o;(QZNUvTe- z{*C>FFN!iTZs$$oT6q^N@t6%(Nyy!%2U-Y)^_a=wzLe(hOpLuZ^EOV$K8{~=3jgs|cZj4jxg?o5ni*m9N zLe|g;v03@_RcyH$b;tgQ*-FQ@PN;$w)_cCTV!&CCqLiDDVOJGf7&4+gwobsX$6jT! z*sCne_iUYzPeN=7hJ!Re7W+pgYqh^X6#}ze+RaXbSx@6MkEbz$Z~nNyLf-g<%9a*Z zEis)(KSnMZ&KmEQbE*P%T0}9=q9WSXCL54Hie z;l|yV?UAFfoZ*ne)Ym*?-f+e~X8MNdm3|lh@nFwJ{3GWs{^cZ397pWxGd&yBA6asF zHsT-7t_8bce=wDRAUL>uC@5Fp;&(w-9y+s)B4uR zgJ>cxIFODzSU3@PthMMWP+{YYcij`nPDMJv5g7dmvKYo0ga!e2o_ zT0YV|o(<&aPQK{Hply&q79__GYi=i;A75>rg{!`A#_ZHoEY zYwu(2UCza4aoaB2=4@H}p2^yWPDh#=g=eGoFNxZV^uudEFc}OYE^k!@VgN@|H77rc zwQ7&Qvx1=)_IoyB4C3#2`D&B3e+GX6L3NpDXws!*cb!d#^PP)Vafk z-0pCD^b+YCrUk1p1Hxg?#$ebL^!aMhez_Q`h+Y^gF8ui;IE;TFo3X9qv=M(J&T97m zR`k-oXS*Sni;X&4vvM25|1q9Kj~w}0crqN83&5u@G7@3$itpTxM_iIeTd^A6i-aj$ z;>1EI-ZOt?&U=O0d(FMf)#0l)=aSda9M%~N@tqqVIySE_kwMZu&e=OQzqEO#c`g4>^N=MkHI0wV(O#OQvs@=@?^8Ovei% zrhl4D|D8-nm%fHo(F)=}CDUU_Z+Q|)&y_g|t-vsn;>t8zQy#HifT83gFe#RvS)Jr)X z9D4FKIBc?O4sYM~0cF3$w5&khXXHHSbw@|rGim!qKG^&_+yAT9jh4-r6S7?v+-sA9)&_ z$WZYeG#&CxZhlI09*8nxGLj~3x%^_J6bJJ`L}tXBhQ?@S7 zfh*$T3znhyZeF-79!1qxo6fb9)6N|l_2IXLipBRW=3Q({tj&C{&4+7GJ8)huj)93|NRzsyQQG$@M+q`&#^=>5o6-pF^nNwa+-{^T7=i-cT-h+eiXq$uV ze9ervAg_bFm0UJW!a5##`|*v-J~{%>mF8O?k*A^VITe~|mcbuhM) zDj&QI8lIbnHVLcpQCA$On#J@DNSAMG8qU8adXD*~RNK&$h8m+PPi^G0cm0{k_nS|G z5bwx$n-$OTR{RbSUe}mhwc&Rw+J)z}rogHuF#BF)s-vZevDcd@D-}5TDr$ zDDY?)mG3gNw(GfB@|NDj_wlu zEu9;J4Tp5@IXI+!t#I8J>_~iDI3@m#_T;@dX9Z{;tQvwsCo(3&hu-KJ_I-?5_?qN* zBJW=FA`~dkqjq&J9RhExGl{1PclVlopBFio>{A(L10w@AddXKTe7C_$JSXH9z8$T@ zxI=kMzJ^8hE@KPaXVTyQ3T>PTr<=($an2UKjiVUS9gEfcOuCOB2X1es8sOgMlsNdHck2@{hE{}2h$apK=D!FgBh-0g8tVq12{ zXkc6N&>c_J&btafGo3dqgX_3+IB$4>ck!=q!0sC7;->&`+MMq{iq^|g`C3Q}Q<6uD z9@vT@;arSw&7jNgV~Y?;WVg>nCeh$+W)n(oxe+>6C*6LkbZOp3>CzQ&NqkFZo#I`n z6lN-gu~hgKDs3GVzECNQR0_vaAs-5?>5W(o@LszmPhg%C*^Kcd-FoX8;ntKfUpW`= z{fS-so#~=~9|7>C5}qK;OE%EZZnOLYB(x#Hyc)IPq58uP0>;ko8|^LLJz-EBD}I-; z*2|Z`I9C;aGj{E0qxM5j;#Y$_=k%Jt5bson2{(WuK($+v&m$k>Rf#8_k)sayzk=0D z{7z=;?~;MxF1+oM+|A~fs0Q4@`#eCJ9l4ZE_ z#GcQLeaTiRqOJ82o#gjl(&&ZKaKZh7<8%N#-A-_~S7Hy9cbU4;^0v?d1(wcSP zcoftzU;!+0gk>R71}*$@Bc{07S4191e<)b|8Jzlwy#4c${1c`-@@Hwuy=3y6O#VP7 ze}{W3w;_}N%;b$Sxf%DO^`%?i!!0v|VE}7KqvHZDhH-}vN+|W*S;$v^NR*n3dr(tI z`6ZPnsR5+8?Lx*AaK8?d0;;`EhLPC%+NO3S(3-y$oILaLFH9E-+Z zFwqd#;MvHssSu5M)ez<^WMgN%pTVSFQ$*6pFF6$xj=2*l)S-tg`nuI|8Z>ydyp0?U z3(}0*<2;E=F?@F5+|rk6g^k|K)*j|1Tv%h9(x${;^~8IfH3Kh|9mDVwqX~g2BPR{2 zQyL3%GN}s=fLr_~G>)^xJZZbynzuS0L^ig} zR>uR%KY5Bfj;+}5_$aSjhiiBiYlj_uM?07F|H z{Q#vqE-T>-t%Ujf>RoT@PdIBD7qF?@M}{fqo8zTA*iDE6A4?Xv+OqqmNVp|g;LTRT zdXaE&n(z<}a{zb3#Ro;gZb0ecKdgjhBB6J(jAJcRL6PuNGRF^A4xUEY>Uax~*H*_4 zDEqGTBtTc+L>31=tE;IA@@ z3N~@T*^TP?j3LR+&qFCKLxrKA)uhR}=ACq$wLoS;XRG7ssPv`feWY)7+<2+g^2=~1 zeBLY)t^$-k2d#w1kYIUm*Z}m=133UiR5EiAcyOxQ@jj}&)$yKH@eCL-M?o1QwdF^w z=kPJ}B+JJ+8wGJ}lDLdSB5xzIn>XWYxRz%bb`y@B`3y>#>yL-~GQ2hBorYZINeFRU z9nJ{;83kNh9ck*WB?DzW!^$GSg)V|<9Qj2bqP#gUF5

EMlwUQYI(z!pO>LHp!fi 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 literal 0 HcmV?d00001 diff --git a/dwm/surf/surf-open.sh b/dwm/surf/surf-open.sh new file mode 100755 index 0000000..c22edc2 --- /dev/null +++ b/dwm/surf/surf-open.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# +# See the LICENSE file for copyright and license details. +# + +xidfile="$HOME/tmp/tabbed-surf.xid" +uri="" + +if [ "$#" -gt 0 ]; +then + uri="$1" +fi + +runtabbed() { + tabbed -dn tabbed-surf -r 2 surf -e '' "$uri" >"$xidfile" \ + 2>/dev/null & +} + +if [ ! -r "$xidfile" ]; +then + runtabbed +else + xid=$(cat "$xidfile") + xprop -id "$xid" >/dev/null 2>&1 + if [ $? -gt 0 ]; + then + runtabbed + else + surf -e "$xid" "$uri" >/dev/null 2>&1 & + fi +fi + diff --git a/dwm/surf/surf.1 b/dwm/surf/surf.1 new file mode 100644 index 0000000..23117b1 --- /dev/null +++ b/dwm/surf/surf.1 @@ -0,0 +1,305 @@ +.TH SURF 1 surf\-VERSION +.SH NAME +surf \- simple webkit-based browser +.SH SYNOPSIS +.B surf +.RB [-bBdDfFgGiIkKmMnNpPsStTvwxX] +.RB [-a\ cookiepolicies] +.RB [-c\ cookiefile] +.RB [-C\ stylefile] +.RB [-e\ xid] +.RB [-r\ scriptfile] +.RB [-u\ useragent] +.RB [-z\ zoomlevel] +.RB [URI] +.SH DESCRIPTION +surf is a simple Web browser based on WebKit/GTK+. It is able +to display websites and follow links. It supports the XEmbed protocol +which makes it possible to embed it in another application. Furthermore, +one can point surf to another URI by setting its XProperties. +.SH OPTIONS +.TP +.B \-a cookiepolicies +Define the order of +.I cookie policies\fR. +The default is "@Aa" but could be +redefined in the +.IR config.h , +with "A" meaning to +accept all cookies, "a" to deny all cookies and "@", which tells surf to +accept no third party cookies. +.TP +.B \-b +Disable Scrollbars. +.TP +.B \-B +Enable Scrollbars. +.TP +.B \-c cookiefile +Specify the +.I cookiefile +to use. +.TP +.B \-C stylefile +Specify the user +.IR stylefile . +This does disable the site-specific styles. +.TP +.B \-d +Disable the disk cache. +.TP +.B \-D +Enable the disk cache. +.TP +.B \-e xid +Reparents to window specified by +.IR xid . +.TP +.B \-f +Start surf in windowed mode (not fullscreen). +.TP +.B \-F +Start surf in fullscreen mode. +.TP +.B \-g +Disable giving the geolocation to websites. +.TP +.B \-G +Enable giving the geolocation to websites. +.TP +.B \-i +Disable Images. +.TP +.B \-I +Enable Images. +.TP +.B \-k +Disable kiosk mode (disable key strokes and right click). +.TP +.B \-K +Enable kiosk mode (disable key strokes and right click). +.TP +.B \-m +Disable application of user style sheets. +.TP +.B \-M +Enable application of user style sheets. +.TP +.B \-n +Disable the Web Inspector (Developer Tools). +.TP +.B \-N +Enable the Web Inspector (Developer Tools). +.TP +.B \-r scriptfile +Specify the user +.IR scriptfile . +.TP +.B \-s +Disable Javascript. +.TP +.B \-S +Enable Javascript. +.TP +.B \-t +Disable strict TLS check. +.TP +.B \-T +Enable strict TLS check. +.TP +.B \-u useragent +Specify the +.I useragent +which surf should use. +.TP +.B \-v +Prints version information to standard output, then exits. +.TP +.B \-w +Prints xid to standard output. This can be used to script the browser in for +example +.BR xdotool(1) . +.TP +.B -x +Disable custom certificates. +.TP +.B -X +Enable custom certificates. +.TP +.B \-z zoomlevel +Specify the +.I zoomlevel +which surf should use. +.SH USAGE +.B Escape +Stops loading current page or stops download. +.TP +.B Ctrl\-h +Walks back the history. +.TP +.B Ctrl\-l +Walks forward the history. +.TP +.B Ctrl\-k +Scrolls page upwards. +.TP +.B Ctrl\-j +Scrolls page downwards. +.TP +.B Ctrl\-b +Scroll up one whole page view. +.TP +.B Ctrl\-Space +Scroll down one whole page view. +.TP +.B Ctrl\-i +Scroll horizontally to the right. +.TP +.B Ctrl\-u +Scroll horizontally to the left. +.TP +.B Ctrl\-Shift\-k or Ctrl\-+ +Zooms page in. +.TP +.B Ctrl\-Shift\-j or Ctrl\-- +Zooms page out. +.TP +.B Ctrl\-Shift\-q +Resets Zoom. +.TP +.B Ctrl\-f and Ctrl\-/ +Opens the search-bar. +.TP +.B Ctrl\-n +Go to next search result. +.TP +.B Ctrl\-Shift\-n +Go to previous search result. +.TP +.B Ctrl\-g +Opens the URL-bar (requires dmenu installed). +.TP +.B Ctrl\-p +Loads URI from primary selection. +.TP +.B Ctrl\-Shift\-p +Calls Printpage Dialog. +.TP +.B Ctrl\-r +Reloads the website. +.TP +.B Ctrl\-Shift\-r +Reloads the website without using the cache. +.TP +.B Ctrl\-y +Copies current URI to primary selection. +.TP +.B Ctrl\-t +Display the current TLS certificate in a popup window. +.TP +.B Ctrl\-Shift\-a +Toggle through the the +.I cookie policies\fR. +This will not reload the page. +.TP +.B Ctrl\-Shift\-b +Toggle scrollbars. This will reload the page. +.TP +.B Ctrl\-Shift\-c +Toggle caret browsing. This will reload the page. +.TP +.B Ctrl\-Shift\-d +Toggle dark mode. +.TP +.B Ctrl\-Shift\-i +Toggle auto-loading of images. This will reload the page. +.TP +.B Ctrl\-Shift\-m +Toggle if the +.I stylefile +file should be loaded. This will reload the page. +.TP +.B Ctrl\-Shift\-o +Open the Web Inspector (Developer Tools) window for the current page. +.TP +.B Ctrl\-Shift\-s +Toggle script execution. This will reload the page. +.TP +.B Ctrl\-Shift\-t +Toggle strict TLS check. This will reload the page. +.TP +.B F11 +Toggle fullscreen mode. +.SH INDICATORS OF OPERATION +Surf is showing indicators of operation in front of the site title. +For all indicators, unless otherwise specified, a lower case letter means disabled and an upper case letter means enabled. +.TP +.B A +all cookies accepted +.TP +.B a +no cookies accepted +.TP +.B @ +all except third-party cookies accepted +.TP +.B c C +caret browsing +.TP +.B g G +geolocation +.TP +.B d D +disk cache +.TP +.B i I +images +.TP +.B s S +scripts +.TP +.B m M +styles +.TP +.B x X +custom certificates +.TP +.B t T +strict TLS +.SH INDICATORS OF WEB PAGE +The second part of the indicators specifies modes of the web page itself. +.SS First character: encryption +.TP +.B - +unencrypted +.TP +.B T +encrypted (TLS) +.TP +.B U +attempted encryption but failed +.SS Second character: proxying +.TP +.B - +no proxy +.TP +.B P +using proxy +.SH ENVIRONMENT +.B SURF_USERAGENT +If this variable is set upon startup, surf will use it as the +.I useragent +string. +.TP +.B http_proxy +If this variable is set and not empty upon startup, surf will use it as the http proxy. +.SH SIGNALS +Surf will reload the current page on +.BR SIGHUP . +.SH SEE ALSO +.BR dmenu(1), +.BR xprop(1), +.BR tabbed(1), +.BR xdotool(1) +.SH BUGS +Please report them! diff --git a/dwm/surf/surf.c b/dwm/surf/surf.c new file mode 100644 index 0000000..18d65d7 --- /dev/null +++ b/dwm/surf/surf.c @@ -0,0 +1,2131 @@ +/* See LICENSE file for copyright and license details. + * + * To understand surf, start reading main(). + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "arg.h" +#include "common.h" + +#define LENGTH(x) (sizeof(x) / sizeof(x[0])) +#define CLEANMASK(mask) (mask & (MODKEY|GDK_SHIFT_MASK)) + +enum { AtomFind, AtomGo, AtomUri, AtomUTF8, AtomLast }; + +enum { + OnDoc = WEBKIT_HIT_TEST_RESULT_CONTEXT_DOCUMENT, + OnLink = WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK, + OnImg = WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE, + OnMedia = WEBKIT_HIT_TEST_RESULT_CONTEXT_MEDIA, + OnEdit = WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE, + OnBar = WEBKIT_HIT_TEST_RESULT_CONTEXT_SCROLLBAR, + OnSel = WEBKIT_HIT_TEST_RESULT_CONTEXT_SELECTION, + OnAny = OnDoc | OnLink | OnImg | OnMedia | OnEdit | OnBar | OnSel, +}; + +typedef enum { + AccessMicrophone, + AccessWebcam, + CaretBrowsing, + Certificate, + CookiePolicies, + DarkMode, + DiskCache, + DefaultCharset, + DNSPrefetch, + Ephemeral, + FileURLsCrossAccess, + FontSize, + Geolocation, + HideBackground, + Inspector, + JavaScript, + KioskMode, + LoadImages, + MediaManualPlay, + PreferredLanguages, + RunInFullscreen, + ScrollBars, + ShowIndicators, + SiteQuirks, + SmoothScrolling, + SpellChecking, + SpellLanguages, + StrictTLS, + Style, + WebGL, + ZoomLevel, + ParameterLast +} ParamName; + +typedef union { + int i; + float f; + const void *v; +} Arg; + +typedef struct { + Arg val; + int prio; +} Parameter; + +typedef struct Client { + GtkWidget *win; + WebKitWebView *view; + WebKitSettings *settings; + WebKitWebContext *context; + WebKitWebInspector *inspector; + WebKitFindController *finder; + WebKitHitTestResult *mousepos; + GTlsCertificate *cert, *failedcert; + GTlsCertificateFlags tlserr; + Window xid; + guint64 pageid; + int progress, fullscreen, https, insecure, errorpage; + const char *title, *overtitle, *targeturi; + const char *needle; + struct Client *next; +} Client; + +typedef struct { + guint mod; + guint keyval; + void (*func)(Client *c, const Arg *a); + const Arg arg; +} Key; + +typedef struct { + unsigned int target; + unsigned int mask; + guint button; + void (*func)(Client *c, const Arg *a, WebKitHitTestResult *h); + const Arg arg; + unsigned int stopevent; +} Button; + +typedef struct { + const char *uri; + Parameter config[ParameterLast]; + regex_t re; +} UriParameters; + +typedef struct { + char *regex; + char *file; + regex_t re; +} SiteSpecific; + +/* Surf */ +static void die(const char *errstr, ...); +static void usage(void); +static void setup(void); +static void sigchld(int unused); +static void sighup(int unused); +static char *buildfile(const char *path); +static char *buildpath(const char *path); +static char *untildepath(const char *path); +static const char *getuserhomedir(const char *user); +static const char *getcurrentuserhomedir(void); +static Client *newclient(Client *c); +static void loaduri(Client *c, const Arg *a); +static const char *geturi(Client *c); +static void setatom(Client *c, int a, const char *v); +static const char *getatom(Client *c, int a); +static void updatetitle(Client *c); +static void gettogglestats(Client *c); +static void getpagestats(Client *c); +static WebKitCookieAcceptPolicy cookiepolicy_get(void); +static char cookiepolicy_set(const WebKitCookieAcceptPolicy p); +static void seturiparameters(Client *c, const char *uri, ParamName *params); +static void setparameter(Client *c, int refresh, ParamName p, const Arg *a); +static const char *getcert(const char *uri); +static void setcert(Client *c, const char *file); +static const char *getstyle(const char *uri); +static void setstyle(Client *c, const char *file); +static void runscript(Client *c); +static void evalscript(Client *c, const char *jsstr, ...); +static void updatewinid(Client *c); +static void handleplumb(Client *c, const char *uri); +static void newwindow(Client *c, const Arg *a, int noembed); +static void spawn(Client *c, const Arg *a); +static void msgext(Client *c, char type, const Arg *a); +static void destroyclient(Client *c); +static void cleanup(void); + +/* GTK/WebKit */ +static WebKitWebView *newview(Client *c, WebKitWebView *rv); +static void initwebextensions(WebKitWebContext *wc, Client *c); +static GtkWidget *createview(WebKitWebView *v, WebKitNavigationAction *a, + Client *c); +static gboolean buttonreleased(GtkWidget *w, GdkEvent *e, Client *c); +static GdkFilterReturn processx(GdkXEvent *xevent, GdkEvent *event, + gpointer d); +static gboolean winevent(GtkWidget *w, GdkEvent *e, Client *c); +static void showview(WebKitWebView *v, Client *c); +static GtkWidget *createwindow(Client *c); +static gboolean loadfailedtls(WebKitWebView *v, gchar *uri, + GTlsCertificate *cert, + GTlsCertificateFlags err, Client *c); +static void loadchanged(WebKitWebView *v, WebKitLoadEvent e, Client *c); +static void progresschanged(WebKitWebView *v, GParamSpec *ps, Client *c); +static void titlechanged(WebKitWebView *view, GParamSpec *ps, Client *c); +static void mousetargetchanged(WebKitWebView *v, WebKitHitTestResult *h, + guint modifiers, Client *c); +static gboolean permissionrequested(WebKitWebView *v, + WebKitPermissionRequest *r, Client *c); +static gboolean decidepolicy(WebKitWebView *v, WebKitPolicyDecision *d, + WebKitPolicyDecisionType dt, Client *c); +static void decidenavigation(WebKitPolicyDecision *d, Client *c); +static void decidenewwindow(WebKitPolicyDecision *d, Client *c); +static void decideresource(WebKitPolicyDecision *d, Client *c); +static void insecurecontent(WebKitWebView *v, WebKitInsecureContentEvent e, + Client *c); +static void downloadstarted(WebKitWebContext *wc, WebKitDownload *d, + Client *c); +static void responsereceived(WebKitDownload *d, GParamSpec *ps, Client *c); +static void download(Client *c, WebKitURIResponse *r); +static gboolean viewusrmsgrcv(WebKitWebView *v, WebKitUserMessage *m, + gpointer u); +static void webprocessterminated(WebKitWebView *v, + WebKitWebProcessTerminationReason r, + Client *c); +static void closeview(WebKitWebView *v, Client *c); +static void destroywin(GtkWidget* w, Client *c); + +/* Hotkeys */ +static void pasteuri(GtkClipboard *clipboard, const char *text, gpointer d); +static void reload(Client *c, const Arg *a); +static void print(Client *c, const Arg *a); +static void showcert(Client *c, const Arg *a); +static void clipboard(Client *c, const Arg *a); +static void zoom(Client *c, const Arg *a); +static void scrollv(Client *c, const Arg *a); +static void scrollh(Client *c, const Arg *a); +static void navigate(Client *c, const Arg *a); +static void stop(Client *c, const Arg *a); +static void toggle(Client *c, const Arg *a); +static void togglefullscreen(Client *c, const Arg *a); +static void togglecookiepolicy(Client *c, const Arg *a); +static void toggleinspector(Client *c, const Arg *a); +static void find(Client *c, const Arg *a); + +/* Buttons */ +static void clicknavigate(Client *c, const Arg *a, WebKitHitTestResult *h); +static void clicknewwindow(Client *c, const Arg *a, WebKitHitTestResult *h); +static void clickexternplayer(Client *c, const Arg *a, WebKitHitTestResult *h); + +static char winid[64]; +static char togglestats[11]; +static char pagestats[2]; +static Atom atoms[AtomLast]; +static Window embed; +static int showxid; +static int cookiepolicy; +static Display *dpy; +static Client *clients; +static GdkDevice *gdkkb; +static char *stylefile; +static const char *useragent; +static Parameter *curconfig; +static int modparams[ParameterLast]; +static int spair[2]; +char *argv0; + +static ParamName loadtransient[] = { + Certificate, + CookiePolicies, + DiskCache, + DNSPrefetch, + FileURLsCrossAccess, + JavaScript, + LoadImages, + PreferredLanguages, + ShowIndicators, + StrictTLS, + ParameterLast +}; + +static ParamName loadcommitted[] = { +// AccessMicrophone, +// AccessWebcam, + CaretBrowsing, + DarkMode, + DefaultCharset, + FontSize, + Geolocation, + HideBackground, + Inspector, +// KioskMode, + MediaManualPlay, + RunInFullscreen, + ScrollBars, + SiteQuirks, + SmoothScrolling, + SpellChecking, + SpellLanguages, + Style, + ZoomLevel, + ParameterLast +}; + +static ParamName loadfinished[] = { + ParameterLast +}; + +/* configuration, allows nested code to access above variables */ +#include "config.h" + +void +die(const char *errstr, ...) +{ + va_list ap; + + va_start(ap, errstr); + vfprintf(stderr, errstr, ap); + va_end(ap); + exit(1); +} + +void +usage(void) +{ + die("usage: surf [-bBdDfFgGiIkKmMnNsStTvwxX]\n" + "[-a cookiepolicies ] [-c cookiefile] [-C stylefile] [-e xid]\n" + "[-r scriptfile] [-u useragent] [-z zoomlevel] [uri]\n"); +} + +void +setup(void) +{ + GIOChannel *gchanin; + GdkDisplay *gdpy; + int i, j; + + /* clean up any zombies immediately */ + sigchld(0); + if (signal(SIGHUP, sighup) == SIG_ERR) + die("Can't install SIGHUP handler"); + + if (!(dpy = XOpenDisplay(NULL))) + die("Can't open default display"); + + /* atoms */ + atoms[AtomFind] = XInternAtom(dpy, "_SURF_FIND", False); + atoms[AtomGo] = XInternAtom(dpy, "_SURF_GO", False); + atoms[AtomUri] = XInternAtom(dpy, "_SURF_URI", False); + atoms[AtomUTF8] = XInternAtom(dpy, "UTF8_STRING", False); + + gtk_init(NULL, NULL); + + gdpy = gdk_display_get_default(); + + curconfig = defconfig; + + /* dirs and files */ + cookiefile = buildfile(cookiefile); + scriptfile = buildfile(scriptfile); + certdir = buildpath(certdir); + if (curconfig[Ephemeral].val.i) + cachedir = NULL; + else + cachedir = buildpath(cachedir); + + gdkkb = gdk_seat_get_keyboard(gdk_display_get_default_seat(gdpy)); + + if (socketpair(AF_UNIX, SOCK_DGRAM, 0, spair) < 0) { + fputs("Unable to create sockets\n", stderr); + spair[0] = spair[1] = -1; + } else { + gchanin = g_io_channel_unix_new(spair[0]); + g_io_channel_set_encoding(gchanin, NULL, NULL); + g_io_channel_set_flags(gchanin, g_io_channel_get_flags(gchanin) + | G_IO_FLAG_NONBLOCK, NULL); + g_io_channel_set_close_on_unref(gchanin, TRUE); + } + + + for (i = 0; i < LENGTH(certs); ++i) { + if (!regcomp(&(certs[i].re), certs[i].regex, REG_EXTENDED)) { + certs[i].file = g_strconcat(certdir, "/", certs[i].file, + NULL); + } else { + fprintf(stderr, "Could not compile regex: %s\n", + certs[i].regex); + certs[i].regex = NULL; + } + } + + if (!stylefile) { + styledir = buildpath(styledir); + for (i = 0; i < LENGTH(styles); ++i) { + if (!regcomp(&(styles[i].re), styles[i].regex, + REG_EXTENDED)) { + styles[i].file = g_strconcat(styledir, "/", + styles[i].file, NULL); + } else { + fprintf(stderr, "Could not compile regex: %s\n", + styles[i].regex); + styles[i].regex = NULL; + } + } + g_free(styledir); + } else { + stylefile = buildfile(stylefile); + } + + for (i = 0; i < LENGTH(uriparams); ++i) { + if (regcomp(&(uriparams[i].re), uriparams[i].uri, + REG_EXTENDED)) { + fprintf(stderr, "Could not compile regex: %s\n", + uriparams[i].uri); + uriparams[i].uri = NULL; + continue; + } + + /* copy default parameters with higher priority */ + for (j = 0; j < ParameterLast; ++j) { + if (defconfig[j].prio >= uriparams[i].config[j].prio) + uriparams[i].config[j] = defconfig[j]; + } + } +} + +void +sigchld(int unused) +{ + if (signal(SIGCHLD, sigchld) == SIG_ERR) + die("Can't install SIGCHLD handler"); + while (waitpid(-1, NULL, WNOHANG) > 0) + ; +} + +void +sighup(int unused) +{ + Arg a = { .i = 0 }; + Client *c; + + for (c = clients; c; c = c->next) + reload(c, &a); +} + +char * +buildfile(const char *path) +{ + char *dname, *bname, *bpath, *fpath; + FILE *f; + + dname = g_path_get_dirname(path); + bname = g_path_get_basename(path); + + bpath = buildpath(dname); + g_free(dname); + + fpath = g_build_filename(bpath, bname, NULL); + g_free(bpath); + g_free(bname); + + if (!(f = fopen(fpath, "a"))) + die("Could not open file: %s\n", fpath); + + g_chmod(fpath, 0600); /* always */ + fclose(f); + + return fpath; +} + +static const char* +getuserhomedir(const char *user) +{ + struct passwd *pw = getpwnam(user); + + if (!pw) + die("Can't get user %s login information.\n", user); + + return pw->pw_dir; +} + +static const char* +getcurrentuserhomedir(void) +{ + const char *homedir; + const char *user; + struct passwd *pw; + + homedir = getenv("HOME"); + if (homedir) + return homedir; + + user = getenv("USER"); + if (user) + return getuserhomedir(user); + + pw = getpwuid(getuid()); + if (!pw) + die("Can't get current user home directory\n"); + + return pw->pw_dir; +} + +char * +buildpath(const char *path) +{ + char *apath, *fpath; + + if (path[0] == '~') + apath = untildepath(path); + else + apath = g_strdup(path); + + /* creating directory */ + if (g_mkdir_with_parents(apath, 0700) < 0) + die("Could not access directory: %s\n", apath); + + fpath = realpath(apath, NULL); + g_free(apath); + + return fpath; +} + +char * +untildepath(const char *path) +{ + char *apath, *name, *p; + const char *homedir; + + if (path[1] == '/' || path[1] == '\0') { + p = (char *)&path[1]; + homedir = getcurrentuserhomedir(); + } else { + if ((p = strchr(path, '/'))) + name = g_strndup(&path[1], p - (path + 1)); + else + name = g_strdup(&path[1]); + + homedir = getuserhomedir(name); + g_free(name); + } + apath = g_build_filename(homedir, p, NULL); + return apath; +} + +Client * +newclient(Client *rc) +{ + Client *c; + + if (!(c = calloc(1, sizeof(Client)))) + die("Cannot malloc!\n"); + + c->next = clients; + clients = c; + + c->progress = 100; + c->view = newview(c, rc ? rc->view : NULL); + + return c; +} + +void +loaduri(Client *c, const Arg *a) +{ + struct stat st; + char *url, *path, *apath; + const char *uri = a->v; + + if (g_strcmp0(uri, "") == 0) + return; + + if (g_str_has_prefix(uri, "http://") || + g_str_has_prefix(uri, "https://") || + g_str_has_prefix(uri, "file://") || + g_str_has_prefix(uri, "webkit://") || + g_str_has_prefix(uri, "about:")) { + url = g_strdup(uri); + } else { + if (uri[0] == '~') + apath = untildepath(uri); + else + apath = (char *)uri; + if (!stat(apath, &st) && (path = realpath(apath, NULL))) { + url = g_strdup_printf("file://%s", path); + free(path); + } else { + url = g_strdup_printf("http://%s", uri); + } + if (apath != uri) + free(apath); + } + + setatom(c, AtomUri, url); + + if (strcmp(url, geturi(c)) == 0) { + reload(c, a); + } else { + webkit_web_view_load_uri(c->view, url); + updatetitle(c); + } + + g_free(url); +} + +const char * +geturi(Client *c) +{ + const char *uri; + + if (!(uri = webkit_web_view_get_uri(c->view))) + uri = "about:blank"; + return uri; +} + +void +setatom(Client *c, int a, const char *v) +{ + XChangeProperty(dpy, c->xid, + atoms[a], atoms[AtomUTF8], 8, PropModeReplace, + (unsigned char *)v, strlen(v) + 1); + XSync(dpy, False); +} + +const char * +getatom(Client *c, int a) +{ + static char buf[BUFSIZ]; + Atom adummy; + int idummy; + unsigned long ldummy; + unsigned char *p = NULL; + + XSync(dpy, False); + XGetWindowProperty(dpy, c->xid, + atoms[a], 0L, BUFSIZ, False, atoms[AtomUTF8], + &adummy, &idummy, &ldummy, &ldummy, &p); + if (p) + strncpy(buf, (char *)p, LENGTH(buf) - 1); + else + buf[0] = '\0'; + XFree(p); + + return buf; +} + +void +updatetitle(Client *c) +{ + char *title; + const char *name = c->overtitle ? c->overtitle : + c->title ? c->title : ""; + + if (curconfig[ShowIndicators].val.i) { + gettogglestats(c); + getpagestats(c); + + if (c->progress != 100) + title = g_strdup_printf("[%i%%] %s:%s | %s", + c->progress, togglestats, pagestats, name); + else + title = g_strdup_printf("%s:%s | %s", + togglestats, pagestats, name); + + gtk_window_set_title(GTK_WINDOW(c->win), title); + g_free(title); + } else { + gtk_window_set_title(GTK_WINDOW(c->win), name); + } +} + +void +gettogglestats(Client *c) +{ + togglestats[0] = cookiepolicy_set(cookiepolicy_get()); + togglestats[1] = curconfig[CaretBrowsing].val.i ? 'C' : 'c'; + togglestats[2] = curconfig[Geolocation].val.i ? 'G' : 'g'; + togglestats[3] = curconfig[DiskCache].val.i ? 'D' : 'd'; + togglestats[4] = curconfig[LoadImages].val.i ? 'I' : 'i'; + togglestats[5] = curconfig[JavaScript].val.i ? 'S' : 's'; + togglestats[6] = curconfig[Style].val.i ? 'M' : 'm'; + togglestats[8] = curconfig[Certificate].val.i ? 'X' : 'x'; + togglestats[9] = curconfig[StrictTLS].val.i ? 'T' : 't'; +} + +void +getpagestats(Client *c) +{ + if (c->https) + pagestats[0] = (c->tlserr || c->insecure) ? 'U' : 'T'; + else + pagestats[0] = '-'; + pagestats[1] = '\0'; +} + +WebKitCookieAcceptPolicy +cookiepolicy_get(void) +{ + switch (((char *)curconfig[CookiePolicies].val.v)[cookiepolicy]) { + case 'a': + return WEBKIT_COOKIE_POLICY_ACCEPT_NEVER; + case '@': + return WEBKIT_COOKIE_POLICY_ACCEPT_NO_THIRD_PARTY; + default: /* fallthrough */ + case 'A': + return WEBKIT_COOKIE_POLICY_ACCEPT_ALWAYS; + } +} + +char +cookiepolicy_set(const WebKitCookieAcceptPolicy p) +{ + switch (p) { + case WEBKIT_COOKIE_POLICY_ACCEPT_NEVER: + return 'a'; + case WEBKIT_COOKIE_POLICY_ACCEPT_NO_THIRD_PARTY: + return '@'; + default: /* fallthrough */ + case WEBKIT_COOKIE_POLICY_ACCEPT_ALWAYS: + return 'A'; + } +} + +void +seturiparameters(Client *c, const char *uri, ParamName *params) +{ + Parameter *config, *uriconfig = NULL; + int i, p; + + for (i = 0; i < LENGTH(uriparams); ++i) { + if (uriparams[i].uri && + !regexec(&(uriparams[i].re), uri, 0, NULL, 0)) { + uriconfig = uriparams[i].config; + break; + } + } + + curconfig = uriconfig ? uriconfig : defconfig; + + for (i = 0; (p = params[i]) != ParameterLast; ++i) { + switch(p) { + default: /* FALLTHROUGH */ + if (!(defconfig[p].prio < curconfig[p].prio || + defconfig[p].prio < modparams[p])) + continue; + case Certificate: + case CookiePolicies: + case Style: + setparameter(c, 0, p, &curconfig[p].val); + } + } +} + +void +setparameter(Client *c, int refresh, ParamName p, const Arg *a) +{ + GdkRGBA bgcolor = { 0 }; + + modparams[p] = curconfig[p].prio; + + switch (p) { + case AccessMicrophone: + return; /* do nothing */ + case AccessWebcam: + return; /* do nothing */ + case CaretBrowsing: + webkit_settings_set_enable_caret_browsing(c->settings, a->i); + refresh = 0; + break; + case Certificate: + if (a->i) + setcert(c, geturi(c)); + return; /* do not update */ + case CookiePolicies: + webkit_cookie_manager_set_accept_policy( + webkit_web_context_get_cookie_manager(c->context), + cookiepolicy_get()); + refresh = 0; + break; + case DarkMode: + g_object_set(gtk_settings_get_default(), + "gtk-application-prefer-dark-theme", a->i, NULL); + return; + case DiskCache: + webkit_web_context_set_cache_model(c->context, a->i ? + WEBKIT_CACHE_MODEL_WEB_BROWSER : + WEBKIT_CACHE_MODEL_DOCUMENT_VIEWER); + return; /* do not update */ + case DefaultCharset: + webkit_settings_set_default_charset(c->settings, a->v); + return; /* do not update */ + case DNSPrefetch: + webkit_settings_set_enable_dns_prefetching(c->settings, a->i); + return; /* do not update */ + case FileURLsCrossAccess: + webkit_settings_set_allow_file_access_from_file_urls( + c->settings, a->i); + webkit_settings_set_allow_universal_access_from_file_urls( + c->settings, a->i); + return; /* do not update */ + case FontSize: + webkit_settings_set_default_font_size(c->settings, a->i); + return; /* do not update */ + case Geolocation: + refresh = 0; + break; + case HideBackground: + if (a->i) + webkit_web_view_set_background_color(c->view, &bgcolor); + return; /* do not update */ + case Inspector: + webkit_settings_set_enable_developer_extras(c->settings, a->i); + return; /* do not update */ + case JavaScript: + webkit_settings_set_enable_javascript(c->settings, a->i); + break; + case KioskMode: + return; /* do nothing */ + case LoadImages: + webkit_settings_set_auto_load_images(c->settings, a->i); + break; + case MediaManualPlay: + webkit_settings_set_media_playback_requires_user_gesture( + c->settings, a->i); + break; + case PreferredLanguages: + return; /* do nothing */ + case RunInFullscreen: + return; /* do nothing */ + case ScrollBars: + /* Disabled until we write some WebKitWebExtension for + * manipulating the DOM directly. + enablescrollbars = !enablescrollbars; + evalscript(c, "document.documentElement.style.overflow = '%s'", + enablescrollbars ? "auto" : "hidden"); + */ + return; /* do not update */ + case ShowIndicators: + break; + case SmoothScrolling: + webkit_settings_set_enable_smooth_scrolling(c->settings, a->i); + return; /* do not update */ + case SiteQuirks: + webkit_settings_set_enable_site_specific_quirks( + c->settings, a->i); + break; + case SpellChecking: + webkit_web_context_set_spell_checking_enabled( + c->context, a->i); + return; /* do not update */ + case SpellLanguages: + return; /* do nothing */ + case StrictTLS: + webkit_website_data_manager_set_tls_errors_policy( + webkit_web_view_get_website_data_manager(c->view), a->i ? + WEBKIT_TLS_ERRORS_POLICY_FAIL : + WEBKIT_TLS_ERRORS_POLICY_IGNORE); + break; + case Style: + webkit_user_content_manager_remove_all_style_sheets( + webkit_web_view_get_user_content_manager(c->view)); + if (a->i) + setstyle(c, getstyle(geturi(c))); + refresh = 0; + break; + case WebGL: + webkit_settings_set_enable_webgl(c->settings, a->i); + break; + case ZoomLevel: + webkit_web_view_set_zoom_level(c->view, a->f); + return; /* do not update */ + default: + return; /* do nothing */ + } + + updatetitle(c); + if (refresh) + reload(c, a); +} + +const char * +getcert(const char *uri) +{ + int i; + + for (i = 0; i < LENGTH(certs); ++i) { + if (certs[i].regex && + !regexec(&(certs[i].re), uri, 0, NULL, 0)) + return certs[i].file; + } + + return NULL; +} + +void +setcert(Client *c, const char *uri) +{ + const char *file = getcert(uri); + char *host; + GTlsCertificate *cert; + + if (!file) + return; + + if (!(cert = g_tls_certificate_new_from_file(file, NULL))) { + fprintf(stderr, "Could not read certificate file: %s\n", file); + return; + } + + if ((uri = strstr(uri, "https://"))) { + uri += sizeof("https://") - 1; + host = g_strndup(uri, strchr(uri, '/') - uri); + webkit_web_context_allow_tls_certificate_for_host(c->context, + cert, host); + g_free(host); + } + + g_object_unref(cert); + +} + +const char * +getstyle(const char *uri) +{ + int i; + + if (stylefile) + return stylefile; + + for (i = 0; i < LENGTH(styles); ++i) { + if (styles[i].regex && + !regexec(&(styles[i].re), uri, 0, NULL, 0)) + return styles[i].file; + } + + return ""; +} + +void +setstyle(Client *c, const char *file) +{ + gchar *style; + + if (!g_file_get_contents(file, &style, NULL, NULL)) { + fprintf(stderr, "Could not read style file: %s\n", file); + return; + } + + webkit_user_content_manager_add_style_sheet( + webkit_web_view_get_user_content_manager(c->view), + webkit_user_style_sheet_new(style, + WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES, + WEBKIT_USER_STYLE_LEVEL_USER, + NULL, NULL)); + + g_free(style); +} + +void +runscript(Client *c) +{ + gchar *script; + gsize l; + + if (g_file_get_contents(scriptfile, &script, &l, NULL) && l) + evalscript(c, "%s", script); + g_free(script); +} + +void +evalscript(Client *c, const char *jsstr, ...) +{ + va_list ap; + gchar *script; + + va_start(ap, jsstr); + script = g_strdup_vprintf(jsstr, ap); + va_end(ap); + + webkit_web_view_evaluate_javascript(c->view, script, -1, + NULL, NULL, NULL, NULL, NULL); + g_free(script); +} + +void +updatewinid(Client *c) +{ + snprintf(winid, LENGTH(winid), "%lu", c->xid); +} + +void +handleplumb(Client *c, const char *uri) +{ + Arg a = (Arg)PLUMB(uri); + spawn(c, &a); +} + +void +newwindow(Client *c, const Arg *a, int noembed) +{ + int i = 0; + char tmp[64]; + const char *cmd[29], *uri; + const Arg arg = { .v = cmd }; + + cmd[i++] = argv0; + cmd[i++] = "-a"; + cmd[i++] = curconfig[CookiePolicies].val.v; + cmd[i++] = curconfig[ScrollBars].val.i ? "-B" : "-b"; + if (cookiefile && g_strcmp0(cookiefile, "")) { + cmd[i++] = "-c"; + cmd[i++] = cookiefile; + } + if (stylefile && g_strcmp0(stylefile, "")) { + cmd[i++] = "-C"; + cmd[i++] = stylefile; + } + cmd[i++] = curconfig[DiskCache].val.i ? "-D" : "-d"; + if (embed && !noembed) { + cmd[i++] = "-e"; + snprintf(tmp, LENGTH(tmp), "%lu", embed); + cmd[i++] = tmp; + } + cmd[i++] = curconfig[RunInFullscreen].val.i ? "-F" : "-f" ; + cmd[i++] = curconfig[Geolocation].val.i ? "-G" : "-g" ; + cmd[i++] = curconfig[LoadImages].val.i ? "-I" : "-i" ; + cmd[i++] = curconfig[KioskMode].val.i ? "-K" : "-k" ; + cmd[i++] = curconfig[Style].val.i ? "-M" : "-m" ; + cmd[i++] = curconfig[Inspector].val.i ? "-N" : "-n" ; + if (scriptfile && g_strcmp0(scriptfile, "")) { + cmd[i++] = "-r"; + cmd[i++] = scriptfile; + } + cmd[i++] = curconfig[JavaScript].val.i ? "-S" : "-s"; + cmd[i++] = curconfig[StrictTLS].val.i ? "-T" : "-t"; + if (fulluseragent && g_strcmp0(fulluseragent, "")) { + cmd[i++] = "-u"; + cmd[i++] = fulluseragent; + } + if (showxid) + cmd[i++] = "-w"; + cmd[i++] = curconfig[Certificate].val.i ? "-X" : "-x" ; + /* do not keep zoom level */ + cmd[i++] = "--"; + if ((uri = a->v)) + cmd[i++] = uri; + cmd[i] = NULL; + + spawn(c, &arg); +} + +void +spawn(Client *c, const Arg *a) +{ + if (fork() == 0) { + if (dpy) + close(ConnectionNumber(dpy)); + close(spair[0]); + close(spair[1]); + setsid(); + execvp(((char **)a->v)[0], (char **)a->v); + fprintf(stderr, "%s: execvp %s", argv0, ((char **)a->v)[0]); + perror(" failed"); + exit(1); + } +} + +void +destroyclient(Client *c) +{ + Client *p; + + webkit_web_view_stop_loading(c->view); + /* Not needed, has already been called + gtk_widget_destroy(c->win); + */ + + for (p = clients; p && p->next != c; p = p->next) + ; + if (p) + p->next = c->next; + else + clients = c->next; + free(c); +} + +void +cleanup(void) +{ + while (clients) + destroyclient(clients); + + close(spair[0]); + close(spair[1]); + g_free(cookiefile); + g_free(scriptfile); + g_free(stylefile); + g_free(cachedir); + XCloseDisplay(dpy); +} + +WebKitWebView * +newview(Client *c, WebKitWebView *rv) +{ + WebKitWebView *v; + WebKitSettings *settings; + WebKitWebContext *context; + WebKitCookieManager *cookiemanager; + WebKitUserContentManager *contentmanager; + + /* Webview */ + if (rv) { + v = WEBKIT_WEB_VIEW(webkit_web_view_new_with_related_view(rv)); + context = webkit_web_view_get_context(v); + settings = webkit_web_view_get_settings(v); + } else { + settings = webkit_settings_new_with_settings( + "allow-file-access-from-file-urls", curconfig[FileURLsCrossAccess].val.i, + "allow-universal-access-from-file-urls", curconfig[FileURLsCrossAccess].val.i, + "auto-load-images", curconfig[LoadImages].val.i, + "default-charset", curconfig[DefaultCharset].val.v, + "default-font-size", curconfig[FontSize].val.i, + "enable-caret-browsing", curconfig[CaretBrowsing].val.i, + "enable-developer-extras", curconfig[Inspector].val.i, + "enable-dns-prefetching", curconfig[DNSPrefetch].val.i, + "enable-html5-database", curconfig[DiskCache].val.i, + "enable-html5-local-storage", curconfig[DiskCache].val.i, + "enable-javascript", curconfig[JavaScript].val.i, + "enable-site-specific-quirks", curconfig[SiteQuirks].val.i, + "enable-smooth-scrolling", curconfig[SmoothScrolling].val.i, + "enable-webgl", curconfig[WebGL].val.i, + "media-playback-requires-user-gesture", curconfig[MediaManualPlay].val.i, + NULL); +/* For more interesting settings, have a look at + * http://webkitgtk.org/reference/webkit2gtk/stable/WebKitSettings.html */ + + if (strcmp(fulluseragent, "")) { + webkit_settings_set_user_agent(settings, fulluseragent); + } else if (surfuseragent) { + webkit_settings_set_user_agent_with_application_details( + settings, "Surf", VERSION); + } + useragent = webkit_settings_get_user_agent(settings); + + contentmanager = webkit_user_content_manager_new(); + + if (curconfig[Ephemeral].val.i) { + context = webkit_web_context_new_ephemeral(); + } else { + context = webkit_web_context_new_with_website_data_manager( + webkit_website_data_manager_new( + "base-cache-directory", cachedir, + "base-data-directory", cachedir, + NULL)); + } + + cookiemanager = webkit_web_context_get_cookie_manager(context); + + /* TLS */ + webkit_website_data_manager_set_tls_errors_policy( + webkit_web_context_get_website_data_manager(context), + curconfig[StrictTLS].val.i ? WEBKIT_TLS_ERRORS_POLICY_FAIL : + WEBKIT_TLS_ERRORS_POLICY_IGNORE); + /* disk cache */ + webkit_web_context_set_cache_model(context, + curconfig[DiskCache].val.i ? WEBKIT_CACHE_MODEL_WEB_BROWSER : + WEBKIT_CACHE_MODEL_DOCUMENT_VIEWER); + + /* Currently only works with text file to be compatible with curl */ + if (!curconfig[Ephemeral].val.i) + webkit_cookie_manager_set_persistent_storage(cookiemanager, + cookiefile, WEBKIT_COOKIE_PERSISTENT_STORAGE_TEXT); + /* cookie policy */ + webkit_cookie_manager_set_accept_policy(cookiemanager, + cookiepolicy_get()); + /* languages */ + webkit_web_context_set_preferred_languages(context, + curconfig[PreferredLanguages].val.v); + webkit_web_context_set_spell_checking_languages(context, + curconfig[SpellLanguages].val.v); + webkit_web_context_set_spell_checking_enabled(context, + curconfig[SpellChecking].val.i); + + g_signal_connect(G_OBJECT(context), "download-started", + G_CALLBACK(downloadstarted), c); + g_signal_connect(G_OBJECT(context), "initialize-web-extensions", + G_CALLBACK(initwebextensions), c); + + v = g_object_new(WEBKIT_TYPE_WEB_VIEW, + "settings", settings, + "user-content-manager", contentmanager, + "web-context", context, + NULL); + } + + g_signal_connect(G_OBJECT(v), "notify::estimated-load-progress", + G_CALLBACK(progresschanged), c); + g_signal_connect(G_OBJECT(v), "notify::title", + G_CALLBACK(titlechanged), c); + g_signal_connect(G_OBJECT(v), "button-release-event", + G_CALLBACK(buttonreleased), c); + g_signal_connect(G_OBJECT(v), "close", + G_CALLBACK(closeview), c); + g_signal_connect(G_OBJECT(v), "create", + G_CALLBACK(createview), c); + g_signal_connect(G_OBJECT(v), "decide-policy", + G_CALLBACK(decidepolicy), c); + g_signal_connect(G_OBJECT(v), "insecure-content-detected", + G_CALLBACK(insecurecontent), c); + g_signal_connect(G_OBJECT(v), "load-failed-with-tls-errors", + G_CALLBACK(loadfailedtls), c); + g_signal_connect(G_OBJECT(v), "load-changed", + G_CALLBACK(loadchanged), c); + g_signal_connect(G_OBJECT(v), "mouse-target-changed", + G_CALLBACK(mousetargetchanged), c); + g_signal_connect(G_OBJECT(v), "permission-request", + G_CALLBACK(permissionrequested), c); + g_signal_connect(G_OBJECT(v), "ready-to-show", + G_CALLBACK(showview), c); + g_signal_connect(G_OBJECT(v), "user-message-received", + G_CALLBACK(viewusrmsgrcv), c); + g_signal_connect(G_OBJECT(v), "web-process-terminated", + G_CALLBACK(webprocessterminated), c); + + c->context = context; + c->settings = settings; + + setparameter(c, 0, DarkMode, &curconfig[DarkMode].val); + + return v; +} + +void +initwebextensions(WebKitWebContext *wc, Client *c) +{ + webkit_web_context_set_web_extensions_directory(wc, WEBEXTDIR); +} + +GtkWidget * +createview(WebKitWebView *v, WebKitNavigationAction *a, Client *c) +{ + Client *n; + + switch (webkit_navigation_action_get_navigation_type(a)) { + case WEBKIT_NAVIGATION_TYPE_OTHER: /* fallthrough */ + /* + * popup windows of type “other” are almost always triggered + * by user gesture, so inverse the logic here + */ +/* instead of this, compare destination uri to mouse-over uri for validating window */ + if (webkit_navigation_action_is_user_gesture(a)) + return NULL; + case WEBKIT_NAVIGATION_TYPE_LINK_CLICKED: /* fallthrough */ + case WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED: /* fallthrough */ + case WEBKIT_NAVIGATION_TYPE_BACK_FORWARD: /* fallthrough */ + case WEBKIT_NAVIGATION_TYPE_RELOAD: /* fallthrough */ + case WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED: + n = newclient(c); + break; + default: + return NULL; + } + + return GTK_WIDGET(n->view); +} + +gboolean +buttonreleased(GtkWidget *w, GdkEvent *e, Client *c) +{ + WebKitHitTestResultContext element; + int i; + + element = webkit_hit_test_result_get_context(c->mousepos); + + for (i = 0; i < LENGTH(buttons); ++i) { + if (element & buttons[i].target && + e->button.button == buttons[i].button && + CLEANMASK(e->button.state) == CLEANMASK(buttons[i].mask) && + buttons[i].func) { + buttons[i].func(c, &buttons[i].arg, c->mousepos); + return buttons[i].stopevent; + } + } + + return FALSE; +} + +GdkFilterReturn +processx(GdkXEvent *e, GdkEvent *event, gpointer d) +{ + Client *c = (Client *)d; + XPropertyEvent *ev; + Arg a; + + if (((XEvent *)e)->type == PropertyNotify) { + ev = &((XEvent *)e)->xproperty; + if (ev->state == PropertyNewValue) { + if (ev->atom == atoms[AtomFind]) { + find(c, NULL); + + return GDK_FILTER_REMOVE; + } else if (ev->atom == atoms[AtomGo]) { + a.v = getatom(c, AtomGo); + loaduri(c, &a); + + return GDK_FILTER_REMOVE; + } + } + } + return GDK_FILTER_CONTINUE; +} + +gboolean +winevent(GtkWidget *w, GdkEvent *e, Client *c) +{ + int i; + + switch (e->type) { + case GDK_ENTER_NOTIFY: + c->overtitle = c->targeturi; + updatetitle(c); + break; + case GDK_KEY_PRESS: + if (!curconfig[KioskMode].val.i) { + for (i = 0; i < LENGTH(keys); ++i) { + if (gdk_keyval_to_lower(e->key.keyval) == + keys[i].keyval && + CLEANMASK(e->key.state) == keys[i].mod && + keys[i].func) { + updatewinid(c); + keys[i].func(c, &(keys[i].arg)); + return TRUE; + } + } + } + case GDK_LEAVE_NOTIFY: + c->overtitle = NULL; + updatetitle(c); + break; + case GDK_WINDOW_STATE: + if (e->window_state.changed_mask == + GDK_WINDOW_STATE_FULLSCREEN) + c->fullscreen = e->window_state.new_window_state & + GDK_WINDOW_STATE_FULLSCREEN; + break; + default: + break; + } + + return FALSE; +} + +void +showview(WebKitWebView *v, Client *c) +{ + GdkRGBA bgcolor = { 0 }; + GdkWindow *gwin; + + c->finder = webkit_web_view_get_find_controller(c->view); + c->inspector = webkit_web_view_get_inspector(c->view); + + c->pageid = webkit_web_view_get_page_id(c->view); + c->win = createwindow(c); + + gtk_container_add(GTK_CONTAINER(c->win), GTK_WIDGET(c->view)); + gtk_widget_show_all(c->win); + gtk_widget_grab_focus(GTK_WIDGET(c->view)); + + gwin = gtk_widget_get_window(GTK_WIDGET(c->win)); + c->xid = gdk_x11_window_get_xid(gwin); + updatewinid(c); + if (showxid) { + gdk_display_sync(gtk_widget_get_display(c->win)); + puts(winid); + fflush(stdout); + } + + if (curconfig[HideBackground].val.i) + webkit_web_view_set_background_color(c->view, &bgcolor); + + if (!curconfig[KioskMode].val.i) { + gdk_window_set_events(gwin, GDK_ALL_EVENTS_MASK); + gdk_window_add_filter(gwin, processx, c); + } + + if (curconfig[RunInFullscreen].val.i) + togglefullscreen(c, NULL); + + if (curconfig[ZoomLevel].val.f != 1.0) + webkit_web_view_set_zoom_level(c->view, + curconfig[ZoomLevel].val.f); + + setatom(c, AtomFind, ""); + setatom(c, AtomUri, "about:blank"); +} + +GtkWidget * +createwindow(Client *c) +{ + char *wmstr; + GtkWidget *w; + + if (embed) { + w = gtk_plug_new(embed); + } else { + w = gtk_window_new(GTK_WINDOW_TOPLEVEL); + + wmstr = g_path_get_basename(argv0); + gtk_window_set_wmclass(GTK_WINDOW(w), wmstr, "Surf"); + g_free(wmstr); + + wmstr = g_strdup_printf("%s[%"PRIu64"]", "Surf", c->pageid); + gtk_window_set_role(GTK_WINDOW(w), wmstr); + g_free(wmstr); + + gtk_window_set_default_size(GTK_WINDOW(w), winsize[0], winsize[1]); + } + + g_signal_connect(G_OBJECT(w), "destroy", + G_CALLBACK(destroywin), c); + g_signal_connect(G_OBJECT(w), "enter-notify-event", + G_CALLBACK(winevent), c); + g_signal_connect(G_OBJECT(w), "key-press-event", + G_CALLBACK(winevent), c); + g_signal_connect(G_OBJECT(w), "leave-notify-event", + G_CALLBACK(winevent), c); + g_signal_connect(G_OBJECT(w), "window-state-event", + G_CALLBACK(winevent), c); + + return w; +} + +gboolean +loadfailedtls(WebKitWebView *v, gchar *uri, GTlsCertificate *cert, + GTlsCertificateFlags err, Client *c) +{ + GString *errmsg = g_string_new(NULL); + gchar *html, *pem; + + c->failedcert = g_object_ref(cert); + c->tlserr = err; + c->errorpage = 1; + + if (err & G_TLS_CERTIFICATE_UNKNOWN_CA) + g_string_append(errmsg, + "The signing certificate authority is not known.
"); + if (err & G_TLS_CERTIFICATE_BAD_IDENTITY) + g_string_append(errmsg, + "The certificate does not match the expected identity " + "of the site that it was retrieved from.
"); + if (err & G_TLS_CERTIFICATE_NOT_ACTIVATED) + g_string_append(errmsg, + "The certificate's activation time " + "is still in the future.
"); + if (err & G_TLS_CERTIFICATE_EXPIRED) + g_string_append(errmsg, "The certificate has expired.
"); + if (err & G_TLS_CERTIFICATE_REVOKED) + g_string_append(errmsg, + "The certificate has been revoked according to " + "the GTlsConnection's certificate revocation list.
"); + if (err & G_TLS_CERTIFICATE_INSECURE) + g_string_append(errmsg, + "The certificate's algorithm is considered insecure.
"); + if (err & G_TLS_CERTIFICATE_GENERIC_ERROR) + g_string_append(errmsg, + "Some error occurred validating the certificate.
"); + + g_object_get(cert, "certificate-pem", &pem, NULL); + html = g_strdup_printf("

Could not validate TLS for “%s”
%s

" + "

You can inspect the following certificate " + "with Ctrl-t (default keybinding).

" + "

%s

", uri, errmsg->str, pem); + g_free(pem); + g_string_free(errmsg, TRUE); + + webkit_web_view_load_alternate_html(c->view, html, uri, NULL); + g_free(html); + + return TRUE; +} + +void +loadchanged(WebKitWebView *v, WebKitLoadEvent e, Client *c) +{ + const char *uri = geturi(c); + + switch (e) { + case WEBKIT_LOAD_STARTED: + setatom(c, AtomUri, uri); + c->title = uri; + c->https = c->insecure = 0; + seturiparameters(c, uri, loadtransient); + if (c->errorpage) + c->errorpage = 0; + else + g_clear_object(&c->failedcert); + break; + case WEBKIT_LOAD_REDIRECTED: + setatom(c, AtomUri, uri); + c->title = uri; + seturiparameters(c, uri, loadtransient); + break; + case WEBKIT_LOAD_COMMITTED: + setatom(c, AtomUri, uri); + c->title = uri; + seturiparameters(c, uri, loadcommitted); + c->https = webkit_web_view_get_tls_info(c->view, &c->cert, + &c->tlserr); + break; + case WEBKIT_LOAD_FINISHED: + seturiparameters(c, uri, loadfinished); + /* Disabled until we write some WebKitWebExtension for + * manipulating the DOM directly. + evalscript(c, "document.documentElement.style.overflow = '%s'", + enablescrollbars ? "auto" : "hidden"); + */ + runscript(c); + break; + } + updatetitle(c); +} + +void +progresschanged(WebKitWebView *v, GParamSpec *ps, Client *c) +{ + c->progress = webkit_web_view_get_estimated_load_progress(c->view) * + 100; + updatetitle(c); +} + +void +titlechanged(WebKitWebView *view, GParamSpec *ps, Client *c) +{ + c->title = webkit_web_view_get_title(c->view); + updatetitle(c); +} + +gboolean +viewusrmsgrcv(WebKitWebView *v, WebKitUserMessage *m, gpointer unused) +{ + WebKitUserMessage *r; + GUnixFDList *gfd; + const char *name; + + name = webkit_user_message_get_name(m); + if (strcmp(name, "page-created") != 0) { + fprintf(stderr, "surf: Unknown UserMessage: %s\n", name); + return TRUE; + } + + if (spair[1] < 0) + return TRUE; + + gfd = g_unix_fd_list_new_from_array(&spair[1], 1); + r = webkit_user_message_new_with_fd_list("surf-pipe", NULL, gfd); + + webkit_user_message_send_reply(m, r); + + return TRUE; +} + +void +mousetargetchanged(WebKitWebView *v, WebKitHitTestResult *h, guint modifiers, + Client *c) +{ + WebKitHitTestResultContext hc = webkit_hit_test_result_get_context(h); + + /* Keep the hit test to know where is the pointer on the next click */ + c->mousepos = h; + + if (hc & OnLink) + c->targeturi = webkit_hit_test_result_get_link_uri(h); + else if (hc & OnImg) + c->targeturi = webkit_hit_test_result_get_image_uri(h); + else if (hc & OnMedia) + c->targeturi = webkit_hit_test_result_get_media_uri(h); + else + c->targeturi = NULL; + + c->overtitle = c->targeturi; + updatetitle(c); +} + +gboolean +permissionrequested(WebKitWebView *v, WebKitPermissionRequest *r, Client *c) +{ + ParamName param = ParameterLast; + + if (WEBKIT_IS_GEOLOCATION_PERMISSION_REQUEST(r)) { + param = Geolocation; + } else if (WEBKIT_IS_USER_MEDIA_PERMISSION_REQUEST(r)) { + if (webkit_user_media_permission_is_for_audio_device( + WEBKIT_USER_MEDIA_PERMISSION_REQUEST(r))) + param = AccessMicrophone; + else if (webkit_user_media_permission_is_for_video_device( + WEBKIT_USER_MEDIA_PERMISSION_REQUEST(r))) + param = AccessWebcam; + } else { + return FALSE; + } + + if (curconfig[param].val.i) + webkit_permission_request_allow(r); + else + webkit_permission_request_deny(r); + + return TRUE; +} + +gboolean +decidepolicy(WebKitWebView *v, WebKitPolicyDecision *d, + WebKitPolicyDecisionType dt, Client *c) +{ + switch (dt) { + case WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION: + decidenavigation(d, c); + break; + case WEBKIT_POLICY_DECISION_TYPE_NEW_WINDOW_ACTION: + decidenewwindow(d, c); + break; + case WEBKIT_POLICY_DECISION_TYPE_RESPONSE: + decideresource(d, c); + break; + default: + webkit_policy_decision_ignore(d); + break; + } + return TRUE; +} + +void +decidenavigation(WebKitPolicyDecision *d, Client *c) +{ + WebKitNavigationAction *a = + webkit_navigation_policy_decision_get_navigation_action( + WEBKIT_NAVIGATION_POLICY_DECISION(d)); + + switch (webkit_navigation_action_get_navigation_type(a)) { + case WEBKIT_NAVIGATION_TYPE_LINK_CLICKED: /* fallthrough */ + case WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED: /* fallthrough */ + case WEBKIT_NAVIGATION_TYPE_BACK_FORWARD: /* fallthrough */ + case WEBKIT_NAVIGATION_TYPE_RELOAD: /* fallthrough */ + case WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED: /* fallthrough */ + case WEBKIT_NAVIGATION_TYPE_OTHER: /* fallthrough */ + default: + /* Do not navigate to links with a "_blank" target (popup) */ + if (webkit_navigation_action_get_frame_name(a)) { + webkit_policy_decision_ignore(d); + } else { + /* Filter out navigation to different domain ? */ + /* get action→urirequest, copy and load in new window+view + * on Ctrl+Click ? */ + webkit_policy_decision_use(d); + } + break; + } +} + +void +decidenewwindow(WebKitPolicyDecision *d, Client *c) +{ + Arg arg; + WebKitNavigationAction *a = + webkit_navigation_policy_decision_get_navigation_action( + WEBKIT_NAVIGATION_POLICY_DECISION(d)); + + + switch (webkit_navigation_action_get_navigation_type(a)) { + case WEBKIT_NAVIGATION_TYPE_LINK_CLICKED: /* fallthrough */ + case WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED: /* fallthrough */ + case WEBKIT_NAVIGATION_TYPE_BACK_FORWARD: /* fallthrough */ + case WEBKIT_NAVIGATION_TYPE_RELOAD: /* fallthrough */ + case WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED: + /* Filter domains here */ +/* If the value of “mouse-button” is not 0, then the navigation was triggered by a mouse event. + * test for link clicked but no button ? */ + arg.v = webkit_uri_request_get_uri( + webkit_navigation_action_get_request(a)); + newwindow(c, &arg, 0); + break; + case WEBKIT_NAVIGATION_TYPE_OTHER: /* fallthrough */ + default: + break; + } + + webkit_policy_decision_ignore(d); +} + +void +decideresource(WebKitPolicyDecision *d, Client *c) +{ + int i, isascii = 1; + WebKitResponsePolicyDecision *r = WEBKIT_RESPONSE_POLICY_DECISION(d); + WebKitURIResponse *res = + webkit_response_policy_decision_get_response(r); + const gchar *uri = webkit_uri_response_get_uri(res); + + if (g_str_has_suffix(uri, "/favicon.ico")) { + webkit_policy_decision_ignore(d); + return; + } + + if (!g_str_has_prefix(uri, "http://") + && !g_str_has_prefix(uri, "https://") + && !g_str_has_prefix(uri, "about:") + && !g_str_has_prefix(uri, "file://") + && !g_str_has_prefix(uri, "webkit://") + && !g_str_has_prefix(uri, "data:") + && !g_str_has_prefix(uri, "blob:") + && strlen(uri) > 0) { + for (i = 0; i < strlen(uri); i++) { + if (!g_ascii_isprint(uri[i])) { + isascii = 0; + break; + } + } + if (isascii) { + handleplumb(c, uri); + webkit_policy_decision_ignore(d); + return; + } + } + + if (webkit_response_policy_decision_is_mime_type_supported(r)) { + webkit_policy_decision_use(d); + } else { + webkit_policy_decision_ignore(d); + download(c, res); + } +} + +void +insecurecontent(WebKitWebView *v, WebKitInsecureContentEvent e, Client *c) +{ + c->insecure = 1; +} + +void +downloadstarted(WebKitWebContext *wc, WebKitDownload *d, Client *c) +{ + g_signal_connect(G_OBJECT(d), "notify::response", + G_CALLBACK(responsereceived), c); +} + +void +responsereceived(WebKitDownload *d, GParamSpec *ps, Client *c) +{ + download(c, webkit_download_get_response(d)); + webkit_download_cancel(d); +} + +void +download(Client *c, WebKitURIResponse *r) +{ + Arg a = (Arg)DOWNLOAD(webkit_uri_response_get_uri(r), geturi(c)); + spawn(c, &a); +} + +void +webprocessterminated(WebKitWebView *v, WebKitWebProcessTerminationReason r, + Client *c) +{ + fprintf(stderr, "web process terminated: %s\n", + r == WEBKIT_WEB_PROCESS_CRASHED ? "crashed" : "no memory"); + closeview(v, c); +} + +void +closeview(WebKitWebView *v, Client *c) +{ + gtk_widget_destroy(c->win); +} + +void +destroywin(GtkWidget* w, Client *c) +{ + destroyclient(c); + if (!clients) + gtk_main_quit(); +} + +void +pasteuri(GtkClipboard *clipboard, const char *text, gpointer d) +{ + Arg a = {.v = text }; + if (text) + loaduri((Client *) d, &a); +} + +void +reload(Client *c, const Arg *a) +{ + if (a->i) + webkit_web_view_reload_bypass_cache(c->view); + else + webkit_web_view_reload(c->view); +} + +void +print(Client *c, const Arg *a) +{ + webkit_print_operation_run_dialog(webkit_print_operation_new(c->view), + GTK_WINDOW(c->win)); +} + +void +showcert(Client *c, const Arg *a) +{ + GTlsCertificate *cert = c->failedcert ? c->failedcert : c->cert; + GcrCertificate *gcrt; + GByteArray *crt; + GtkWidget *win; + GcrCertificateWidget *wcert; + + if (!cert) + return; + + g_object_get(cert, "certificate", &crt, NULL); + gcrt = gcr_simple_certificate_new(crt->data, crt->len); + g_byte_array_unref(crt); + + win = gtk_window_new(GTK_WINDOW_TOPLEVEL); + wcert = gcr_certificate_widget_new(gcrt); + g_object_unref(gcrt); + + gtk_container_add(GTK_CONTAINER(win), GTK_WIDGET(wcert)); + gtk_widget_show_all(win); +} + +void +clipboard(Client *c, const Arg *a) +{ + if (a->i) { /* load clipboard uri */ + gtk_clipboard_request_text(gtk_clipboard_get( + GDK_SELECTION_PRIMARY), + pasteuri, c); + } else { /* copy uri */ + gtk_clipboard_set_text(gtk_clipboard_get( + GDK_SELECTION_PRIMARY), c->targeturi + ? c->targeturi : geturi(c), -1); + } +} + +void +zoom(Client *c, const Arg *a) +{ + if (a->i > 0) + webkit_web_view_set_zoom_level(c->view, + curconfig[ZoomLevel].val.f + 0.1); + else if (a->i < 0) + webkit_web_view_set_zoom_level(c->view, + curconfig[ZoomLevel].val.f - 0.1); + else + webkit_web_view_set_zoom_level(c->view, 1.0); + + curconfig[ZoomLevel].val.f = webkit_web_view_get_zoom_level(c->view); +} + +static void +msgext(Client *c, char type, const Arg *a) +{ + static unsigned char msg[MSGBUFSZ]; + int ret; + + if (spair[0] < 0) + return; + + ret = snprintf(msg, sizeof(msg), "%c%c%c", + (unsigned char)c->pageid, type, (signed char)a->i); + if (ret >= sizeof(msg)) { + fprintf(stderr, "surf: message too long: %d\n", ret); + return; + } + + if (send(spair[0], msg, ret, 0) != ret) + fprintf(stderr, "surf: error sending: %hhu/%c/%d (%d)\n", + (unsigned char)c->pageid, type, a->i, ret); +} + +void +scrollv(Client *c, const Arg *a) +{ + msgext(c, 'v', a); +} + +void +scrollh(Client *c, const Arg *a) +{ + msgext(c, 'h', a); +} + +void +navigate(Client *c, const Arg *a) +{ + if (a->i < 0) + webkit_web_view_go_back(c->view); + else if (a->i > 0) + webkit_web_view_go_forward(c->view); +} + +void +stop(Client *c, const Arg *a) +{ + webkit_web_view_stop_loading(c->view); +} + +void +toggle(Client *c, const Arg *a) +{ + curconfig[a->i].val.i ^= 1; + setparameter(c, 1, (ParamName)a->i, &curconfig[a->i].val); +} + +void +togglefullscreen(Client *c, const Arg *a) +{ + /* toggling value is handled in winevent() */ + if (c->fullscreen) + gtk_window_unfullscreen(GTK_WINDOW(c->win)); + else + gtk_window_fullscreen(GTK_WINDOW(c->win)); +} + +void +togglecookiepolicy(Client *c, const Arg *a) +{ + ++cookiepolicy; + cookiepolicy %= strlen(curconfig[CookiePolicies].val.v); + + setparameter(c, 0, CookiePolicies, NULL); +} + +void +toggleinspector(Client *c, const Arg *a) +{ + if (webkit_web_inspector_is_attached(c->inspector)) + webkit_web_inspector_close(c->inspector); + else if (curconfig[Inspector].val.i) + webkit_web_inspector_show(c->inspector); +} + +void +find(Client *c, const Arg *a) +{ + const char *s, *f; + + if (a && a->i) { + if (a->i > 0) + webkit_find_controller_search_next(c->finder); + else + webkit_find_controller_search_previous(c->finder); + } else { + s = getatom(c, AtomFind); + f = webkit_find_controller_get_search_text(c->finder); + + if (g_strcmp0(f, s) == 0) /* reset search */ + webkit_find_controller_search(c->finder, "", findopts, + G_MAXUINT); + + webkit_find_controller_search(c->finder, s, findopts, + G_MAXUINT); + + if (strcmp(s, "") == 0) + webkit_find_controller_search_finish(c->finder); + } +} + +void +clicknavigate(Client *c, const Arg *a, WebKitHitTestResult *h) +{ + navigate(c, a); +} + +void +clicknewwindow(Client *c, const Arg *a, WebKitHitTestResult *h) +{ + Arg arg; + + arg.v = webkit_hit_test_result_get_link_uri(h); + newwindow(c, &arg, a->i); +} + +void +clickexternplayer(Client *c, const Arg *a, WebKitHitTestResult *h) +{ + Arg arg; + + arg = (Arg)VIDEOPLAY(webkit_hit_test_result_get_media_uri(h)); + spawn(c, &arg); +} + +int +main(int argc, char *argv[]) +{ + Arg arg; + Client *c; + + memset(&arg, 0, sizeof(arg)); + + /* command line args */ + ARGBEGIN { + case 'a': + defconfig[CookiePolicies].val.v = EARGF(usage()); + defconfig[CookiePolicies].prio = 2; + break; + case 'b': + defconfig[ScrollBars].val.i = 0; + defconfig[ScrollBars].prio = 2; + break; + case 'B': + defconfig[ScrollBars].val.i = 1; + defconfig[ScrollBars].prio = 2; + break; + case 'c': + cookiefile = EARGF(usage()); + break; + case 'C': + stylefile = EARGF(usage()); + break; + case 'd': + defconfig[DiskCache].val.i = 0; + defconfig[DiskCache].prio = 2; + break; + case 'D': + defconfig[DiskCache].val.i = 1; + defconfig[DiskCache].prio = 2; + break; + case 'e': + embed = strtol(EARGF(usage()), NULL, 0); + break; + case 'f': + defconfig[RunInFullscreen].val.i = 0; + defconfig[RunInFullscreen].prio = 2; + break; + case 'F': + defconfig[RunInFullscreen].val.i = 1; + defconfig[RunInFullscreen].prio = 2; + break; + case 'g': + defconfig[Geolocation].val.i = 0; + defconfig[Geolocation].prio = 2; + break; + case 'G': + defconfig[Geolocation].val.i = 1; + defconfig[Geolocation].prio = 2; + break; + case 'i': + defconfig[LoadImages].val.i = 0; + defconfig[LoadImages].prio = 2; + break; + case 'I': + defconfig[LoadImages].val.i = 1; + defconfig[LoadImages].prio = 2; + break; + case 'k': + defconfig[KioskMode].val.i = 0; + defconfig[KioskMode].prio = 2; + break; + case 'K': + defconfig[KioskMode].val.i = 1; + defconfig[KioskMode].prio = 2; + break; + case 'm': + defconfig[Style].val.i = 0; + defconfig[Style].prio = 2; + break; + case 'M': + defconfig[Style].val.i = 1; + defconfig[Style].prio = 2; + break; + case 'n': + defconfig[Inspector].val.i = 0; + defconfig[Inspector].prio = 2; + break; + case 'N': + defconfig[Inspector].val.i = 1; + defconfig[Inspector].prio = 2; + break; + case 'r': + scriptfile = EARGF(usage()); + break; + case 's': + defconfig[JavaScript].val.i = 0; + defconfig[JavaScript].prio = 2; + break; + case 'S': + defconfig[JavaScript].val.i = 1; + defconfig[JavaScript].prio = 2; + break; + case 't': + defconfig[StrictTLS].val.i = 0; + defconfig[StrictTLS].prio = 2; + break; + case 'T': + defconfig[StrictTLS].val.i = 1; + defconfig[StrictTLS].prio = 2; + break; + case 'u': + fulluseragent = EARGF(usage()); + break; + case 'v': + die("surf-"VERSION", see LICENSE for © details\n"); + case 'w': + showxid = 1; + break; + case 'x': + defconfig[Certificate].val.i = 0; + defconfig[Certificate].prio = 2; + break; + case 'X': + defconfig[Certificate].val.i = 1; + defconfig[Certificate].prio = 2; + break; + case 'z': + defconfig[ZoomLevel].val.f = strtof(EARGF(usage()), NULL); + defconfig[ZoomLevel].prio = 2; + break; + default: + usage(); + } ARGEND; + if (argc > 0) + arg.v = argv[0]; + else + arg.v = "about:blank"; + + setup(); + c = newclient(NULL); + showview(NULL, c); + + loaduri(c, &arg); + updatetitle(c); + + gtk_main(); + cleanup(); + + return 0; +} diff --git a/dwm/surf/surf.o b/dwm/surf/surf.o new file mode 100644 index 0000000000000000000000000000000000000000..8aa60bf835f66b09833b3739eca5b09969a96f10 GIT binary patch literal 71816 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�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 literal 0 HcmV?d00001 diff --git a/dwm/surf/surf.png b/dwm/surf/surf.png new file mode 100644 index 0000000000000000000000000000000000000000..f5b2ab149a2ed429e8875541b80a7cf974ded0c4 GIT binary patch literal 240 zcmeAS@N?(olHy`uVBq!ia0vp^;Xtgx$P6Sm7Jab>QjEnx?oJHr&dIz4a@YcVLR^6~ z5G+mKc^*hHmIV0)GdMiEkp|)!MP|ku_QG`p**uBL&4qCHz2%`PaLR-y~NYkmHj3smw=`0t2gS7Kp{g< z7sn8Z%UjQGfr(=)l +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "common.h" + +#define LENGTH(x) (sizeof(x) / sizeof(x[0])) + +static WebKitWebExtension *webext; +static int sock; + +/* + * Return: + * 0 No data processed: need more data + * > 0 Amount of data processed or discarded + */ +static size_t +evalmsg(char *msg, size_t sz) +{ + char js[48]; + WebKitWebPage *page; + JSCContext *jsc; + JSCValue *jsv; + + if (!(page = webkit_web_extension_get_page(webext, msg[0]))) + return sz; + + if (sz < 2) + return 0; + + jsc = webkit_frame_get_js_context(webkit_web_page_get_main_frame(page)); + jsv = NULL; + + switch (msg[1]) { + case 'h': + if (sz < 3) { + sz = 0; + break; + } + sz = 3; + snprintf(js, sizeof(js), + "window.scrollBy(window.innerWidth/100*%hhd,0);", + msg[2]); + jsv = jsc_context_evaluate(jsc, js, -1); + break; + case 'v': + if (sz < 3) { + sz = 0; + break; + } + sz = 3; + snprintf(js, sizeof(js), + "window.scrollBy(0,window.innerHeight/100*%hhd);", + msg[2]); + jsv = jsc_context_evaluate(jsc, js, -1); + break; + default: + fprintf(stderr, "%s:%d:evalmsg: unknown cmd(%zu): '%#x'\n", + __FILE__, __LINE__, sz, msg[1]); + } + + g_object_unref(jsc); + if (jsv) + g_object_unref(jsv); + + return sz; +} + +static gboolean +readsock(GIOChannel *s, GIOCondition c, gpointer unused) +{ + static char msg[MSGBUFSZ]; + static size_t msgoff; + GError *gerr = NULL; + size_t sz; + gsize rsz; + + if (g_io_channel_read_chars(s, msg+msgoff, sizeof(msg)-msgoff, &rsz, &gerr) != + G_IO_STATUS_NORMAL) { + if (gerr) { + fprintf(stderr, "webext: error reading socket: %s\n", + gerr->message); + g_error_free(gerr); + } + return TRUE; + } + if (msgoff >= sizeof(msg)) { + fprintf(stderr, "%s:%d:%s: msgoff: %zu", __FILE__, __LINE__, __func__, msgoff); + return FALSE; + } + + for (rsz += msgoff; rsz; rsz -= sz) { + sz = evalmsg(msg, rsz); + if (sz == 0) { + /* need more data */ + break; + } + if (sz != rsz) { + /* continue processing message */ + memmove(msg, msg+sz, rsz-sz); + } + } + msgoff = rsz; + + return TRUE; +} + +static void +pageusermessagereply(GObject *o, GAsyncResult *r, gpointer page) +{ + WebKitUserMessage *m; + GUnixFDList *gfd; + GIOChannel *gchansock; + const char *name; + int nfd; + + m = webkit_web_page_send_message_to_view_finish(page, r, NULL); + name = webkit_user_message_get_name(m); + if (strcmp(name, "surf-pipe") != 0) { + fprintf(stderr, "webext-surf: Unknown User Reply: %s\n", name); + return; + } + + gfd = webkit_user_message_get_fd_list(m); + if ((nfd = g_unix_fd_list_get_length(gfd)) != 1) { + fprintf(stderr, "webext-surf: Too many file-descriptors: %d\n", nfd); + return; + } + + sock = g_unix_fd_list_get(gfd, 0, NULL); + + gchansock = g_io_channel_unix_new(sock); + g_io_channel_set_encoding(gchansock, NULL, NULL); + g_io_channel_set_flags(gchansock, g_io_channel_get_flags(gchansock) + | G_IO_FLAG_NONBLOCK, NULL); + g_io_channel_set_close_on_unref(gchansock, TRUE); + g_io_add_watch(gchansock, G_IO_IN, readsock, NULL); +} + +void +pagecreated(WebKitWebExtension *e, WebKitWebPage *p, gpointer unused) +{ + WebKitUserMessage *msg; + + msg = webkit_user_message_new("page-created", NULL); + webkit_web_page_send_message_to_view(p, msg, NULL, pageusermessagereply, p); +} + +G_MODULE_EXPORT void +webkit_web_extension_initialize(WebKitWebExtension *e) +{ + webext = e; + + g_signal_connect(G_OBJECT(e), "page-created", + G_CALLBACK(pagecreated), NULL); +} diff --git a/dwm/surf/webext-surf.o b/dwm/surf/webext-surf.o new file mode 100644 index 0000000000000000000000000000000000000000..b691525d68c9c071ab70a3aafd6303c9a9cfaba2 GIT binary patch literal 6560 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 ato3#J=MQJNPlels5sc)^UQxgL?f(N|{WyyN literal 0 HcmV?d00001 diff --git a/dwm/surf/webext-surf.so b/dwm/surf/webext-surf.so new file mode 100755 index 0000000000000000000000000000000000000000..8a38856443785a22df3a416e2d031aefedf9cabd GIT binary patch literal 16728 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 `|!i9 z1=-WShG?i(#`(Ueq$G4s6zI%Pa;bDB2eK4eOSxAz~CN*>}g*@v{x!q|4EPN zAjomgLp<$!hz6y-v;PuiBTzx#WN+@XVL!Q6 zC=>mK#TKj1_AxaSpHaxugwb;c`fk{%Pdw3=z!vvxWKYi}X4V4;h4chB&lCO{2ypI@ zJ?(esJMcLf7@YrD$c~QV8-QW_$ez9nI(0K@GZqxY{7?2oe+&|$c9Nzp!5AF{vuRRhx$+Zx>;%OCq+q}{(c2G_TM>9vUi?;sEfr)Y3>P$ z__FY(t|C1epo~40FA+<}oc$zv?xM=Tfl?|Ns{{`1f B!Vv%f literal 0 HcmV?d00001