surf-0.7/000077500000000000000000000000001263527032200123415ustar00rootroot00000000000000surf-0.7/FAQ.md000066400000000000000000000006431263527032200132750ustar00rootroot00000000000000# Frequently Asked Questions ## Surf is starting up slowly. What might happen? The first suspect for such a behaviour is the plugin handling. Run surf on the commandline and see if there are errors because of »nspluginwrap‐ per« 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. surf-0.7/LICENSE000066400000000000000000000022331263527032200133460ustar00rootroot00000000000000MIT/X Consortium License © 2011-2012 Troels Henriksen © 2009-2011 Enno Boland © 2012 Christoph Lohmann <20h@r-36.net> 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. surf-0.7/Makefile000066400000000000000000000027531263527032200140100ustar00rootroot00000000000000# surf - simple browser # See LICENSE file for copyright and license details. include config.mk SRC = surf.c OBJ = ${SRC:.c=.o} all: options surf options: @echo surf build options: @echo "CFLAGS = ${CFLAGS}" @echo "LDFLAGS = ${LDFLAGS}" @echo "CC = ${CC}" .c.o: @echo CC $< @${CC} -c ${CFLAGS} $< ${OBJ}: config.h config.mk config.h: @echo creating $@ from config.def.h @cp config.def.h $@ surf: ${OBJ} @echo CC -o $@ @${CC} -o $@ surf.o ${LDFLAGS} clean: @echo cleaning @rm -f surf ${OBJ} surf-${VERSION}.tar.gz dist: clean @echo creating dist tarball @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 ${SRC} surf-${VERSION} @tar -cf surf-${VERSION}.tar surf-${VERSION} @gzip surf-${VERSION}.tar @rm -rf surf-${VERSION} install: all @echo installing executable file to ${DESTDIR}${PREFIX}/bin @mkdir -p ${DESTDIR}${PREFIX}/bin @cp -f surf ${DESTDIR}${PREFIX}/bin @chmod 755 ${DESTDIR}${PREFIX}/bin/surf @echo installing manual page to ${DESTDIR}${MANPREFIX}/man1 @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: @echo removing executable file from ${DESTDIR}${PREFIX}/bin @rm -f ${DESTDIR}${PREFIX}/bin/surf @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 @rm -f ${DESTDIR}${MANPREFIX}/man1/surf.1 .PHONY: all options clean dist install uninstall surf-0.7/README000066400000000000000000000016771263527032200132340ustar00rootroot00000000000000surf - 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 surf-0.7/TODO.md000066400000000000000000000003671263527032200134360ustar00rootroot00000000000000# 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) surf-0.7/arg.h000066400000000000000000000013341263527032200132640ustar00rootroot00000000000000/* * Copy me if you can. * by 20h */ #ifndef __ARG_H__ #define __ARG_H__ extern char *argv0; #define USED(x) ((void)(x)) #define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ argv[0] && argv[0][1]\ && argv[0][0] == '-';\ argc--, argv++) {\ char _argc;\ char **_argv;\ if (argv[0][1] == '-' && argv[0][2] == '\0') {\ argv++;\ argc--;\ break;\ }\ for (argv[0]++, _argv = argv; argv[0][0];\ argv[0]++) {\ if (_argv != argv)\ break;\ _argc = argv[0][0];\ switch (_argc) #define ARGEND }\ USED(_argc);\ }\ USED(argv);\ USED(argc); #define EARGF(x) ((argv[1] == NULL)? ((x), abort(), (char *)0) :\ (argc--, argv++, argv[0])) #endif surf-0.7/config.def.h000066400000000000000000000134401263527032200145160ustar00rootroot00000000000000/* modifier 0 means no modifier */ static char *useragent = "Mozilla/5.0 (X11; U; Unix; en-US) " "AppleWebKit/537.15 (KHTML, like Gecko) " "Chrome/24.0.1295.0 Safari/537.15 Surf/"VERSION; static char *scriptfile = "~/.surf/script.js"; static char *styledir = "~/.surf/styles/"; static char *cachefolder = "~/.surf/cache/"; static Bool kioskmode = FALSE; /* Ignore shortcuts */ static Bool showindicators = TRUE; /* Show indicators in window title */ static Bool zoomto96dpi = TRUE; /* Zoom pages to always emulate 96dpi */ static Bool runinfullscreen = FALSE; /* Run in fullscreen mode by default */ static guint defaultfontsize = 12; /* Default font size */ static gfloat zoomlevel = 1.0; /* Default zoom level */ /* Soup default features */ static char *cookiefile = "~/.surf/cookies.txt"; static char *cookiepolicies = "Aa@"; /* A: accept all; a: accept nothing, * @: accept all except third party */ static char *cafile = "/etc/ssl/certs/ca-certificates.crt"; static Bool strictssl = FALSE; /* Refuse untrusted SSL connections */ static time_t sessiontime = 3600; /* Webkit default features */ static Bool enablescrollbars = TRUE; static Bool enablespatialbrowsing = TRUE; static Bool enablediskcache = TRUE; static int diskcachebytes = 5 * 1024 * 1024; static Bool enableplugins = TRUE; static Bool enablescripts = TRUE; static Bool enableinspector = TRUE; static Bool enablestyle = TRUE; static Bool loadimages = TRUE; static Bool hidebackground = FALSE; static Bool allowgeolocation = TRUE; #define SETPROP(p, q) { \ .v = (char *[]){ "/bin/sh", "-c", \ "prop=\"`xprop -id $2 $0 " \ "| sed \"s/^$0(STRING) = \\(\\\\\"\\?\\)\\(.*\\)\\1$/\\2/\" " \ "| xargs -0 printf %b | dmenu`\" &&" \ "xprop -id $2 -f $1 8s -set $1 \"$prop\"", \ p, q, winid, NULL \ } \ } /* DOWNLOAD(URI, referer) */ #define DOWNLOAD(d, r) { \ .v = (char *[]){ "/bin/sh", "-c", \ "st -e /bin/sh -c \"curl -L -J -O --user-agent '$1'" \ " --referer '$2' -b $3 -c $3 '$0';" \ " sleep 5;\"", \ d, useragent, r, cookiefile, NULL \ } \ } /* PLUMB(URI) */ /* This called when some URI which does not begin with "about:", * "http://" or "https://" should be opened. */ #define PLUMB(u) {\ .v = (char *[]){ "/bin/sh", "-c", \ "xdg-open \"$0\"", u, NULL \ } \ } /* styles */ /* * The iteration will stop at the first match, beginning at the beginning of * the list. */ static SiteStyle styles[] = { /* regexp file in $styledir */ { ".*", "default.css" }, }; #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 Focus */ { MODKEY|GDK_SHIFT_MASK,GDK_r, reload, { .b = TRUE } }, { MODKEY, GDK_r, reload, { .b = FALSE } }, { MODKEY|GDK_SHIFT_MASK,GDK_p, print, { 0 } }, { MODKEY, GDK_p, clipboard, { .b = TRUE } }, { MODKEY, GDK_y, clipboard, { .b = FALSE } }, { MODKEY|GDK_SHIFT_MASK,GDK_j, zoom, { .i = -1 } }, { MODKEY|GDK_SHIFT_MASK,GDK_k, zoom, { .i = +1 } }, { MODKEY|GDK_SHIFT_MASK,GDK_q, zoom, { .i = 0 } }, { MODKEY, GDK_minus, zoom, { .i = -1 } }, { MODKEY, GDK_plus, zoom, { .i = +1 } }, { MODKEY, GDK_l, navigate, { .i = +1 } }, { MODKEY, GDK_h, navigate, { .i = -1 } }, { MODKEY, GDK_j, scroll_v, { .i = +1 } }, { MODKEY, GDK_k, scroll_v, { .i = -1 } }, { MODKEY, GDK_b, scroll_v, { .i = -10000 } }, { MODKEY, GDK_space, scroll_v, { .i = +10000 } }, { MODKEY, GDK_i, scroll_h, { .i = +1 } }, { MODKEY, GDK_u, scroll_h, { .i = -1 } }, { 0, GDK_F11, fullscreen, { 0 } }, { 0, GDK_Escape, stop, { 0 } }, { MODKEY, GDK_o, source, { 0 } }, { MODKEY|GDK_SHIFT_MASK,GDK_o, inspector, { 0 } }, { MODKEY, GDK_g, spawn, SETPROP("_SURF_URI", "_SURF_GO") }, { MODKEY, GDK_f, spawn, SETPROP("_SURF_FIND", "_SURF_FIND") }, { MODKEY, GDK_slash, spawn, SETPROP("_SURF_FIND", "_SURF_FIND") }, { MODKEY, GDK_n, find, { .b = TRUE } }, { MODKEY|GDK_SHIFT_MASK,GDK_n, find, { .b = FALSE } }, { MODKEY|GDK_SHIFT_MASK,GDK_c, toggle, { .v = "enable-caret-browsing" } }, { MODKEY|GDK_SHIFT_MASK,GDK_i, toggle, { .v = "auto-load-images" } }, { MODKEY|GDK_SHIFT_MASK,GDK_s, toggle, { .v = "enable-scripts" } }, { MODKEY|GDK_SHIFT_MASK,GDK_v, toggle, { .v = "enable-plugins" } }, { MODKEY|GDK_SHIFT_MASK,GDK_a, togglecookiepolicy, { 0 } }, { MODKEY|GDK_SHIFT_MASK,GDK_m, togglestyle, { 0 } }, { MODKEY|GDK_SHIFT_MASK,GDK_b, togglescrollbars, { 0 } }, { MODKEY|GDK_SHIFT_MASK,GDK_g, togglegeolocation, { 0 } }, }; /* button definitions */ /* click can be ClkDoc, ClkLink, ClkImg, ClkMedia, ClkSel, ClkEdit, ClkAny */ static Button buttons[] = { /* click event mask button function argument */ { ClkLink, 0, 2, linkopenembed, { 0 } }, { ClkLink, MODKEY, 2, linkopen, { 0 } }, { ClkLink, MODKEY, 1, linkopen, { 0 } }, { ClkAny, 0, 8, navigate, { .i = -1 } }, { ClkAny, 0, 9, navigate, { .i = +1 } }, }; surf-0.7/config.mk000066400000000000000000000012331263527032200141360ustar00rootroot00000000000000# surf version VERSION = 0.7 # Customize below to fit your system # paths PREFIX = /usr/local MANPREFIX = ${PREFIX}/share/man X11INC = /usr/X11R6/include X11LIB = /usr/X11R6/lib GTKINC = `pkg-config --cflags gtk+-2.0 webkit-1.0` GTKLIB = `pkg-config --libs gtk+-2.0 webkit-1.0` # includes and libs INCS = -I. -I/usr/include -I${X11INC} ${GTKINC} LIBS = -L/usr/lib -lc -L${X11LIB} -lX11 ${GTKLIB} -lgthread-2.0 # flags CPPFLAGS = -DVERSION=\"${VERSION}\" -D_DEFAULT_SOURCE CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS} LDFLAGS = -g ${LIBS} # Solaris #CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\" #LDFLAGS = ${LIBS} # compiler and linker CC = cc surf-0.7/surf-open.sh000077500000000000000000000006741263527032200146250ustar00rootroot00000000000000#!/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 surf-0.7/surf.1000066400000000000000000000131261263527032200134050ustar00rootroot00000000000000.TH SURF 1 surf\-VERSION .SH NAME surf \- simple webkit-based browser .SH SYNOPSIS .B surf .RB [-bBdDfFgGiIkKmMnNpPsSvx] .RB [-a\ cookiepolicies] .RB [-c\ cookiefile] .RB [-e\ xid] .RB [-r\ scriptfile] .RB [-t\ stylefile] .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. The default is "Aa@" but could be redefined in the .I 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 \-d Disable the disk cache. .TP .B \-D Enable the disk cache. .TP .B \-e xid Reparents to window specified by .I 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 \-p Disable Plugins .TP .B \-P Enable Plugins .TP .B \-r scriptfile Specify the user .I scriptfile. .TP .B \-s Disable Javascript .TP .B \-S Enable Javascript .TP .B \-t stylefile Specify the user .I stylefile. This does disable the site-specific styles. .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 \-x Prints xid to standard output. This can be used to script the browser in for example .BR xdotool(1). .TP .B \-z zoomlevel Specify the .I zoomlevel which surf should use. .BR xprop(1). .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\-o Show the sourcecode of the current page. .TP .B Ctrl\-Shift\-a Toggle through the the .I cookie policies. 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\-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\-v Toggle the enabling of plugins on that surf instance. 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 v V plugins .TP .B m M styles .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 (SSL) .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 .TP .B no_proxy If both http_proxy is set and no_proxy contain a .BR comma-separated list of domain extensions and both is not empty upon startup, proxy will .BR not be used for each of the elements in no_proxy. .SH PLUGINS For using plugins in surf, first determine your running architecture. Then get the appropriate plugin for that architecture and copy it to .BR /usr/lib/browser-plugins or .BR /usr/lib64/browser-plugins. Surf should load them automatically. .BR If you want to use a 32bit plugin on a 64bit system, .BR nspluginwrapper(1) will help you. .SH SEE ALSO .BR dmenu(1), .BR xprop(1), .BR tabbed(1), .BR nspluginwrapper(1), .BR xdotool(1) .SH BUGS Please report them! surf-0.7/surf.c000066400000000000000000001255251263527032200134760ustar00rootroot00000000000000/* 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 "arg.h" char *argv0; #define LENGTH(x) (sizeof(x) / sizeof(x[0])) #define CLEANMASK(mask) (mask & (MODKEY|GDK_SHIFT_MASK)) #define COOKIEJAR_TYPE (cookiejar_get_type ()) #define COOKIEJAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), COOKIEJAR_TYPE, CookieJar)) enum { AtomFind, AtomGo, AtomUri, AtomLast }; enum { ClkDoc = WEBKIT_HIT_TEST_RESULT_CONTEXT_DOCUMENT, ClkLink = WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK, ClkImg = WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE, ClkMedia = WEBKIT_HIT_TEST_RESULT_CONTEXT_MEDIA, ClkSel = WEBKIT_HIT_TEST_RESULT_CONTEXT_SELECTION, ClkEdit = WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE, ClkAny = ClkDoc | ClkLink | ClkImg | ClkMedia | ClkSel | ClkEdit, }; typedef union Arg Arg; union Arg { gboolean b; gint i; const void *v; }; typedef struct Client { GtkWidget *win, *scroll, *vbox, *pane; WebKitWebView *view; WebKitWebInspector *inspector; char *title, *linkhover; const char *needle; gint progress; struct Client *next; gboolean zoomed, fullscreen, isinspecting, sslfailed; } Client; typedef struct { guint mod; guint keyval; void (*func)(Client *c, const Arg *arg); const Arg arg; } Key; typedef struct { unsigned int click; unsigned int mask; guint button; void (*func)(Client *c, const Arg *arg); const Arg arg; } Button; typedef struct { SoupCookieJarText parent_instance; int lock; } CookieJar; typedef struct { SoupCookieJarTextClass parent_class; } CookieJarClass; G_DEFINE_TYPE(CookieJar, cookiejar, SOUP_TYPE_COOKIE_JAR_TEXT) typedef struct { char *regex; char *style; regex_t re; } SiteStyle; static Display *dpy; static Atom atoms[AtomLast]; static Client *clients = NULL; static GdkNativeWindow embed = 0; static gboolean showxid = FALSE; static char winid[64]; static gboolean usingproxy = 0; static char togglestat[9]; static char pagestat[3]; static GTlsDatabase *tlsdb; static int policysel = 0; static char *stylefile = NULL; static SoupCache *diskcache = NULL; static void addaccelgroup(Client *c); static void beforerequest(WebKitWebView *w, WebKitWebFrame *f, WebKitWebResource *r, WebKitNetworkRequest *req, WebKitNetworkResponse *resp, Client *c); static char *buildfile(const char *path); static char *buildpath(const char *path); static gboolean buttonrelease(WebKitWebView *web, GdkEventButton *e, Client *c); static void cleanup(void); static void clipboard(Client *c, const Arg *arg); /* Cookiejar implementation */ static void cookiejar_changed(SoupCookieJar *self, SoupCookie *old_cookie, SoupCookie *new_cookie); static void cookiejar_finalize(GObject *self); static SoupCookieJarAcceptPolicy cookiepolicy_get(void); static SoupCookieJar *cookiejar_new(const char *filename, gboolean read_only, SoupCookieJarAcceptPolicy policy); static void cookiejar_set_property(GObject *self, guint prop_id, const GValue *value, GParamSpec *pspec); static char cookiepolicy_set(const SoupCookieJarAcceptPolicy p); static char *copystr(char **str, const char *src); static WebKitWebView *createwindow(WebKitWebView *v, WebKitWebFrame *f, Client *c); static gboolean decidedownload(WebKitWebView *v, WebKitWebFrame *f, WebKitNetworkRequest *r, gchar *m, WebKitWebPolicyDecision *p, Client *c); static gboolean decidewindow(WebKitWebView *v, WebKitWebFrame *f, WebKitNetworkRequest *r, WebKitWebNavigationAction *n, WebKitWebPolicyDecision *p, Client *c); static gboolean deletion_interface(WebKitWebView *view, WebKitDOMHTMLElement *arg1, Client *c); static void destroyclient(Client *c); static void destroywin(GtkWidget* w, Client *c); static void die(const char *errstr, ...); static void eval(Client *c, const Arg *arg); static void find(Client *c, const Arg *arg); static void fullscreen(Client *c, const Arg *arg); static void geopolicyrequested(WebKitWebView *v, WebKitWebFrame *f, WebKitGeolocationPolicyDecision *d, Client *c); static const char *getatom(Client *c, int a); static void gettogglestat(Client *c); static void getpagestat(Client *c); static char *geturi(Client *c); static const gchar *getstyle(const char *uri); static void setstyle(Client *c, const char *style); static void handleplumb(Client *c, WebKitWebView *w, const gchar *uri); static gboolean initdownload(WebKitWebView *v, WebKitDownload *o, Client *c); static void inspector(Client *c, const Arg *arg); static WebKitWebView *inspector_new(WebKitWebInspector *i, WebKitWebView *v, Client *c); static gboolean inspector_show(WebKitWebInspector *i, Client *c); static gboolean inspector_close(WebKitWebInspector *i, Client *c); static void inspector_finished(WebKitWebInspector *i, Client *c); static gboolean keypress(GtkAccelGroup *group, GObject *obj, guint key, GdkModifierType mods, Client *c); static void linkhover(WebKitWebView *v, const char* t, const char* l, Client *c); static void loadstatuschange(WebKitWebView *view, GParamSpec *pspec, Client *c); static void loaduri(Client *c, const Arg *arg); static void navigate(Client *c, const Arg *arg); static Client *newclient(void); static void newwindow(Client *c, const Arg *arg, gboolean noembed); static void pasteuri(GtkClipboard *clipboard, const char *text, gpointer d); static gboolean contextmenu(WebKitWebView *view, GtkWidget *menu, WebKitHitTestResult *target, gboolean keyboard, Client *c); static void menuactivate(GtkMenuItem *item, Client *c); static void print(Client *c, const Arg *arg); static GdkFilterReturn processx(GdkXEvent *xevent, GdkEvent *event, gpointer d); static void progresschange(WebKitWebView *view, GParamSpec *pspec, Client *c); static void linkopen(Client *c, const Arg *arg); static void linkopenembed(Client *c, const Arg *arg); static void reload(Client *c, const Arg *arg); static void scroll_h(Client *c, const Arg *arg); static void scroll_v(Client *c, const Arg *arg); static void scroll(GtkAdjustment *a, const Arg *arg); static void setatom(Client *c, int a, const char *v); static void setup(void); static void sigchld(int unused); static void source(Client *c, const Arg *arg); static void spawn(Client *c, const Arg *arg); static void stop(Client *c, const Arg *arg); static void titlechange(WebKitWebView *view, GParamSpec *pspec, Client *c); static void titlechangeleave(void *a, void *b, Client *c); static void toggle(Client *c, const Arg *arg); static void togglecookiepolicy(Client *c, const Arg *arg); static void togglegeolocation(Client *c, const Arg *arg); static void togglescrollbars(Client *c, const Arg *arg); static void togglestyle(Client *c, const Arg *arg); static void updatetitle(Client *c); static void updatewinid(Client *c); static void usage(void); static void windowobjectcleared(GtkWidget *w, WebKitWebFrame *frame, JSContextRef js, JSObjectRef win, Client *c); static void zoom(Client *c, const Arg *arg); /* configuration, allows nested code to access above variables */ #include "config.h" void addaccelgroup(Client *c) { int i; GtkAccelGroup *group = gtk_accel_group_new(); GClosure *closure; for (i = 0; i < LENGTH(keys); i++) { closure = g_cclosure_new(G_CALLBACK(keypress), c, NULL); gtk_accel_group_connect(group, keys[i].keyval, keys[i].mod, 0, closure); } gtk_window_add_accel_group(GTK_WINDOW(c->win), group); } void beforerequest(WebKitWebView *w, WebKitWebFrame *f, WebKitWebResource *r, WebKitNetworkRequest *req, WebKitNetworkResponse *resp, Client *c) { const gchar *uri = webkit_network_request_get_uri(req); int i, isascii = 1; if (g_str_has_suffix(uri, "/favicon.ico")) webkit_network_request_set_uri(req, "about:blank"); 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, "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, w, uri); } } 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; } char * buildpath(const char *path) { struct passwd *pw; char *apath, *name, *p, *fpath; if (path[0] == '~') { if (path[1] == '/' || path[1] == '\0') { p = (char *)&path[1]; pw = getpwuid(getuid()); } else { if ((p = strchr(path, '/'))) name = g_strndup(&path[1], --p - path); else name = g_strdup(&path[1]); if (!(pw = getpwnam(name))) { die("Can't get user %s home directory: %s.\n", name, path); } g_free(name); } apath = g_build_filename(pw->pw_dir, p, NULL); } 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; } gboolean buttonrelease(WebKitWebView *web, GdkEventButton *e, Client *c) { WebKitHitTestResultContext context; WebKitHitTestResult *result; Arg arg; unsigned int i; result = webkit_web_view_get_hit_test_result(web, e); g_object_get(result, "context", &context, NULL); g_object_get(result, "link-uri", &arg.v, NULL); for (i = 0; i < LENGTH(buttons); i++) { if (context & buttons[i].click && e->button == buttons[i].button && CLEANMASK(e->state) == CLEANMASK(buttons[i].mask) && buttons[i].func) { buttons[i].func(c, buttons[i].click == ClkLink && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); return true; } } return false; } void cleanup(void) { if (diskcache) { soup_cache_flush(diskcache); soup_cache_dump(diskcache); } while (clients) destroyclient(clients); g_free(cookiefile); g_free(scriptfile); g_free(stylefile); } void cookiejar_changed(SoupCookieJar *self, SoupCookie *old_cookie, SoupCookie *new_cookie) { flock(COOKIEJAR(self)->lock, LOCK_EX); if (new_cookie && !new_cookie->expires && sessiontime) { soup_cookie_set_expires(new_cookie, soup_date_new_from_now(sessiontime)); } SOUP_COOKIE_JAR_CLASS(cookiejar_parent_class)->changed(self, old_cookie, new_cookie); flock(COOKIEJAR(self)->lock, LOCK_UN); } void cookiejar_class_init(CookieJarClass *klass) { SOUP_COOKIE_JAR_CLASS(klass)->changed = cookiejar_changed; G_OBJECT_CLASS(klass)->get_property = G_OBJECT_CLASS(cookiejar_parent_class)->get_property; G_OBJECT_CLASS(klass)->set_property = cookiejar_set_property; G_OBJECT_CLASS(klass)->finalize = cookiejar_finalize; g_object_class_override_property(G_OBJECT_CLASS(klass), 1, "filename"); } void cookiejar_finalize(GObject *self) { close(COOKIEJAR(self)->lock); G_OBJECT_CLASS(cookiejar_parent_class)->finalize(self); } void cookiejar_init(CookieJar *self) { self->lock = open(cookiefile, 0); } SoupCookieJar * cookiejar_new(const char *filename, gboolean read_only, SoupCookieJarAcceptPolicy policy) { return g_object_new(COOKIEJAR_TYPE, SOUP_COOKIE_JAR_TEXT_FILENAME, filename, SOUP_COOKIE_JAR_READ_ONLY, read_only, SOUP_COOKIE_JAR_ACCEPT_POLICY, policy, NULL); } void cookiejar_set_property(GObject *self, guint prop_id, const GValue *value, GParamSpec *pspec) { flock(COOKIEJAR(self)->lock, LOCK_SH); G_OBJECT_CLASS(cookiejar_parent_class)->set_property(self, prop_id, value, pspec); flock(COOKIEJAR(self)->lock, LOCK_UN); } SoupCookieJarAcceptPolicy cookiepolicy_get(void) { switch (cookiepolicies[policysel]) { case 'a': return SOUP_COOKIE_JAR_ACCEPT_NEVER; case '@': return SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY; case 'A': default: break; } return SOUP_COOKIE_JAR_ACCEPT_ALWAYS; } char cookiepolicy_set(const SoupCookieJarAcceptPolicy ep) { switch (ep) { case SOUP_COOKIE_JAR_ACCEPT_NEVER: return 'a'; case SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY: return '@'; case SOUP_COOKIE_JAR_ACCEPT_ALWAYS: default: break; } return 'A'; } void evalscript(JSContextRef js, char *script, char* scriptname) { JSStringRef jsscript, jsscriptname; JSValueRef exception = NULL; jsscript = JSStringCreateWithUTF8CString(script); jsscriptname = JSStringCreateWithUTF8CString(scriptname); JSEvaluateScript(js, jsscript, JSContextGetGlobalObject(js), jsscriptname, 0, &exception); JSStringRelease(jsscript); JSStringRelease(jsscriptname); } void runscript(WebKitWebFrame *frame) { char *script; GError *error; if (g_file_get_contents(scriptfile, &script, NULL, &error)) { evalscript(webkit_web_frame_get_global_context(frame), script, scriptfile); } } void clipboard(Client *c, const Arg *arg) { gboolean paste = *(gboolean *)arg; if (paste) { gtk_clipboard_request_text(gtk_clipboard_get( GDK_SELECTION_PRIMARY), pasteuri, c); } else { gtk_clipboard_set_text(gtk_clipboard_get( GDK_SELECTION_PRIMARY), c->linkhover ? c->linkhover : geturi(c), -1); } } char * copystr(char **str, const char *src) { char *tmp; tmp = g_strdup(src); if (str && *str) { g_free(*str); *str = tmp; } return tmp; } WebKitWebView * createwindow(WebKitWebView *v, WebKitWebFrame *f, Client *c) { Client *n = newclient(); return n->view; } gboolean decidedownload(WebKitWebView *v, WebKitWebFrame *f, WebKitNetworkRequest *r, gchar *m, WebKitWebPolicyDecision *p, Client *c) { if (!webkit_web_view_can_show_mime_type(v, m)) { webkit_web_policy_decision_download(p); return TRUE; } return FALSE; } gboolean decidewindow(WebKitWebView *view, WebKitWebFrame *f, WebKitNetworkRequest *r, WebKitWebNavigationAction *n, WebKitWebPolicyDecision *p, Client *c) { Arg arg; if (webkit_web_navigation_action_get_reason(n) == WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED) { webkit_web_policy_decision_ignore(p); arg.v = (void *)webkit_network_request_get_uri(r); newwindow(NULL, &arg, 0); return TRUE; } return FALSE; } gboolean deletion_interface(WebKitWebView *view, WebKitDOMHTMLElement *arg1, Client *c) { return FALSE; } void destroyclient(Client *c) { Client *p; webkit_web_view_stop_loading(c->view); gtk_widget_destroy(GTK_WIDGET(c->view)); gtk_widget_destroy(c->scroll); gtk_widget_destroy(c->vbox); 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); if (clients == NULL) gtk_main_quit(); } void destroywin(GtkWidget* w, Client *c) { destroyclient(c); } void die(const char *errstr, ...) { va_list ap; va_start(ap, errstr); vfprintf(stderr, errstr, ap); va_end(ap); exit(EXIT_FAILURE); } void find(Client *c, const Arg *arg) { const char *s; s = getatom(c, AtomFind); gboolean forward = *(gboolean *)arg; webkit_web_view_search_text(c->view, s, FALSE, forward, TRUE); } void fullscreen(Client *c, const Arg *arg) { if (c->fullscreen) gtk_window_unfullscreen(GTK_WINDOW(c->win)); else gtk_window_fullscreen(GTK_WINDOW(c->win)); c->fullscreen = !c->fullscreen; } void geopolicyrequested(WebKitWebView *v, WebKitWebFrame *f, WebKitGeolocationPolicyDecision *d, Client *c) { if (allowgeolocation) webkit_geolocation_policy_allow(d); else webkit_geolocation_policy_deny(d); } const char * getatom(Client *c, int a) { static char buf[BUFSIZ]; Atom adummy; int idummy; unsigned long ldummy; unsigned char *p = NULL; XGetWindowProperty(dpy, GDK_WINDOW_XID(GTK_WIDGET(c->win)->window), atoms[a], 0L, BUFSIZ, False, XA_STRING, &adummy, &idummy, &ldummy, &ldummy, &p); if (p) strncpy(buf, (char *)p, LENGTH(buf)-1); else buf[0] = '\0'; XFree(p); return buf; } char * geturi(Client *c) { char *uri; if (!(uri = (char *)webkit_web_view_get_uri(c->view))) uri = "about:blank"; return uri; } const gchar * getstyle(const char *uri) { int i; if (stylefile != NULL) return stylefile; for (i = 0; i < LENGTH(styles); i++) { if (styles[i].regex && !regexec(&(styles[i].re), uri, 0, NULL, 0)) return styles[i].style; } return ""; } void setstyle(Client *c, const char *style) { WebKitWebSettings *settings = webkit_web_view_get_settings(c->view); g_object_set(G_OBJECT(settings), "user-stylesheet-uri", style, NULL); } void handleplumb(Client *c, WebKitWebView *w, const gchar *uri) { Arg arg; webkit_web_view_stop_loading(w); arg = (Arg)PLUMB((char *)uri); spawn(c, &arg); } gboolean initdownload(WebKitWebView *view, WebKitDownload *o, Client *c) { Arg arg; updatewinid(c); arg = (Arg)DOWNLOAD((char *)webkit_download_get_uri(o), geturi(c)); spawn(c, &arg); return FALSE; } void inspector(Client *c, const Arg *arg) { if (enableinspector) { if (c->isinspecting) webkit_web_inspector_close(c->inspector); else webkit_web_inspector_show(c->inspector); } } WebKitWebView * inspector_new(WebKitWebInspector *i, WebKitWebView *v, Client *c) { return WEBKIT_WEB_VIEW(webkit_web_view_new()); } gboolean inspector_show(WebKitWebInspector *i, Client *c) { WebKitWebView *w; if (c->isinspecting) return false; w = webkit_web_inspector_get_web_view(i); gtk_paned_pack2(GTK_PANED(c->pane), GTK_WIDGET(w), TRUE, TRUE); gtk_widget_show(GTK_WIDGET(w)); c->isinspecting = true; return true; } gboolean inspector_close(WebKitWebInspector *i, Client *c) { GtkWidget *w; if (!c->isinspecting) return false; w = GTK_WIDGET(webkit_web_inspector_get_web_view(i)); gtk_widget_hide(w); gtk_widget_destroy(w); c->isinspecting = false; return true; } void inspector_finished(WebKitWebInspector *i, Client *c) { g_free(c->inspector); } gboolean keypress(GtkAccelGroup *group, GObject *obj, guint key, GdkModifierType mods, Client *c) { guint i; gboolean processed = FALSE; mods = CLEANMASK(mods); key = gdk_keyval_to_lower(key); updatewinid(c); for (i = 0; i < LENGTH(keys); i++) { if (key == keys[i].keyval && mods == keys[i].mod && keys[i].func) { keys[i].func(c, &(keys[i].arg)); processed = TRUE; } } return processed; } void linkhover(WebKitWebView *v, const char* t, const char* l, Client *c) { if (l) { c->linkhover = copystr(&c->linkhover, l); } else if (c->linkhover) { free(c->linkhover); c->linkhover = NULL; } updatetitle(c); } void loadstatuschange(WebKitWebView *view, GParamSpec *pspec, Client *c) { WebKitWebFrame *frame; WebKitWebDataSource *src; WebKitNetworkRequest *request; SoupMessage *msg; char *uri; switch (webkit_web_view_get_load_status (c->view)) { case WEBKIT_LOAD_COMMITTED: uri = geturi(c); if (strstr(uri, "https://") == uri) { frame = webkit_web_view_get_main_frame(c->view); src = webkit_web_frame_get_data_source(frame); request = webkit_web_data_source_get_request(src); msg = webkit_network_request_get_message(request); c->sslfailed = !(soup_message_get_flags(msg) & SOUP_MESSAGE_CERTIFICATE_TRUSTED); } setatom(c, AtomUri, uri); c->title = copystr(&c->title, uri); if (enablestyle) setstyle(c, getstyle(uri)); break; case WEBKIT_LOAD_FINISHED: c->progress = 100; updatetitle(c); if (diskcache) { soup_cache_flush(diskcache); soup_cache_dump(diskcache); } break; default: break; } } void loaduri(Client *c, const Arg *arg) { char *u = NULL, *rp; const char *uri = (char *)arg->v; Arg a = { .b = FALSE }; struct stat st; if (strcmp(uri, "") == 0) return; /* In case it's a file path. */ if (stat(uri, &st) == 0) { rp = realpath(uri, NULL); u = g_strdup_printf("file://%s", rp); free(rp); } else { u = g_strrstr(uri, "://") ? g_strdup(uri) : g_strdup_printf("http://%s", uri); } setatom(c, AtomUri, uri); /* prevents endless loop */ if (strcmp(u, geturi(c)) == 0) { reload(c, &a); } else { webkit_web_view_load_uri(c->view, u); c->progress = 0; c->title = copystr(&c->title, u); updatetitle(c); } g_free(u); } void navigate(Client *c, const Arg *arg) { int steps = *(int *)arg; webkit_web_view_go_back_or_forward(c->view, steps); } Client * newclient(void) { Client *c; WebKitWebSettings *settings; WebKitWebFrame *frame; GdkGeometry hints = { 1, 1 }; GdkScreen *screen; gdouble dpi; char *ua; if (!(c = calloc(1, sizeof(Client)))) die("Cannot malloc!\n"); c->title = NULL; c->progress = 100; /* Window */ if (embed) { c->win = gtk_plug_new(embed); } else { c->win = gtk_window_new(GTK_WINDOW_TOPLEVEL); /* TA: 20091214: Despite what the GNOME docs say, the ICCCM * is always correct, so we should still call this function. * But when doing so, we *must* differentiate between a * WM_CLASS and a resource on the window. By convention, the * window class (WM_CLASS) is capped, while the resource is in * lowercase. Both these values come as a pair. */ gtk_window_set_wmclass(GTK_WINDOW(c->win), "surf", "Surf"); /* TA: 20091214: And set the role here as well -- so that * sessions can pick this up. */ gtk_window_set_role(GTK_WINDOW(c->win), "Surf"); } gtk_window_set_default_size(GTK_WINDOW(c->win), 800, 600); g_signal_connect(G_OBJECT(c->win), "destroy", G_CALLBACK(destroywin), c); g_signal_connect(G_OBJECT(c->win), "leave_notify_event", G_CALLBACK(titlechangeleave), c); if (!kioskmode) addaccelgroup(c); /* Pane */ c->pane = gtk_vpaned_new(); /* VBox */ c->vbox = gtk_vbox_new(FALSE, 0); gtk_paned_pack1(GTK_PANED(c->pane), c->vbox, TRUE, TRUE); /* Webview */ c->view = WEBKIT_WEB_VIEW(webkit_web_view_new()); g_signal_connect(G_OBJECT(c->view), "notify::title", G_CALLBACK(titlechange), c); g_signal_connect(G_OBJECT(c->view), "hovering-over-link", G_CALLBACK(linkhover), c); g_signal_connect(G_OBJECT(c->view), "geolocation-policy-decision-requested", G_CALLBACK(geopolicyrequested), c); g_signal_connect(G_OBJECT(c->view), "create-web-view", G_CALLBACK(createwindow), c); g_signal_connect(G_OBJECT(c->view), "new-window-policy-decision-requested", G_CALLBACK(decidewindow), c); g_signal_connect(G_OBJECT(c->view), "mime-type-policy-decision-requested", G_CALLBACK(decidedownload), c); g_signal_connect(G_OBJECT(c->view), "window-object-cleared", G_CALLBACK(windowobjectcleared), c); g_signal_connect(G_OBJECT(c->view), "notify::load-status", G_CALLBACK(loadstatuschange), c); g_signal_connect(G_OBJECT(c->view), "notify::progress", G_CALLBACK(progresschange), c); g_signal_connect(G_OBJECT(c->view), "download-requested", G_CALLBACK(initdownload), c); g_signal_connect(G_OBJECT(c->view), "button-release-event", G_CALLBACK(buttonrelease), c); g_signal_connect(G_OBJECT(c->view), "context-menu", G_CALLBACK(contextmenu), c); g_signal_connect(G_OBJECT(c->view), "resource-request-starting", G_CALLBACK(beforerequest), c); g_signal_connect(G_OBJECT(c->view), "should-show-delete-interface-for-element", G_CALLBACK(deletion_interface), c); /* Scrolled Window */ c->scroll = gtk_scrolled_window_new(NULL, NULL); frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(c->view)); g_signal_connect(G_OBJECT(frame), "scrollbars-policy-changed", G_CALLBACK(gtk_true), NULL); if (!enablescrollbars) { gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(c->scroll), GTK_POLICY_NEVER, GTK_POLICY_NEVER); } else { gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(c->scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); } /* Arranging */ gtk_container_add(GTK_CONTAINER(c->scroll), GTK_WIDGET(c->view)); gtk_container_add(GTK_CONTAINER(c->win), c->pane); gtk_container_add(GTK_CONTAINER(c->vbox), c->scroll); /* Setup */ gtk_box_set_child_packing(GTK_BOX(c->vbox), c->scroll, TRUE, TRUE, 0, GTK_PACK_START); gtk_widget_grab_focus(GTK_WIDGET(c->view)); gtk_widget_show(c->pane); gtk_widget_show(c->vbox); gtk_widget_show(c->scroll); gtk_widget_show(GTK_WIDGET(c->view)); gtk_widget_show(c->win); gtk_window_set_geometry_hints(GTK_WINDOW(c->win), NULL, &hints, GDK_HINT_MIN_SIZE); gdk_window_set_events(GTK_WIDGET(c->win)->window, GDK_ALL_EVENTS_MASK); gdk_window_add_filter(GTK_WIDGET(c->win)->window, processx, c); webkit_web_view_set_full_content_zoom(c->view, TRUE); runscript(frame); settings = webkit_web_view_get_settings(c->view); if (!(ua = getenv("SURF_USERAGENT"))) ua = useragent; g_object_set(G_OBJECT(settings), "user-agent", ua, NULL); g_object_set(G_OBJECT(settings), "auto-load-images", loadimages, NULL); g_object_set(G_OBJECT(settings), "enable-plugins", enableplugins, NULL); g_object_set(G_OBJECT(settings), "enable-scripts", enablescripts, NULL); g_object_set(G_OBJECT(settings), "enable-spatial-navigation", enablespatialbrowsing, NULL); g_object_set(G_OBJECT(settings), "enable-developer-extras", enableinspector, NULL); g_object_set(G_OBJECT(settings), "enable-default-context-menu", kioskmode ^ 1, NULL); g_object_set(G_OBJECT(settings), "default-font-size", defaultfontsize, NULL); g_object_set(G_OBJECT(settings), "resizable-text-areas", 1, NULL); if (enablestyle) setstyle(c, getstyle("about:blank")); /* * While stupid, CSS specifies that a pixel represents 1/96 of an inch. * This ensures websites are not unusably small with a high DPI screen. * It is equivalent to firefox's "layout.css.devPixelsPerPx" setting. */ if (zoomto96dpi) { screen = gdk_window_get_screen(GTK_WIDGET(c->win)->window); dpi = gdk_screen_get_resolution(screen); if (dpi != -1) { g_object_set(G_OBJECT(settings), "enforce-96-dpi", true, NULL); webkit_web_view_set_zoom_level(c->view, dpi/96); } } /* This might conflict with _zoomto96dpi_. */ if (zoomlevel != 1.0) webkit_web_view_set_zoom_level(c->view, zoomlevel); if (enableinspector) { c->inspector = webkit_web_view_get_inspector(c->view); g_signal_connect(G_OBJECT(c->inspector), "inspect-web-view", G_CALLBACK(inspector_new), c); g_signal_connect(G_OBJECT(c->inspector), "show-window", G_CALLBACK(inspector_show), c); g_signal_connect(G_OBJECT(c->inspector), "close-window", G_CALLBACK(inspector_close), c); g_signal_connect(G_OBJECT(c->inspector), "finished", G_CALLBACK(inspector_finished), c); c->isinspecting = false; } if (runinfullscreen) fullscreen(c, NULL); setatom(c, AtomFind, ""); setatom(c, AtomUri, "about:blank"); if (hidebackground) webkit_web_view_set_transparent(c->view, TRUE); c->next = clients; clients = c; if (showxid) { gdk_display_sync(gtk_widget_get_display(c->win)); printf("%u\n", (guint)GDK_WINDOW_XID(GTK_WIDGET(c->win)->window)); fflush(NULL); if (fclose(stdout) != 0) { die("Error closing stdout"); } } return c; } void newwindow(Client *c, const Arg *arg, gboolean noembed) { guint i = 0; const char *cmd[18], *uri; const Arg a = { .v = (void *)cmd }; char tmp[64]; cmd[i++] = argv0; cmd[i++] = "-a"; cmd[i++] = cookiepolicies; if (!enablescrollbars) cmd[i++] = "-b"; if (embed && !noembed) { cmd[i++] = "-e"; snprintf(tmp, LENGTH(tmp), "%u", (int)embed); cmd[i++] = tmp; } if (!allowgeolocation) cmd[i++] = "-g"; if (!loadimages) cmd[i++] = "-i"; if (kioskmode) cmd[i++] = "-k"; if (!enableplugins) cmd[i++] = "-p"; if (!enablescripts) cmd[i++] = "-s"; if (showxid) cmd[i++] = "-x"; if (enablediskcache) cmd[i++] = "-D"; cmd[i++] = "-c"; cmd[i++] = cookiefile; cmd[i++] = "--"; uri = arg->v ? (char *)arg->v : c->linkhover; if (uri) cmd[i++] = uri; cmd[i++] = NULL; spawn(NULL, &a); } gboolean contextmenu(WebKitWebView *view, GtkWidget *menu, WebKitHitTestResult *target, gboolean keyboard, Client *c) { GList *items = gtk_container_get_children(GTK_CONTAINER(GTK_MENU(menu))); for (GList *l = items; l; l = l->next) g_signal_connect(l->data, "activate", G_CALLBACK(menuactivate), c); g_list_free(items); return FALSE; } void menuactivate(GtkMenuItem *item, Client *c) { /* * context-menu-action-2000 open link * context-menu-action-1 open link in window * context-menu-action-2 download linked file * context-menu-action-3 copy link location * context-menu-action-7 copy image address * context-menu-action-13 reload * context-menu-action-10 back * context-menu-action-11 forward * context-menu-action-12 stop */ GtkAction *a = NULL; const char *name, *uri; GtkClipboard *prisel, *clpbrd; a = gtk_activatable_get_related_action(GTK_ACTIVATABLE(item)); if (a == NULL) return; name = gtk_action_get_name(a); if (!g_strcmp0(name, "context-menu-action-3")) { prisel = gtk_clipboard_get(GDK_SELECTION_PRIMARY); gtk_clipboard_set_text(prisel, c->linkhover, -1); } else if (!g_strcmp0(name, "context-menu-action-7")) { prisel = gtk_clipboard_get(GDK_SELECTION_PRIMARY); clpbrd = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); uri = gtk_clipboard_wait_for_text(clpbrd); if (uri) gtk_clipboard_set_text(prisel, uri, -1); } } void pasteuri(GtkClipboard *clipboard, const char *text, gpointer d) { Arg arg = {.v = text }; if (text != NULL) loaduri((Client *) d, &arg); } void print(Client *c, const Arg *arg) { webkit_web_frame_print(webkit_web_view_get_main_frame(c->view)); } GdkFilterReturn processx(GdkXEvent *e, GdkEvent *event, gpointer d) { Client *c = (Client *)d; XPropertyEvent *ev; Arg arg; if (((XEvent *)e)->type == PropertyNotify) { ev = &((XEvent *)e)->xproperty; if (ev->state == PropertyNewValue) { if (ev->atom == atoms[AtomFind]) { arg.b = TRUE; find(c, &arg); return GDK_FILTER_REMOVE; } else if (ev->atom == atoms[AtomGo]) { arg.v = getatom(c, AtomGo); loaduri(c, &arg); return GDK_FILTER_REMOVE; } } } return GDK_FILTER_CONTINUE; } void progresschange(WebKitWebView *view, GParamSpec *pspec, Client *c) { c->progress = webkit_web_view_get_progress(c->view) * 100; updatetitle(c); } void linkopen(Client *c, const Arg *arg) { newwindow(NULL, arg, 1); } void linkopenembed(Client *c, const Arg *arg) { newwindow(NULL, arg, 0); } void reload(Client *c, const Arg *arg) { gboolean nocache = *(gboolean *)arg; if (nocache) webkit_web_view_reload_bypass_cache(c->view); else webkit_web_view_reload(c->view); } void scroll_h(Client *c, const Arg *arg) { scroll(gtk_scrolled_window_get_hadjustment( GTK_SCROLLED_WINDOW(c->scroll)), arg); } void scroll_v(Client *c, const Arg *arg) { scroll(gtk_scrolled_window_get_vadjustment( GTK_SCROLLED_WINDOW(c->scroll)), arg); } void scroll(GtkAdjustment *a, const Arg *arg) { gdouble v; v = gtk_adjustment_get_value(a); switch (arg->i) { case +10000: case -10000: v += gtk_adjustment_get_page_increment(a) * (arg->i / 10000); break; case +20000: case -20000: default: v += gtk_adjustment_get_step_increment(a) * arg->i; } v = MAX(v, 0.0); v = MIN(v, gtk_adjustment_get_upper(a) - gtk_adjustment_get_page_size(a)); gtk_adjustment_set_value(a, v); } void setatom(Client *c, int a, const char *v) { XSync(dpy, False); XChangeProperty(dpy, GDK_WINDOW_XID(GTK_WIDGET(c->win)->window), atoms[a], XA_STRING, 8, PropModeReplace, (unsigned char *)v, strlen(v) + 1); } void setup(void) { int i; char *proxy, *new_proxy, *no_proxy, **new_no_proxy; char *styledirfile, *stylepath; GProxyResolver *pr; SoupSession *s; GError *error = NULL; /* clean up any zombies immediately */ sigchld(0); gtk_init(NULL, NULL); dpy = GDK_DISPLAY(); /* atoms */ atoms[AtomFind] = XInternAtom(dpy, "_SURF_FIND", False); atoms[AtomGo] = XInternAtom(dpy, "_SURF_GO", False); atoms[AtomUri] = XInternAtom(dpy, "_SURF_URI", False); /* dirs and files */ cookiefile = buildfile(cookiefile); scriptfile = buildfile(scriptfile); cachefolder = buildpath(cachefolder); if (stylefile == NULL) { styledir = buildpath(styledir); for (i = 0; i < LENGTH(styles); i++) { if (regcomp(&(styles[i].re), styles[i].regex, REG_EXTENDED)) { fprintf(stderr, "Could not compile regex: %s\n", styles[i].regex); styles[i].regex = NULL; } styledirfile = g_strconcat(styledir, "/", styles[i].style, NULL); stylepath = buildfile(styledirfile); styles[i].style = g_strconcat("file://", stylepath, NULL); g_free(styledirfile); g_free(stylepath); } g_free(styledir); } else { stylepath = buildfile(stylefile); stylefile = g_strconcat("file://", stylepath, NULL); g_free(stylepath); } /* request handler */ s = webkit_get_default_session(); /* cookie jar */ soup_session_add_feature(s, SOUP_SESSION_FEATURE(cookiejar_new(cookiefile, FALSE, cookiepolicy_get()))); /* disk cache */ if (enablediskcache) { diskcache = soup_cache_new(cachefolder, SOUP_CACHE_SINGLE_USER); soup_cache_set_max_size(diskcache, diskcachebytes); soup_cache_load(diskcache); soup_session_add_feature(s, SOUP_SESSION_FEATURE(diskcache)); } /* ssl */ tlsdb = g_tls_file_database_new(cafile, &error); if (error) { g_warning("Error loading SSL database %s: %s", cafile, error->message); g_error_free(error); } g_object_set(G_OBJECT(s), "tls-database", tlsdb, NULL); g_object_set(G_OBJECT(s), "ssl-strict", strictssl, NULL); /* proxy */ if ((proxy = getenv("http_proxy")) && strcmp(proxy, "")) { new_proxy = g_strrstr(proxy, "http://") || g_strrstr(proxy, "https://") || g_strrstr(proxy, "socks://") || g_strrstr(proxy, "socks4://") || g_strrstr(proxy, "socks4a://") || g_strrstr(proxy, "socks5://") ? g_strdup(proxy) : g_strdup_printf("http://%s", proxy); new_no_proxy = ((no_proxy = getenv("no_proxy")) && strcmp(no_proxy, "")) ? g_strsplit(no_proxy, ",", -1) : NULL; pr = g_simple_proxy_resolver_new(new_proxy, new_no_proxy); g_object_set(G_OBJECT(s), "proxy-resolver", pr, NULL); g_free(new_proxy); g_strfreev(new_no_proxy); usingproxy = 1; } } void sigchld(int unused) { if (signal(SIGCHLD, sigchld) == SIG_ERR) die("Can't install SIGCHLD handler"); while (0 < waitpid(-1, NULL, WNOHANG)); } void source(Client *c, const Arg *arg) { Arg a = { .b = FALSE }; gboolean s; s = webkit_web_view_get_view_source_mode(c->view); webkit_web_view_set_view_source_mode(c->view, !s); reload(c, &a); } void spawn(Client *c, const Arg *arg) { if (fork() == 0) { if (dpy) close(ConnectionNumber(dpy)); setsid(); execvp(((char **)arg->v)[0], (char **)arg->v); fprintf(stderr, "surf: execvp %s", ((char **)arg->v)[0]); perror(" failed"); exit(0); } } void eval(Client *c, const Arg *arg) { WebKitWebFrame *frame = webkit_web_view_get_main_frame(c->view); evalscript(webkit_web_frame_get_global_context(frame), ((char **)arg->v)[0], ""); } void stop(Client *c, const Arg *arg) { webkit_web_view_stop_loading(c->view); } void titlechange(WebKitWebView *view, GParamSpec *pspec, Client *c) { const gchar *t = webkit_web_view_get_title(view); if (t) { c->title = copystr(&c->title, t); updatetitle(c); } } void titlechangeleave(void *a, void *b, Client *c) { c->linkhover = NULL; updatetitle(c); } void toggle(Client *c, const Arg *arg) { WebKitWebSettings *settings; char *name = (char *)arg->v; gboolean value; Arg a = { .b = FALSE }; settings = webkit_web_view_get_settings(c->view); g_object_get(G_OBJECT(settings), name, &value, NULL); g_object_set(G_OBJECT(settings), name, !value, NULL); reload(c, &a); } void togglecookiepolicy(Client *c, const Arg *arg) { SoupCookieJar *jar; SoupCookieJarAcceptPolicy policy; jar = SOUP_COOKIE_JAR(soup_session_get_feature( webkit_get_default_session(), SOUP_TYPE_COOKIE_JAR)); g_object_get(G_OBJECT(jar), "accept-policy", &policy, NULL); policysel++; if (policysel >= strlen(cookiepolicies)) policysel = 0; g_object_set(G_OBJECT(jar), "accept-policy", cookiepolicy_get(), NULL); updatetitle(c); /* Do not reload. */ } void togglegeolocation(Client *c, const Arg *arg) { Arg a = { .b = FALSE }; allowgeolocation ^= 1; reload(c, &a); } void twitch(Client *c, const Arg *arg) { GtkAdjustment *a; gdouble v; a = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW( c->scroll)); v = gtk_adjustment_get_value(a); v += arg->i; v = MAX(v, 0.0); v = MIN(v, gtk_adjustment_get_upper(a) - gtk_adjustment_get_page_size(a)); gtk_adjustment_set_value(a, v); } void togglescrollbars(Client *c, const Arg *arg) { GtkPolicyType vspolicy; Arg a; gtk_scrolled_window_get_policy(GTK_SCROLLED_WINDOW(c->scroll), NULL, &vspolicy); if (vspolicy == GTK_POLICY_AUTOMATIC) { gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(c->scroll), GTK_POLICY_NEVER, GTK_POLICY_NEVER); } else { gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(c->scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); a.i = +1; twitch(c, &a); a.i = -1; twitch(c, &a); } } void togglestyle(Client *c, const Arg *arg) { enablestyle = !enablestyle; setstyle(c, enablestyle ? getstyle(geturi(c)) : ""); updatetitle(c); } void gettogglestat(Client *c) { gboolean value; int p = 0; WebKitWebSettings *settings = webkit_web_view_get_settings(c->view); togglestat[p++] = cookiepolicy_set(cookiepolicy_get()); g_object_get(G_OBJECT(settings), "enable-caret-browsing", &value, NULL); togglestat[p++] = value? 'C': 'c'; togglestat[p++] = allowgeolocation? 'G': 'g'; togglestat[p++] = enablediskcache? 'D': 'd'; g_object_get(G_OBJECT(settings), "auto-load-images", &value, NULL); togglestat[p++] = value? 'I': 'i'; g_object_get(G_OBJECT(settings), "enable-scripts", &value, NULL); togglestat[p++] = value? 'S': 's'; g_object_get(G_OBJECT(settings), "enable-plugins", &value, NULL); togglestat[p++] = value? 'V': 'v'; togglestat[p++] = enablestyle ? 'M': 'm'; togglestat[p] = '\0'; } void getpagestat(Client *c) { const char *uri = geturi(c); if (strstr(uri, "https://") == uri) pagestat[0] = c->sslfailed ? 'U' : 'T'; else pagestat[0] = '-'; pagestat[1] = usingproxy ? 'P' : '-'; pagestat[2] = '\0'; } void updatetitle(Client *c) { char *t; if (showindicators) { gettogglestat(c); getpagestat(c); if (c->linkhover) { t = g_strdup_printf("%s:%s | %s", togglestat, pagestat, c->linkhover); } else if (c->progress != 100) { t = g_strdup_printf("[%i%%] %s:%s | %s", c->progress, togglestat, pagestat, c->title == NULL ? "" : c->title); } else { t = g_strdup_printf("%s:%s | %s", togglestat, pagestat, c->title == NULL ? "" : c->title); } gtk_window_set_title(GTK_WINDOW(c->win), t); g_free(t); } else { gtk_window_set_title(GTK_WINDOW(c->win), (c->title == NULL) ? "" : c->title); } } void updatewinid(Client *c) { snprintf(winid, LENGTH(winid), "%u", (int)GDK_WINDOW_XID(GTK_WIDGET(c->win)->window)); } void usage(void) { die("usage: %s [-bBdDfFgGiIkKmMnNpPsSvx] [-a cookiepolicies ] " "[-c cookiefile] [-e xid] [-r scriptfile] [-t stylefile] " "[-u useragent] [-z zoomlevel] [uri]\n", basename(argv0)); } void windowobjectcleared(GtkWidget *w, WebKitWebFrame *frame, JSContextRef js, JSObjectRef win, Client *c) { runscript(frame); } void zoom(Client *c, const Arg *arg) { c->zoomed = TRUE; if (arg->i < 0) { /* zoom out */ webkit_web_view_zoom_out(c->view); } else if (arg->i > 0) { /* zoom in */ webkit_web_view_zoom_in(c->view); } else { /* reset */ c->zoomed = FALSE; webkit_web_view_set_zoom_level(c->view, 1.0); } } int main(int argc, char *argv[]) { Arg arg; Client *c; memset(&arg, 0, sizeof(arg)); /* command line args */ ARGBEGIN { case 'a': cookiepolicies = EARGF(usage()); break; case 'b': enablescrollbars = 0; break; case 'B': enablescrollbars = 1; break; case 'c': cookiefile = EARGF(usage()); break; case 'd': enablediskcache = 0; break; case 'D': enablediskcache = 1; break; case 'e': embed = strtol(EARGF(usage()), NULL, 0); break; case 'f': runinfullscreen = 0; break; case 'F': runinfullscreen = 1; break; case 'g': allowgeolocation = 0; break; case 'G': allowgeolocation = 1; break; case 'i': loadimages = 0; break; case 'I': loadimages = 1; break; case 'k': kioskmode = 0; break; case 'K': kioskmode = 1; break; case 'm': enablestyle = 0; break; case 'M': enablestyle = 1; break; case 'n': enableinspector = 0; break; case 'N': enableinspector = 1; break; case 'p': enableplugins = 0; break; case 'P': enableplugins = 1; break; case 'r': scriptfile = EARGF(usage()); break; case 's': enablescripts = 0; break; case 'S': enablescripts = 1; break; case 't': stylefile = EARGF(usage()); break; case 'u': useragent = EARGF(usage()); break; case 'v': die("surf-"VERSION", ©2009-2015 surf engineers, " "see LICENSE for details\n"); case 'x': showxid = TRUE; break; case 'z': zoomlevel = strtof(EARGF(usage()), NULL); break; default: usage(); } ARGEND; if (argc > 0) arg.v = argv[0]; setup(); c = newclient(); if (arg.v) loaduri(clients, &arg); else updatetitle(c); gtk_main(); cleanup(); return EXIT_SUCCESS; } surf-0.7/surf.png000066400000000000000000000003601263527032200140250ustar00rootroot00000000000000PNG  IHDRW(r;sRGBPLTEgtRNS@fbKGDH pHYs  tEXtCommentCreated with GIMPWtIME 9'A1IDATӵͱ 0<#]]gi FV<> D^ >_Cɋ\@IENDB`