#include <sys/file.h>
#define LENGTH(x) (sizeof x / sizeof x[0])
+#define CLEANMASK(mask) (mask & (GDK_SHIFT_MASK|GDK_CONTROL_MASK|GDK_MOD1_MASK|GDK_MOD2_MASK|GDK_MOD3_MASK|GDK_MOD4_MASK|GDK_MOD5_MASK))
enum { AtomFind, AtomGo, AtomUri, AtomLast };
char *title, *linkhover;
const char *uri, *needle;
gint progress;
+ gboolean sslfailed;
struct Client *next;
gboolean zoomed;
} Client;
static gboolean loadimage = 1, plugin = 1, script = 1;
static char *buildpath(const char *path);
+static gboolean buttonrelease(WebKitWebView *web, GdkEventButton *e, GList *gl);
static void cleanup(void);
static void clipboard(Client *c, const Arg *arg);
static char *copystr(char **str, const char *src);
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);
+static void newwindow(Client *c, const Arg *arg, gboolean noembed);
static void newrequest(SoupSession *s, SoupMessage *msg, gpointer v);
static void pasteuri(GtkClipboard *clipboard, const char *text, gpointer d);
+static void populatepopup(WebKitWebView *web, GtkMenu *menu, Client *c);
+static void popupactivate(GtkMenuItem *menu, Client *);
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 reload(Client *c, const Arg *arg);
-static void resize(GtkWidget *w, GtkAllocation *a, Client *c);
-static void scroll(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 setcookie(SoupCookie *c);
static void setup(void);
apath = g_strconcat(g_get_home_dir(), "/", path, NULL);
if((p = strrchr(apath, '/'))) {
*p = '\0';
- g_mkdir_with_parents(apath, 0755);
+ g_mkdir_with_parents(apath, 0700);
+ g_chmod(apath, 0700); /* in case it existed */
*p = '/';
}
/* creating file (gives error when apath ends with "/") */
- if((f = fopen(apath, "a")))
+ if((f = fopen(apath, "a"))) {
+ g_chmod(apath, 0600); /* always */
fclose(f);
+ }
return apath;
}
+static gboolean
+buttonrelease(WebKitWebView *web, GdkEventButton *e, GList *gl) {
+ WebKitHitTestResultContext context;
+ WebKitHitTestResult *result = webkit_web_view_get_hit_test_result(web, e);
+ Arg arg;
+
+ g_object_get(result, "context", &context, NULL);
+ if(context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK) {
+ if(e->button == 2) {
+ g_object_get(result, "link-uri", &arg.v, NULL);
+ newwindow(NULL, &arg, e->state & GDK_CONTROL_MASK);
+ return true;
+ }
+ }
+ return false;
+}
+
void
cleanup(void) {
while(clients)
}
void
-evalscript(WebKitWebFrame *frame, JSContextRef js, char *script, char* scriptname) {
+evalscript(JSContextRef js, char *script, char* scriptname) {
JSStringRef jsscript, jsscriptname;
JSValueRef exception = NULL;
}
void
-runscript(WebKitWebFrame *frame, JSContextRef js) {
+runscript(WebKitWebFrame *frame) {
char *script;
GError *error;
if(g_file_get_contents(scriptfile, &script, NULL, &error)) {
- evalscript(frame, webkit_web_frame_get_global_context(frame), script, scriptfile);
+ evalscript(webkit_web_frame_get_global_context(frame), script, scriptfile);
}
}
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);
+ newwindow(NULL, &arg, 0);
return TRUE;
}
return FALSE;
w = c->indicator;
width = c->progress * w->allocation.width / 100;
gc = gdk_gc_new(w->window);
- gdk_color_parse(strstr(uri, "https://") == uri ?
- progress_trust : progress, &fg);
+ if(strstr(uri, "https://") == uri)
+ gdk_color_parse(c->sslfailed ?
+ progress_untrust : progress_trust, &fg);
+ else
+ gdk_color_parse(progress, &fg);
gdk_gc_set_rgb_fg_color(gc, &fg);
gdk_draw_rectangle(w->window,
w->style->bg_gc[GTK_WIDGET_STATE(w)],
void
gotheaders(SoupMessage *msg, gpointer v) {
- SoupURI *uri;
GSList *l, *p;
- uri = soup_message_get_uri(msg);
for(p = l = soup_cookies_from_response(msg); p;
p = g_slist_next(p)) {
setcookie((SoupCookie *)p->data);
updatewinid(c);
for(i = 0; i < LENGTH(keys); i++) {
if(gdk_keyval_to_lower(ev->keyval) == keys[i].keyval
- && (ev->state & keys[i].mod) == keys[i].mod
+ && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)
&& keys[i].func) {
keys[i].func(c, &(keys[i].arg));
processed = TRUE;
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:
- setatom(c, AtomUri, geturi(c));
+ 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);
break;
case WEBKIT_LOAD_FINISHED:
- c->progress = 0;
+ c->progress = 100;
update(c);
break;
default:
* 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");
+ 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_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), "key-press-event", G_CALLBACK(keypress), c);
- g_signal_connect(G_OBJECT(c->win), "size-allocate", G_CALLBACK(resize), c);
/* VBox */
c->vbox = gtk_vbox_new(FALSE, 0);
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), "populate-popup", G_CALLBACK(populatepopup), c);
/* Indicator */
c->indicator = gtk_drawing_area_new();
gdk_window_add_filter(GTK_WIDGET(c->win)->window, processx, c);
webkit_web_view_set_full_content_zoom(c->view, TRUE);
frame = webkit_web_view_get_main_frame(c->view);
- runscript(frame, webkit_web_frame_get_global_context(frame));
+ runscript(frame);
settings = webkit_web_view_get_settings(c->view);
if(!(ua = getenv("SURF_USERAGENT")))
ua = useragent;
g_object_set(G_OBJECT(settings), "auto-load-images", loadimage, NULL);
g_object_set(G_OBJECT(settings), "enable-plugins", plugin, NULL);
g_object_set(G_OBJECT(settings), "enable-scripts", script, NULL);
- g_object_set(G_OBJECT(settings), "enable-spatial-navigation", true, NULL);
+ g_object_set(G_OBJECT(settings), "enable-spatial-navigation", SPATIAL_BROWSING, NULL);
g_free(uri);
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) {
+newwindow(Client *c, const Arg *arg, gboolean noembed) {
guint i = 0;
const char *cmd[10], *uri;
const Arg a = { .v = (void *)cmd };
char tmp[64];
cmd[i++] = progname;
- if(embed) {
+ if(embed && !noembed) {
cmd[i++] = "-e";
snprintf(tmp, LENGTH(tmp), "%u\n", (int)embed);
cmd[i++] = tmp;
spawn(NULL, &a);
}
+static void
+populatepopup(WebKitWebView *web, GtkMenu *menu, Client *c) {
+ GList *items = gtk_container_get_children(GTK_CONTAINER(menu));
+
+ for(GList *l = items; l; l = l->next) {
+ g_signal_connect(l->data, "activate", G_CALLBACK(popupactivate), c);
+ }
+
+ g_list_free(items);
+}
+
+static void
+popupactivate(GtkMenuItem *menu, 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-13 reload
+ * context-menu-action-10 back
+ * context-menu-action-11 forward
+ * context-menu-action-12 stop
+ */
+
+ GtkAction *a = NULL;
+ const char *name;
+ GtkClipboard *prisel;
+
+ a = gtk_activatable_get_related_action(GTK_ACTIVATABLE(menu));
+ 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);
+ }
+}
+
void
pasteuri(GtkClipboard *clipboard, const char *text, gpointer d) {
Arg arg = {.v = text };
}
void
-resize(GtkWidget *w, GtkAllocation *a, Client *c) {
- double zoom;
+scroll_h(Client *c, const Arg *arg) {
+ scroll(gtk_scrolled_window_get_hadjustment(
+ GTK_SCROLLED_WINDOW(c->scroll)), arg);
+}
- if(c->zoomed)
- return;
- zoom = webkit_web_view_get_zoom_level(c->view);
- if(a->width * a->height < 300 * 400 && zoom != 0.2)
- webkit_web_view_set_zoom_level(c->view, 0.2);
- else if(zoom != 1.0)
- webkit_web_view_set_zoom_level(c->view, 1.0);
+void
+scroll_v(Client *c, const Arg *arg) {
+ scroll(gtk_scrolled_window_get_vadjustment(
+ GTK_SCROLLED_WINDOW(c->scroll)), arg);
}
void
-scroll(Client *c, const Arg *arg) {
+scroll(GtkAdjustment *a, const Arg *arg) {
gdouble v;
- GtkAdjustment *a;
- a = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(c->scroll));
v = gtk_adjustment_get_value(a);
- v += gtk_adjustment_get_step_increment(a) * arg->i;
+ 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));
+ v = MIN(v, gtk_adjustment_get_upper(a) -
+ gtk_adjustment_get_page_size(a));
gtk_adjustment_set_value(a, v);
}
soup_session_remove_feature_by_type(s, soup_cookie_jar_get_type());
g_signal_connect_after(G_OBJECT(s), "request-started", G_CALLBACK(newrequest), NULL);
+ /* ssl */
+ g_object_set(G_OBJECT(s), "ssl-ca-file", cafile, 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_strdup(proxy) :
void
eval(Client *c, const Arg *arg) {
WebKitWebFrame *frame = webkit_web_view_get_main_frame(c->view);
- evalscript(frame, webkit_web_frame_get_global_context(frame), ((char **)arg->v)[0], "");
+ evalscript(webkit_web_frame_get_global_context(frame),
+ ((char **)arg->v)[0], "");
}
void
update(Client *c) {
char *t;
- if(c->progress != 100)
- t = g_strdup_printf("[%i%%] %s", c->progress, c->title);
- else if(c->linkhover)
+ if(c->linkhover)
t = g_strdup(c->linkhover);
+ else if(c->progress != 100)
+ t = g_strdup_printf("[%i%%] %s", c->progress, c->title);
else
t = g_strdup(c->title);
drawindicator(c);
void
windowobjectcleared(GtkWidget *w, WebKitWebFrame *frame, JSContextRef js, JSObjectRef win, Client *c) {
- runscript(frame, js);
+ runscript(frame);
}
void
switch(argv[i][1]) {
case 'e':
if(++i < argc)
- embed = atoi(argv[i]);
+ embed = strtol(argv[i], NULL, 0);
else
usage();
break;
showxid = TRUE;
break;
case 'v':
- die("surf-"VERSION", © 2009 surf engineers, see LICENSE for details\n");
+ die("surf-"VERSION", ©2009-2012 surf engineers, see LICENSE for details\n");
default:
usage();
}