diff --git a/config.def.h b/config.def.h
index e3b9881..591a668 100644
--- a/config.def.h
+++ b/config.def.h
@@ -1,10 +1,11 @@
 /* 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 *useragent      = NULL;
 static char *scriptfile     = "~/.surf/script.js";
 static char *styledir       = "~/.surf/styles/";
 static char *cachefolder    = "~/.surf/cache/";
+static char *dbfolder       = "~/.surf/databases/";
+static char *origincachefolder = "~/.surf/origins/%s/cache/";
+static char *origindbfolder = "~/.surf/origins/%s/databases/";
 
 static Bool kioskmode       = FALSE; /* Ignore shortcuts */
 static Bool showindicators  = TRUE;  /* Show indicators in window title */
@@ -16,6 +17,7 @@ static gfloat zoomlevel = 1.0;       /* Default zoom level */
 
 /* Soup default features */
 static char *cookiefile     = "~/.surf/cookies.txt";
+static char *origincookiefile = "~/.surf/origins/%s/cookies.txt";
 static char *cookiepolicies = "Aa@"; /* A: accept all; a: accept nothing,
                                         @: accept no third party */
 static char *cafile         = "/etc/ssl/certs/ca-certificates.crt";
@@ -34,22 +36,29 @@ static Bool enablestyles          = TRUE;
 static Bool loadimages            = TRUE;
 static Bool hidebackground        = FALSE;
 static Bool allowgeolocation      = TRUE;
+static Bool sameoriginpolicy      = TRUE;
+static char *originprompt         = "Crossing from %s to";
 
-#define SETPROP(p, q) { \
+#define SETPROP(p, q, prompt) { \
 	.v = (char *[]){ "/bin/sh", "-c", \
-		"prop=\"`xprop -id $2 $0 | cut -d '\"' -f 2 | xargs -0 printf %b | dmenu`\" &&" \
+		"prop=\"`xprop -id $2 $0 | cut -d '\"' -f 2 | xargs -0 printf %b | dmenu -p \"$3\"`\" &&" \
 		"xprop -id $2 -f $1 8s -set $1 \"$prop\"", \
-		p, q, winid, NULL \
+		p, q, winid, prompt, 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 \
+		"D=\"`printf \"${PWD}\n${HOME}\n${HOME}/Downloads\n\" | dmenu -p 'Save into' -l 3`\" &&" \
+		"mkdir -p \"${D}\" && cd \"${D}\" &&" \
+		"N=\"`basename \"$0\"`\" &&" \
+		"F=\"`echo \"${N}\" | dmenu -p 'Save as'`\" &&" \
+		"st -e /bin/sh -c \"" \
+			"curl -L -J -o '$F' --user-agent '$1' --referer '$2' -b $3 -c $3 '$0';" \
+			"echo 'Press Enter to close window...'; read y" \
+		"\"", \
+		d, (useragent ? useragent : ""), r, cookiefile, NULL \
 	} \
 }
 
@@ -110,9 +119,9 @@ static Key keys[] = {
     { 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_g,      spawn,      SETPROP("_SURF_URI", "_SURF_GO", "Go to") },
+    { MODKEY,               GDK_f,      spawn,      SETPROP("_SURF_FIND", "_SURF_FIND", "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 } },
diff --git a/surf.c b/surf.c
index 48f6b39..ab10990 100644
--- a/surf.c
+++ b/surf.c
@@ -35,6 +35,7 @@ char *argv0;
 #define COOKIEJAR(obj)          (G_TYPE_CHECK_INSTANCE_CAST ((obj), COOKIEJAR_TYPE, CookieJar))
 
 enum { AtomFind, AtomGo, AtomUri, AtomLast };
+enum NavTransparency { NavExplicit, NavImplicit };
 
 typedef union Arg Arg;
 union Arg {
@@ -85,12 +86,16 @@ static GdkNativeWindow embed = 0;
 static gboolean showxid = FALSE;
 static char winid[64];
 static gboolean usingproxy = 0;
-static char togglestat[9];
+static char togglestat[10];
 static char pagestat[3];
 static GTlsDatabase *tlsdb;
 static int policysel = 0;
 static char *stylefile = NULL;
+static char *origin_uri = NULL;
+static char *referring_origin = NULL;
 static SoupCache *diskcache = NULL;
+static gboolean hasloaded = false;
+static gboolean hasvisual = false;
 
 static void addaccelgroup(Client *c);
 static void beforerequest(WebKitWebView *w, WebKitWebFrame *f,
@@ -119,6 +124,9 @@ static WebKitWebView *createwindow(WebKitWebView *v, WebKitWebFrame *f,
 static gboolean decidedownload(WebKitWebView *v, WebKitWebFrame *f,
 		WebKitNetworkRequest *r, gchar *m,  WebKitWebPolicyDecision *p,
 		Client *c);
+static gboolean decidenavigation(WebKitWebView *v, WebKitWebFrame *f,
+		WebKitNetworkRequest *r, WebKitWebNavigationAction *n,
+		WebKitWebPolicyDecision *p, Client *c);
 static gboolean decidewindow(WebKitWebView *v, WebKitWebFrame *f,
 		WebKitNetworkRequest *r, WebKitWebNavigationAction *n,
 		WebKitWebPolicyDecision *p, Client *c);
@@ -156,10 +164,16 @@ 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 loaduri(Client *c, const Arg *arg, const enum NavTransparency navtrans);
 static void navigate(Client *c, const Arg *arg);
 static Client *newclient(void);
-static void newwindow(Client *c, const Arg *arg, gboolean noembed);
+static void newwindow(Client *c, const Arg *arg, gboolean noembed, const enum NavTransparency navtrans);
+static int origincmp(const char *uri1, const char *uri2);
+static int originhas(const char *uri);
+static const char *origingetproto(const char *uri);
+static char *origingethost(const char *uri);
+static char *origingeturi(const char *uri);
+static int originmatch(const char *uri1, const char *uri2);
 static void pasteuri(GtkClipboard *clipboard, const char *text, gpointer d);
 static gboolean contextmenu(WebKitWebView *view, GtkWidget *menu,
 		WebKitHitTestResult *target, gboolean keyboard, Client *c);
@@ -173,11 +187,16 @@ 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 setup(const char *uri_arg);
 static void sigchld(int unused);
 static void source(Client *c, const Arg *arg);
 static void spawn(Client *c, const Arg *arg);
+static int strrand(char *buf, int buflen);
+static gchar *strentropy();
+static gchar *strlangentropy();
 static void stop(Client *c, const Arg *arg);
+static void acceptlanguagescramble();
+static void useragentscramble(WebKitWebView *view);
 static void titlechange(WebKitWebView *view, GParamSpec *pspec, Client *c);
 static void toggle(Client *c, const Arg *arg);
 static void togglecookiepolicy(Client *c, const Arg *arg);
@@ -187,6 +206,7 @@ static void togglestyle(Client *c, const Arg *arg);
 static void updatetitle(Client *c);
 static void updatewinid(Client *c);
 static void usage(void);
+char *qualify_uri(const char *uri);
 static void windowobjectcleared(GtkWidget *w, WebKitWebFrame *frame,
 		JSContextRef js, JSObjectRef win, Client *c);
 static void zoom(Client *c, const Arg *arg);
@@ -283,7 +303,7 @@ buttonrelease(WebKitWebView *web, GdkEventButton *e, GList *gl) {
 		if(e->button == 2 ||
 				(e->button == 1 && CLEANMASK(e->state) == CLEANMASK(MODKEY))) {
 			g_object_get(result, "link-uri", &arg.v, NULL);
-			newwindow(NULL, &arg, e->state & GDK_CONTROL_MASK);
+			newwindow(NULL, &arg, e->state & GDK_CONTROL_MASK, NavImplicit);
 			return true;
 		}
 	}
@@ -445,7 +465,7 @@ createwindow(WebKitWebView  *v, WebKitWebFrame *f, Client *c) {
 static gboolean
 decidedownload(WebKitWebView *v, WebKitWebFrame *f, WebKitNetworkRequest *r,
 		gchar *m,  WebKitWebPolicyDecision *p, Client *c) {
-	if(!webkit_web_view_can_show_mime_type(v, m)) {
+	if(!webkit_web_view_can_show_mime_type(v, m) && !webkit_web_frame_get_parent(f)) {
 		webkit_web_policy_decision_download(p);
 		return TRUE;
 	}
@@ -453,6 +473,43 @@ decidedownload(WebKitWebView *v, WebKitWebFrame *f, WebKitNetworkRequest *r,
 }
 
 static gboolean
+decidenavigation(WebKitWebView *view, WebKitWebFrame *f, WebKitNetworkRequest *r,
+		WebKitWebNavigationAction *n, WebKitWebPolicyDecision *p,
+		Client *c) {
+	const char *uri = webkit_network_request_get_uri(r);
+	Arg arg;
+
+	if (!useragent) {
+		useragentscramble(view);
+	}
+	acceptlanguagescramble();
+
+	if (!sameoriginpolicy) {
+		/* configured to not bother isolating origins */
+		return FALSE;
+	} else if (webkit_web_frame_get_parent(f)) {
+		/* has a parent, and therefore not an origin */
+		return FALSE;
+	/* branches below operate on the origin, top-most frame */
+	} else if (uri && (uri[0] == '\0' || strcmp(uri, "about:blank") == 0)) {
+		/* nothing is really going to load */
+		return FALSE;
+	} else if (!hasloaded) {
+		/* we *are* the new window */
+		return FALSE;
+	} else if (!origin_uri || originmatch(uri, origin_uri)) {
+		/* origin matches */
+		return FALSE;
+	} else {
+		/* top-most frame, and origin differs -- isolate within a new process */
+		webkit_web_policy_decision_ignore(p);
+		arg.v = (void *) uri;
+		newwindow(NULL, &arg, 0, NavImplicit);
+		return TRUE;
+	}
+}
+
+static gboolean
 decidewindow(WebKitWebView *view, WebKitWebFrame *f, WebKitNetworkRequest *r,
 		WebKitWebNavigationAction *n, WebKitWebPolicyDecision *p,
 		Client *c) {
@@ -462,7 +519,7 @@ decidewindow(WebKitWebView *view, WebKitWebFrame *f, WebKitNetworkRequest *r,
 			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);
+		newwindow(NULL, &arg, 0, NavImplicit);
 		return TRUE;
 	}
 	return FALSE;
@@ -565,7 +622,7 @@ geturi(Client *c) {
 	char *uri;
 
 	if(!(uri = (char *)webkit_web_view_get_uri(c->view)))
-		uri = "about:blank";
+		uri = "";
 	return uri;
 }
 
@@ -695,8 +752,14 @@ loadstatuschange(WebKitWebView *view, GParamSpec *pspec, Client *c) {
 	char *uri;
 
 	switch(webkit_web_view_get_load_status (c->view)) {
+	case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT:
+		hasvisual = true;
+		break;
 	case WEBKIT_LOAD_COMMITTED:
 		uri = geturi(c);
+		if (strcmp(uri, "about:blank") != 0) {
+			origin_uri = uri;
+		}
 		if(strstr(uri, "https://") == uri) {
 			frame = webkit_web_view_get_main_frame(c->view);
 			src = webkit_web_frame_get_data_source(frame);
@@ -725,38 +788,49 @@ loadstatuschange(WebKitWebView *view, GParamSpec *pspec, Client *c) {
 	}
 }
 
-static void
-loaduri(Client *c, const Arg *arg) {
-	char *u = NULL, *rp;
-	const char *uri = (char *)arg->v;
-	Arg a = { .b = FALSE };
+/* needs to be g_free()'d by caller */
+char *
+qualify_uri(const char *uri) {
+	char *qualified_uri = NULL, *rp;
 	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);
+		qualified_uri = g_strdup_printf("file://%s", rp);
 		free(rp);
 	} else {
-		u = g_strrstr(uri, "://") ? g_strdup(uri)
+		qualified_uri = g_strrstr(uri, "://") ? g_strdup(uri)
 			: g_strdup_printf("http://%s", uri);
 	}
 
-	setatom(c, AtomUri, uri);
+	return qualified_uri;
+}
+
+static void
+loaduri(Client *c, const Arg *arg, const enum NavTransparency navtrans) {
+	const char *uri = (char *)arg->v;
+	Arg a = { .b = FALSE };
+
+	if(strcmp(uri, "") == 0)
+		return;
+
+	if (!sameoriginpolicy || !origin_uri || !originhas(origin_uri) || originmatch(uri, origin_uri)) {
+		setatom(c, AtomUri, uri);
 
-	/* prevents endless loop */
-	if(strcmp(u, geturi(c)) == 0) {
-		reload(c, &a);
+		/* prevents endless loop */
+		if(strcmp(uri, geturi(c)) == 0) {
+			reload(c, &a);
+		} else {
+			webkit_web_view_load_uri(c->view, uri);
+			c->progress = 0;
+			hasloaded = true;
+			c->title = copystr(&c->title, uri);
+			updatetitle(c);
+		}
 	} else {
-		webkit_web_view_load_uri(c->view, u);
-		c->progress = 0;
-		c->title = copystr(&c->title, u);
-		updatetitle(c);
+		newwindow(NULL, arg, 0, navtrans);
 	}
-	g_free(u);
 }
 
 static void
@@ -780,6 +854,7 @@ newclient(void) {
 
 	c->title = NULL;
 	c->progress = 100;
+	hasloaded = false;
 
 	/* Window */
 	if(embed) {
@@ -835,6 +910,9 @@ newclient(void) {
 			"new-window-policy-decision-requested",
 			G_CALLBACK(decidewindow), c);
 	g_signal_connect(G_OBJECT(c->view),
+			"navigation-policy-decision-requested",
+			G_CALLBACK(decidenavigation), c);
+	g_signal_connect(G_OBJECT(c->view),
 			"mime-type-policy-decision-requested",
 			G_CALLBACK(decidedownload), c);
 	g_signal_connect(G_OBJECT(c->view),
@@ -900,6 +978,11 @@ newclient(void) {
 	runscript(frame);
 
 	settings = webkit_web_view_get_settings(c->view);
+	g_object_set(G_OBJECT(settings), "html5-local-storage-database-path", dbfolder, NULL);
+	g_object_set(G_OBJECT(settings), "enable-running-of-insecure-content", 0, NULL);
+	g_object_set(G_OBJECT(settings), "enable-offline-web-application-cache", 0, NULL);
+	g_object_set(G_OBJECT(settings), "enable-dns-prefetching", 0, NULL);
+
 	if(!(ua = getenv("SURF_USERAGENT")))
 		ua = useragent;
 	g_object_set(G_OBJECT(settings), "user-agent", ua, NULL);
@@ -924,6 +1007,11 @@ newclient(void) {
 	g_object_set(G_OBJECT(settings), "resizable-text-areas",
 			1, NULL);
 
+	if (!useragent) {
+		useragentscramble(c->view);
+	}
+	acceptlanguagescramble();
+
 	/*
 	 * 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.
@@ -983,11 +1071,12 @@ newclient(void) {
 }
 
 static void
-newwindow(Client *c, const Arg *arg, gboolean noembed) {
+newwindow(Client *c, const Arg *arg, gboolean noembed, const enum NavTransparency navtrans) {
 	guint i = 0;
-	const char *cmd[18], *uri;
+	const char *cmd[22], *uri;
 	const Arg a = { .v = (void *)cmd };
 	char tmp[64];
+	char *origin_packed = NULL;
 
 	cmd[i++] = argv0;
 	cmd[i++] = "-a";
@@ -1011,8 +1100,19 @@ newwindow(Client *c, const Arg *arg, gboolean noembed) {
 		cmd[i++] = "-s";
 	if(showxid)
 		cmd[i++] = "-x";
+	if(sameoriginpolicy)
+		cmd[i++] = "-O";
 	if(enablediskcache)
 		cmd[i++] = "-D";
+	if(navtrans == NavImplicit) {
+		cmd[i++] = "-R";
+		if (originhas(origin_uri)) {
+			origin_packed = origingeturi(origin_uri);
+		} else {
+			origin_packed = g_strdup("-");
+		}
+		cmd[i++] = origin_packed;
+	}
 	cmd[i++] = "-c";
 	cmd[i++] = cookiefile;
 	cmd[i++] = "--";
@@ -1021,6 +1121,13 @@ newwindow(Client *c, const Arg *arg, gboolean noembed) {
 		cmd[i++] = uri;
 	cmd[i++] = NULL;
 	spawn(NULL, &a);
+	g_free(origin_packed);
+	if (!hasvisual) {
+		if(dpy)
+			close(ConnectionNumber(dpy));
+
+		exit(0);
+	}
 }
 
 static gboolean
@@ -1071,11 +1178,112 @@ menuactivate(GtkMenuItem *item, Client *c) {
 	}
 }
 
+static int
+origincmp(const char *uri1, const char *uri2) {
+	/* Doesn't handle default ports, but otherwise should comply with RFC 6454, The Web Origin Concept. */
+	int c;
+	if        (g_str_has_prefix(uri1, "http://")  && g_str_has_prefix(uri2, "http://")) {
+		return strncmp(uri1 + strlen("http://"),  uri2 + strlen("http://"),  strcspn(uri1 + strlen("http://"),  "/?#"));
+	} else if (g_str_has_prefix(uri1, "https://") && g_str_has_prefix(uri2, "https://")) {
+		return strncmp(uri1 + strlen("https://"), uri2 + strlen("https://"), strcspn(uri1 + strlen("https://"), "/?#"));
+	} else {
+		c = strcmp(uri1, uri2);
+		if (c == 0) {
+			/* -1 when 0 to force a mismatch in originmatch() */
+			c = -1;
+		}
+		return c;
+	}
+}
+
+static int
+originhas(const char *uri) {
+	char *origin = origingethost(uri);
+	int has = origin != NULL;
+	free(origin);
+	return has;
+}
+
+static const char *
+origingetproto(const char *uri) {
+	if (g_str_has_prefix(uri, "http://")) {
+		return "http";
+	} else if (g_str_has_prefix(uri, "https://")) {
+		return "https";
+	} else {
+		return NULL;
+	}
+}
+
+/* caller must free() the return value, if not NULL */
+static char *
+origingethost(const char *uri) {
+	/* Doesn't handle default ports, but otherwise should comply with RFC 6454, The Web Origin Concept. */
+	char  *origin = NULL;
+	size_t spansize;
+	size_t spanstart;
+
+	if (       g_str_has_prefix(uri, "http://")) {
+		spanstart =       strlen("http://");
+	} else if (g_str_has_prefix(uri, "https://")) {
+		spanstart =       strlen("https://");
+	} else {
+		/* 
+		 * RFC 6454: this case should return a globally unique origin. 
+		 *
+		 * As long as processes are per-origin
+		 * (that is, new origins get a new process),
+		 * then relying only on process state provides this uniqueness,
+		 * since anything stored would be stored by an inaccessible key. 
+		 *
+		 * So, when the caller gets this error,
+		 * it should just bypass storage altogether.
+		 */
+		return NULL;
+	}
+
+	spansize = strcspn(uri + spanstart, "/?#");
+	if (spansize > 0 && uri[spanstart] == '.') {
+		/* kill attempt to traverse into parent folder */
+		return NULL;
+	}
+	origin = malloc(sizeof(char) * (spansize + 1));
+	if (origin) {
+		strncpy(origin, uri + spanstart, spansize);
+		origin[spansize] = '\0';
+		/* malloc()'d origin */
+		return origin;
+	} else {
+		/* ENOMEM set by malloc() */
+		return NULL;
+	}
+}
+
+/* caller must g_free() the return value, if not NULL */
+static char *
+origingeturi(const char *uri) {
+	const char *origin_proto = origingetproto(uri);
+	char *origin_host = origingethost(uri);
+	char *origin_packed = NULL;
+	if (origin_host) {
+		origin_packed = g_strdup_printf("%s://%s", origin_proto, origin_host);
+	}
+	g_free(origin_host);
+	return origin_packed;
+}
+
+static int
+originmatch(const char *uri1, const char *uri2) {
+	return origincmp(uri1, uri2) == 0;
+}
+
 static void
 pasteuri(GtkClipboard *clipboard, const char *text, gpointer d) {
-	Arg arg = {.v = text };
+	char *qualified_uri = qualify_uri(text);
+	Arg arg = {.v = qualified_uri };
 	if(text != NULL)
-		loaduri((Client *) d, &arg);
+		loaduri((Client *) d, &arg, NavExplicit);
+	g_free(qualified_uri);
 }
 
 static void
@@ -1088,6 +1296,8 @@ processx(GdkXEvent *e, GdkEvent *event, gpointer d) {
 	Client *c = (Client *)d;
 	XPropertyEvent *ev;
 	Arg arg;
+	const char *unqualified_uri = NULL;
+	char *qualified_uri = NULL;
 
 	if(((XEvent *)e)->type == PropertyNotify) {
 		ev = &((XEvent *)e)->xproperty;
@@ -1098,10 +1308,17 @@ processx(GdkXEvent *e, GdkEvent *event, gpointer d) {
 
 				return GDK_FILTER_REMOVE;
 			} else if(ev->atom == atoms[AtomGo]) {
-				arg.v = getatom(c, AtomGo);
-				loaduri(c, &arg);
-
-				return GDK_FILTER_REMOVE;
+				unqualified_uri = getatom(c, AtomGo);
+				if (unqualified_uri) {
+					qualified_uri = qualify_uri(unqualified_uri);
+					if (qualified_uri) {
+						arg.v = qualified_uri;
+						loaduri(c, &arg, NavExplicit);
+						g_free(qualified_uri);
+	
+						return GDK_FILTER_REMOVE;
+					}
+				}
 			}
 		}
 	}
@@ -1165,13 +1382,16 @@ setatom(Client *c, int a, const char *v) {
 	XChangeProperty(dpy, GDK_WINDOW_XID(GTK_WIDGET(c->win)->window),
 			atoms[a], XA_STRING, 8, PropModeReplace,
 			(unsigned char *)v, strlen(v) + 1);
+	XSync(dpy, False);
 }
 
 static void
-setup(void) {
+setup(const char *qualified_uri) {
 	int i;
 	char *proxy;
 	char *new_proxy;
+	char *origin;
+	char *originpath;
 	SoupURI *puri;
 	SoupSession *s;
 	GError *error = NULL;
@@ -1188,9 +1408,29 @@ setup(void) {
 	atoms[AtomUri] = XInternAtom(dpy, "_SURF_URI", False);
 
 	/* dirs and files */
-	cookiefile = buildpath(cookiefile);
+	if (sameoriginpolicy && qualified_uri && originhas(qualified_uri)) {
+		origin = origingethost(qualified_uri);
+
+		originpath = g_strdup_printf(origincookiefile, origin);
+		cookiefile = buildpath(originpath);
+		g_free(originpath);
+
+		originpath = g_strdup_printf(origincachefolder, origin);
+		cachefolder = buildpath(originpath);
+		g_free(originpath);
+
+		originpath = g_strdup_printf(origindbfolder, origin);
+		dbfolder = buildpath(originpath);
+		g_free(originpath);
+
+		free(origin);
+	} else {
+		cookiefile = buildpath(cookiefile);
+		cachefolder = buildpath(cachefolder);
+		dbfolder = buildpath(dbfolder);
+	}
+
 	scriptfile = buildpath(scriptfile);
-	cachefolder = buildpath(cachefolder);
 	styledir = buildpath(styledir);
 	if(stylefile == NULL) {
 		for(i = 0; i < LENGTH(styles); i++) {
@@ -1277,6 +1517,99 @@ spawn(Client *c, const Arg *arg) {
 	}
 }
 
+static int
+strrand(char *buf, int buflen) {
+	int fd;
+	int received;
+
+	fd = g_open("/dev/urandom", O_RDONLY, 0);
+	if (fd == -1) {
+		return -1;
+	}
+	received = read(fd, buf, buflen);
+	if (received != buflen) {
+		return -1;
+	}
+	return g_close(fd, NULL);
+}
+
+/* return value must be freed with g_free() */
+static gchar *
+strentropy() {
+	gchar *strname = NULL;
+	char byte, randbuf[256], namebuf[256];
+	int rand_i, name_i;
+
+	if (strrand(randbuf, sizeof (randbuf)) == -1) {
+		return NULL;
+	}
+
+	name_i = 0;
+	for (rand_i = 0; rand_i < sizeof (randbuf) && name_i < sizeof (namebuf); rand_i++) {
+		byte = randbuf[rand_i];
+		if (byte >= ' ' - 5 && byte <= ' ') { /* space AND some characters below, to increase the number of spaces */
+			namebuf[name_i++] = ' ';
+		} else if ((byte >= 'A' && byte <= 'Z') || (byte >= 'a' && byte <= 'z') || (byte >= '0' && byte <= '9')) {
+			namebuf[name_i++] = byte;
+		} else {
+			/* some bytes are skipped, to randomly vary the length */
+		}
+	}
+	namebuf[name_i] = '\0';
+	strname = g_strndup(namebuf, sizeof(namebuf) - 1);
+
+	return strname;
+}
+
+/* return value must be freed with g_free() */
+static gchar *
+strlangentropy() {
+	gchar *strname = NULL;
+	char randbuf[4], langbuf[6];
+
+	if (strrand(randbuf, sizeof (randbuf)) == -1) {
+		return NULL;
+	}
+
+#define randbetween(low, high, randbyte) (low + ((unsigned char) randbyte % (high - low)))
+	langbuf[0] = randbetween('a', 'z', randbuf[0]); 
+	langbuf[1] = randbetween('a', 'z', randbuf[1]);
+	langbuf[2] = '_';
+	langbuf[3] = randbetween('A', 'Z', randbuf[2]);
+	langbuf[4] = randbetween('A', 'Z', randbuf[3]);
+	langbuf[5] = '\0';
+
+	strname = g_strdup(langbuf);
+
+	return strname;
+}
+
+static void
+acceptlanguagescramble() {
+	SoupSession *s = webkit_get_default_session();
+	char *lang = getenv("LANG");
+	char *randlang1 = strlangentropy();
+	char *randlang2 = strlangentropy();
+	char *acceptlanguage;
+
+	if (strlen(lang) >= 5) {
+		acceptlanguage = g_strdup_printf("%5.5s, %s;q=0.9, %s;q=0.8", lang, randlang1, randlang2);
+		g_object_set(G_OBJECT(s), "accept-language", acceptlanguage, NULL);
+	}
+	g_free(randlang1);
+	g_free(randlang2);
+}
+
+static void
+useragentscramble(WebKitWebView *view) {
+	WebKitWebSettings *settings = webkit_web_view_get_settings(view);
+	gchar *ua = strentropy();
+	if (!ua) 
+		ua = " "; /* fallback to blank user-agent -- NULL or "" return a webkit default string that leaks information */
+	g_object_set(G_OBJECT(settings), "user-agent", ua, NULL);
+	g_free(ua);
+}
+	
 static void
 eval(Client *c, const Arg *arg) {
 	WebKitWebFrame *frame = webkit_web_view_get_main_frame(c->view);
@@ -1409,6 +1742,8 @@ gettogglestat(Client *c){
 
 	togglestat[p++] = enablediskcache? 'D': 'd';
 
+	togglestat[p++] = sameoriginpolicy? 'O' : 'o';
+
 	g_object_get(G_OBJECT(settings), "auto-load-images", &value, NULL);
 	togglestat[p++] = value? 'I': 'i';
 
@@ -1441,6 +1776,14 @@ getpagestat(Client *c) {
 static void
 updatetitle(Client *c) {
 	char *t;
+	char *originstat;
+
+	if(originhas(origin_uri)) {
+		originstat = origingethost(origin_uri);
+	} else {
+		originstat = g_strdup("-");
+	}
+			
 
 	if(showindicators) {
 		gettogglestat(c);
@@ -1450,11 +1793,14 @@ updatetitle(Client *c) {
 			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,
+			t = g_strdup_printf("[%i%%] %s:%s | %s | %s", c->progress,
 					togglestat, pagestat,
+					originstat,
 					(c->title == NULL)? "" : c->title);
 		} else {
-			t = g_strdup_printf("%s:%s | %s", togglestat, pagestat,
+			t = g_strdup_printf(       "%s:%s | %s | %s", 
+					togglestat, pagestat,
+					originstat,
 					(c->title == NULL)? "" : c->title);
 		}
 
@@ -1464,6 +1810,8 @@ updatetitle(Client *c) {
 		gtk_window_set_title(GTK_WINDOW(c->win),
 				(c->title == NULL)? "" : c->title);
 	}
+
+	g_free(originstat);
 }
 
 static void
@@ -1507,6 +1855,8 @@ int
 main(int argc, char *argv[]) {
 	Arg arg;
 	Client *c;
+	char *qualified_uri = NULL;
+	char *prompt = NULL;
 
 	memset(&arg, 0, sizeof(arg));
 
@@ -1569,6 +1919,12 @@ main(int argc, char *argv[]) {
 	case 'N':
 		enableinspector = 1;
 		break;
+	case 'o':
+		sameoriginpolicy = 0;
+		break;
+	case 'O':
+		sameoriginpolicy = 1;
+		break;
 	case 'p':
 		enableplugins = 0;
 		break;
@@ -1578,6 +1934,9 @@ main(int argc, char *argv[]) {
 	case 'r':
 		scriptfile = EARGF(usage());
 		break;
+	case 'R':
+		referring_origin = EARGF(usage());
+		break;
 	case 's':
 		enablescripts = 0;
 		break;
@@ -1602,13 +1961,29 @@ main(int argc, char *argv[]) {
 	default:
 		usage();
 	} ARGEND;
-	if(argc > 0)
-		arg.v = argv[0];
+	if(argc > 0) {
+		if (argv[0]) {
+			qualified_uri = qualify_uri(argv[0]);
+		}
+	}
 
-	setup();
+	setup(qualified_uri);
 	c = newclient();
-	if(arg.v) {
-		loaduri(clients, &arg);
+	updatewinid(c);
+	if(qualified_uri) {
+		if (originhas(qualified_uri)) {
+			origin_uri = qualified_uri;
+		}
+		if (sameoriginpolicy && referring_origin && (strcmp(referring_origin, "-") == 0 || !originmatch(referring_origin, qualified_uri))) {
+			setatom(c, AtomUri, qualified_uri);
+			prompt = g_strdup_printf(originprompt, referring_origin);
+			arg = (Arg)SETPROP("_SURF_URI", "_SURF_GO", prompt);
+			spawn(c, &arg);
+			g_free(prompt);
+		} else {
+			arg.v = qualified_uri;
+			loaduri(clients, &arg, NavImplicit);
+		}
 	} else {
 		updatetitle(c);
 	}
@@ -1616,6 +1991,8 @@ main(int argc, char *argv[]) {
 	gtk_main();
 	cleanup();
 
+	g_free(qualified_uri);
+
 	return EXIT_SUCCESS;
 }
 
