Hi all,

This patch is a bit of a beast for surf. It is intended to be applied after
the disk cache patch. It breaks some internal interfaces, so it could
conflict with other patches.

I have been wanting a browser to implement a complete same-origin policy,
and have been investigating how to do this in various browsers for many
months. When I saw how surf opened new windows in a separate process, and
was so simple, I knew I could do it quickly. Over the last two weeks, I
have been developing this implementation on surf.

The basic idea is to prevent browser-based tracking as you browse from site
to site, or origin to origin. By "origin" domain, I mean the "first-party"
domain, the domain normally in the location bar (of the typical browser
interface). Each origin domain effectively gets its own browser profile,
and a browser process only ever deals with one origin domain at a time.
This isolates origins vertically, preventing cookies, disk cache, memory
cache, and window.name vulnerabilities. Basically, all known
vulnerabilities that google and Mozilla cite as counter-examples when they
explain why they haven't disabled third-party cookies yet.

When you are on msnbc.com, the tracking pixels will be stored in a cookie
file for msnbc.com. When you go to cnn.com, the tracking pixels will be
stored in a cookie file for cnn.com. You will not be tracked between them.
However, third-party cookies, and the caching of third party resources will
still work, but they will be isolated between origin domains. Instead of
blocking cookies and cache entries, they are "double-keyed", or *also*
keyed by origin.

There is a unidirectional communication channel, however, from one origin
to the next, through navigation from one origin to the next. That is, the
query string is passed from one origin to the next, and may embed
identifiers. One example is an affiliate link that identifies where the
lead came from. I have implemented what I call "horizontal isolation", in
the form of an "Origin Crossing Gate".

Whenever you follow a link to a new domain, or even are just redirected to
a new domain, a new window/tab is opened, and passed the referring origin
via -R. The page passed to -O, for example -O originprompt.html, is an HTML
page that is loaded in the new origin's context. That page tells you the
origin you were on, the new origin, and the full link, and you can decide
to go just to the new origin, or go to the full URL, after reviewing it for
tracking data.

Also, you may click links that store your trust of that relationship with
various expiration times, the same way you would trust geolocation requests
for a particular origin for a period of time. The database used is actually
the new origin's cookie file. Since the origin prompt is loaded in the new
origin's context, I can set a cookie on behalf of the new origin. The
expiration time of the trust is the expiration time of the cookie. The
cookie implementation in webkit automatically expires the trust as part of
how cookies work. Each time you cross an origin, the origin crossing page
checks the cookie to see if trust is still established. If so, it will use
window.location.replace() to continue on automatically. The initial page
renders blank until the trust is invalidated, in which case the content of
the gate is made visible.

However, the new origin is technically able to mess with those cookies, so
a website could set trust for an origin crossing. I have addressed that by
hashing the key with a salt, and setting the real expiration time as the
value, along with an HMAC to verify the contents of the value. If the
cookie is messed with in any way, the trust will be disabled, and the
prompt will appear again. So it has a fail-safe function.

I know it seems a bit convoluted, but it just started out as a nice little
rabbit hole, and I just wanted to get something workable. At first I
thought using the cookie expiration time was convenient, but then when I
realized that I needed to protect the cookie, things got a bit hairy. But
it works.

Each profile is, by default, stored in ~/.surf/origins/$origin/
The interesting side effect is that if there is a problem where a website
relies on the cross-site cookie vulnerability to make a connection, you can
simply make a symbolic link from one origin folder to another, and they
will share the same profile. And if you want to delete cookies and/or cache
for a particular origin, you just rm -rf the origin's profile folder, and
don't have to interfere with your other sites that are working just fine.

One thing I don't handle are cross-origins POSTs. They just end up as GET
requests right now. I intend to do something about that, but I haven't
figured that out yet.

I have only been using this functionality for a few days myself, so I have
absolutely no feedback yet. I wanted to provide the first implementation of
the management of identity as a system resource the same way that things
like geolocation, camera, and microphone resources are managed in browsers
and mobile apps.

Currently, Mozilla and Tor have are working on third-party tracking issues
in Firefox.
https://blog.mozilla.org/privacy/2014/11/10/introducing-polaris-privacy-initiative-to-accelerate-user-focused-privacy-online/

Up to this point, Tor has provided a patch that double-keys cookies with
the origin domain, but no other progress is visible. I have seen no
discussion of how horizontal isolation is supposed to happen, and I wanted
to show people that it can be done, and this is one way it can be done, and
to compel the other browser makers to catch up, and hopefully the community
can work toward a standard *without* the tracking loopholes, by showing
people what a *complete* solution looks like.

Thank you,

Ben Woolley
From 0dbf5e54fc4b7e720564cca07868840c8b4ecdb8 Mon Sep 17 00:00:00 2001
From: Ben Woolley <tauto...@gmail.com>
Date: Wed, 7 Jan 2015 17:01:32 -0800
Subject: [PATCH 5/5] same-origin policy

---
 config.def.h      |   6 +
 originprompt.html | 176 +++++++++++++++++++++++++++
 surf.c            | 349 +++++++++++++++++++++++++++++++++++++++++++++++-------
 3 files changed, 487 insertions(+), 44 deletions(-)
 create mode 100644 originprompt.html

diff --git a/config.def.h b/config.def.h
index 033adfe..7126115 100644
--- a/config.def.h
+++ b/config.def.h
@@ -6,6 +6,10 @@ static char *downloadfolder = "~/Downloads/";
 static char *stylefile      = "~/.surf/style.css";
 static char *scriptfile     = "~/.surf/script.js";
 static char *cachefolder    = "~/.surf/cache/";
+static char *originpromptfile = NULL;
+static char *originpromptdigestsalt = "Surf Origin Prompt Digest Salt 123456789"; /* change me; changing invalidates the origin trust state */
+static char *originprompthmackey    = "Surf Origin Prompt HMAC Key 123456789";    /* " */
+static char *origincachefolder = "~/.surf/origins/%s/cache/";
 
 static Bool kioskmode       = FALSE; /* Ignore shortcuts */
 static Bool showindicators  = TRUE;  /* Show indicators in window title */
@@ -17,6 +21,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,6 +39,7 @@ static Bool enableinspector = TRUE;
 static Bool loadimages = TRUE;
 static Bool hidebackground  = FALSE;
 static Bool allowgeolocation = TRUE;
+static Bool sameoriginpolicy = TRUE;
 
 #define SETPROP(p, q) { \
 	.v = (char *[]){ "/bin/sh", "-c", \
diff --git a/originprompt.html b/originprompt.html
new file mode 100644
index 0000000..629f577
--- /dev/null
+++ b/originprompt.html
@@ -0,0 +1,176 @@
+<html>
+<head>
+<script type="text/javascript">
+/*
+ A JavaScript implementation of the SHA family of hashes, as
+ defined in FIPS PUB 180-2 as well as the corresponding HMAC implementation
+ as defined in FIPS PUB 198a
+
+ Copyright Brian Turek 2008-2014
+ Distributed under the BSD License
+ See http://caligatio.github.com/jsSHA/ for more information
+
+ Several functions taken from Paul Johnston
+*/
+'use strict';(function(J){function u(a,c,b){var f=0,g=[0],k="",l=null,k=b||"UTF8";if("UTF8"!==k&&"UTF16"!==k)throw"encoding must be UTF8 or UTF16";if("HEX"===c){if(0!==a.length%%2)throw"srcString of HEX type must be in byte increments";l=x(a);f=l.binLen;g=l.value}else if("TEXT"===c)l=y(a,k),f=l.binLen,g=l.value;else if("B64"===c)l=z(a),f=l.binLen,g=l.value;else if("BYTES"===c)l=A(a),f=l.binLen,g=l.value;else throw"inputFormat must be HEX, TEXT, B64, or BYTES";this.getHash=function(a,c,b,k){var l=null,
+e=g.slice(),n=f,m;3===arguments.length?"number"!==typeof b&&(k=b,b=1):2===arguments.length&&(b=1);if(b!==parseInt(b,10)||1>b)throw"numRounds must a integer >= 1";switch(c){case "HEX":l=B;break;case "B64":l=C;break;case "BYTES":l=D;break;default:throw"format must be HEX, B64, or BYTES";}if("SHA-384"===a)for(m=0;m<b;m+=1)e=t(e,n,a),n=384;else if("SHA-512"===a)for(m=0;m<b;m+=1)e=t(e,n,a),n=512;else throw"Chosen SHA variant is not supported";return l(e,E(k))};this.getHMAC=function(a,b,c,l,p){var e,n,
+m,r,q=[],v=[];e=null;switch(l){case "HEX":l=B;break;case "B64":l=C;break;case "BYTES":l=D;break;default:throw"outputFormat must be HEX, B64, or BYTES";}if("SHA-384"===c)n=128,r=384;else if("SHA-512"===c)n=128,r=512;else throw"Chosen SHA variant is not supported";if("HEX"===b)e=x(a),m=e.binLen,e=e.value;else if("TEXT"===b)e=y(a,k),m=e.binLen,e=e.value;else if("B64"===b)e=z(a),m=e.binLen,e=e.value;else if("BYTES"===b)e=A(a),m=e.binLen,e=e.value;else throw"inputFormat must be HEX, TEXT, B64, or BYTES";
+a=8*n;b=n/4-1;n<m/8?(e=t(e,m,c),e[b]&=4294967040):n>m/8&&(e[b]&=4294967040);for(n=0;n<=b;n+=1)q[n]=e[n]^909522486,v[n]=e[n]^1549556828;c=t(v.concat(t(q.concat(g),a+f,c)),a+r,c);return l(c,E(p))}}function p(a,c){this.a=a;this.b=c}function y(a,c){var b=[],f,g=[],k=0,l;if("UTF8"===c)for(l=0;l<a.length;l+=1)for(f=a.charCodeAt(l),g=[],128>f?g.push(f):2048>f?(g.push(192|f>>>6),g.push(128|f&63)):55296>f||57344<=f?g.push(224|f>>>12,128|f>>>6&63,128|f&63):(l+=1,f=65536+((f&1023)<<10|a.charCodeAt(l)&1023),
+g.push(240|f>>>18,128|f>>>12&63,128|f>>>6&63,128|f&63)),f=0;f<g.length;f+=1)(k>>>2)+1>b.length&&b.push(0),b[k>>>2]|=g[f]<<24-k%%4*8,k+=1;else if("UTF16"===c)for(l=0;l<a.length;l+=1)(k>>>2)+1>b.length&&b.push(0),b[k>>>2]|=a.charCodeAt(l)<<16-k%%4*8,k+=2;return{value:b,binLen:8*k}}function x(a){var c=[],b=a.length,f,g;if(0!==b%%2)throw"String of HEX type must be in byte increments";for(f=0;f<b;f+=2){g=parseInt(a.substr(f,2),16);if(isNaN(g))throw"String of HEX type contains invalid characters";c[f>>>3]|=
+g<<24-f%%8*4}return{value:c,binLen:4*b}}function A(a){var c=[],b,f;for(f=0;f<a.length;f+=1)b=a.charCodeAt(f),(f>>>2)+1>c.length&&c.push(0),c[f>>>2]|=b<<24-f%%4*8;return{value:c,binLen:8*a.length}}function z(a){var c=[],b=0,f,g,k,l,p;if(-1===a.search(/^[a-zA-Z0-9=+\/]+$/))throw"Invalid character in base-64 string";f=a.indexOf("=");a=a.replace(/\=/g,"");if(-1!==f&&f<a.length)throw"Invalid '=' found in base-64 string";for(g=0;g<a.length;g+=4){p=a.substr(g,4);for(k=l=0;k<p.length;k+=1)f="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".indexOf(p[k]),
+l|=f<<18-6*k;for(k=0;k<p.length-1;k+=1)c[b>>2]|=(l>>>16-8*k&255)<<24-b%%4*8,b+=1}return{value:c,binLen:8*b}}function B(a,c){var b="",f=4*a.length,g,k;for(g=0;g<f;g+=1)k=a[g>>>2]>>>8*(3-g%%4),b+="0123456789abcdef".charAt(k>>>4&15)+"0123456789abcdef".charAt(k&15);return c.outputUpper?b.toUpperCase():b}function C(a,c){var b="",f=4*a.length,g,k,l;for(g=0;g<f;g+=3)for(l=(a[g>>>2]>>>8*(3-g%%4)&255)<<16|(a[g+1>>>2]>>>8*(3-(g+1)%%4)&255)<<8|a[g+2>>>2]>>>8*(3-(g+2)%%4)&255,k=0;4>k;k+=1)b=8*g+6*k<=32*a.length?b+
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(l>>>6*(3-k)&63):b+c.b64Pad;return b}function D(a){var c="",b=4*a.length,f,g;for(f=0;f<b;f+=1)g=a[f>>>2]>>>8*(3-f%%4)&255,c+=String.fromCharCode(g);return c}function E(a){var c={outputUpper:!1,b64Pad:"="};try{a.hasOwnProperty("outputUpper")&&(c.outputUpper=a.outputUpper),a.hasOwnProperty("b64Pad")&&(c.b64Pad=a.b64Pad)}catch(b){}if("boolean"!==typeof c.outputUpper)throw"Invalid outputUpper formatting option";if("string"!==typeof c.b64Pad)throw"Invalid b64Pad formatting option";
+return c}function q(a,c){var b=null,b=new p(a.a,a.b);return b=32>=c?new p(b.a>>>c|b.b<<32-c&4294967295,b.b>>>c|b.a<<32-c&4294967295):new p(b.b>>>c-32|b.a<<64-c&4294967295,b.a>>>c-32|b.b<<64-c&4294967295)}function F(a,c){var b=null;return b=32>=c?new p(a.a>>>c,a.b>>>c|a.a<<32-c&4294967295):new p(0,a.a>>>c-32)}function K(a,c,b){return new p(a.a&c.a^~a.a&b.a,a.b&c.b^~a.b&b.b)}function L(a,c,b){return new p(a.a&c.a^a.a&b.a^c.a&b.a,a.b&c.b^a.b&b.b^c.b&b.b)}function M(a){var c=q(a,28),b=q(a,34);a=q(a,39);
+return new p(c.a^b.a^a.a,c.b^b.b^a.b)}function N(a){var c=q(a,14),b=q(a,18);a=q(a,41);return new p(c.a^b.a^a.a,c.b^b.b^a.b)}function O(a){var c=q(a,1),b=q(a,8);a=F(a,7);return new p(c.a^b.a^a.a,c.b^b.b^a.b)}function P(a){var c=q(a,19),b=q(a,61);a=F(a,6);return new p(c.a^b.a^a.a,c.b^b.b^a.b)}function Q(a,c){var b,f,g;b=(a.b&65535)+(c.b&65535);f=(a.b>>>16)+(c.b>>>16)+(b>>>16);g=(f&65535)<<16|b&65535;b=(a.a&65535)+(c.a&65535)+(f>>>16);f=(a.a>>>16)+(c.a>>>16)+(b>>>16);return new p((f&65535)<<16|b&65535,
+g)}function R(a,c,b,f){var g,k,l;g=(a.b&65535)+(c.b&65535)+(b.b&65535)+(f.b&65535);k=(a.b>>>16)+(c.b>>>16)+(b.b>>>16)+(f.b>>>16)+(g>>>16);l=(k&65535)<<16|g&65535;g=(a.a&65535)+(c.a&65535)+(b.a&65535)+(f.a&65535)+(k>>>16);k=(a.a>>>16)+(c.a>>>16)+(b.a>>>16)+(f.a>>>16)+(g>>>16);return new p((k&65535)<<16|g&65535,l)}function S(a,c,b,f,g){var k,l,q;k=(a.b&65535)+(c.b&65535)+(b.b&65535)+(f.b&65535)+(g.b&65535);l=(a.b>>>16)+(c.b>>>16)+(b.b>>>16)+(f.b>>>16)+(g.b>>>16)+(k>>>16);q=(l&65535)<<16|k&65535;k=(a.a&
+65535)+(c.a&65535)+(b.a&65535)+(f.a&65535)+(g.a&65535)+(l>>>16);l=(a.a>>>16)+(c.a>>>16)+(b.a>>>16)+(f.a>>>16)+(g.a>>>16)+(k>>>16);return new p((l&65535)<<16|k&65535,q)}function t(a,c,b){var f,g,k,l,q,t,u,G,x,e,n,m,r,y,v,s,z,A,B,C,D,E,F,H,d,w=[],I,h=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,
+2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298];e=[3238371032,914150663,812702999,4144912697,4290775857,1750603025,1694076839,
+3204075428];g=[1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225];if("SHA-384"===b||"SHA-512"===b)n=80,f=(c+128>>>10<<5)+31,y=32,v=2,d=p,s=Q,z=R,A=S,B=O,C=P,D=M,E=N,H=L,F=K,h=[new d(h[0],3609767458),new d(h[1],602891725),new d(h[2],3964484399),new d(h[3],2173295548),new d(h[4],4081628472),new d(h[5],3053834265),new d(h[6],2937671579),new d(h[7],3664609560),new d(h[8],2734883394),new d(h[9],1164996542),new d(h[10],1323610764),new d(h[11],3590304994),new d(h[12],
+4068182383),new d(h[13],991336113),new d(h[14],633803317),new d(h[15],3479774868),new d(h[16],2666613458),new d(h[17],944711139),new d(h[18],2341262773),new d(h[19],2007800933),new d(h[20],1495990901),new d(h[21],1856431235),new d(h[22],3175218132),new d(h[23],2198950837),new d(h[24],3999719339),new d(h[25],766784016),new d(h[26],2566594879),new d(h[27],3203337956),new d(h[28],1034457026),new d(h[29],2466948901),new d(h[30],3758326383),new d(h[31],168717936),new d(h[32],1188179964),new d(h[33],1546045734),
+new d(h[34],1522805485),new d(h[35],2643833823),new d(h[36],2343527390),new d(h[37],1014477480),new d(h[38],1206759142),new d(h[39],344077627),new d(h[40],1290863460),new d(h[41],3158454273),new d(h[42],3505952657),new d(h[43],106217008),new d(h[44],3606008344),new d(h[45],1432725776),new d(h[46],1467031594),new d(h[47],851169720),new d(h[48],3100823752),new d(h[49],1363258195),new d(h[50],3750685593),new d(h[51],3785050280),new d(h[52],3318307427),new d(h[53],3812723403),new d(h[54],2003034995),
+new d(h[55],3602036899),new d(h[56],1575990012),new d(h[57],1125592928),new d(h[58],2716904306),new d(h[59],442776044),new d(h[60],593698344),new d(h[61],3733110249),new d(h[62],2999351573),new d(h[63],3815920427),new d(3391569614,3928383900),new d(3515267271,566280711),new d(3940187606,3454069534),new d(4118630271,4000239992),new d(116418474,1914138554),new d(174292421,2731055270),new d(289380356,3203993006),new d(460393269,320620315),new d(685471733,587496836),new d(852142971,1086792851),new d(1017036298,
+365543100),new d(1126000580,2618297676),new d(1288033470,3409855158),new d(1501505948,4234509866),new d(1607167915,987167468),new d(1816402316,1246189591)],e="SHA-384"===b?[new d(3418070365,e[0]),new d(1654270250,e[1]),new d(2438529370,e[2]),new d(355462360,e[3]),new d(1731405415,e[4]),new d(41048885895,e[5]),new d(3675008525,e[6]),new d(1203062813,e[7])]:[new d(g[0],4089235720),new d(g[1],2227873595),new d(g[2],4271175723),new d(g[3],1595750129),new d(g[4],2917565137),new d(g[5],725511199),new d(g[6],
+4215389547),new d(g[7],327033209)];else throw"Unexpected error in SHA-2 implementation";a[c>>>5]|=128<<24-c%%32;a[f]=c;I=a.length;for(m=0;m<I;m+=y){c=e[0];f=e[1];g=e[2];k=e[3];l=e[4];q=e[5];t=e[6];u=e[7];for(r=0;r<n;r+=1)w[r]=16>r?new d(a[r*v+m],a[r*v+m+1]):z(C(w[r-2]),w[r-7],B(w[r-15]),w[r-16]),G=A(u,E(l),F(l,q,t),h[r],w[r]),x=s(D(c),H(c,f,g)),u=t,t=q,q=l,l=s(k,G),k=g,g=f,f=c,c=s(G,x);e[0]=s(c,e[0]);e[1]=s(f,e[1]);e[2]=s(g,e[2]);e[3]=s(k,e[3]);e[4]=s(l,e[4]);e[5]=s(q,e[5]);e[6]=s(t,e[6]);e[7]=s(u,
+e[7])}if("SHA-384"===b)a=[e[0].a,e[0].b,e[1].a,e[1].b,e[2].a,e[2].b,e[3].a,e[3].b,e[4].a,e[4].b,e[5].a,e[5].b];else if("SHA-512"===b)a=[e[0].a,e[0].b,e[1].a,e[1].b,e[2].a,e[2].b,e[3].a,e[3].b,e[4].a,e[4].b,e[5].a,e[5].b,e[6].a,e[6].b,e[7].a,e[7].b];else throw"Unexpected error in SHA-2 implementation";return a}"function"===typeof define&&define.amd?define(function(){return u}):"undefined"!==typeof exports?"undefined"!==typeof module&&module.exports?module.exports=exports=u:exports=u:J.jsSHA=u})(this);
+</script>
+<script type="text/javascript">
+	var digest_salt = "%s";
+	var hmac_key = "%s";
+	var cross_from_origin = "%s";
+	var cross_to_uri = "%s";
+	// setCookie() and getCookie() are from w3schools.com
+	function setCookie(cname, cvalue, exdays) {
+		var d = new Date();
+		d.setTime(d.getTime() + (exdays*24*60*60*1000));
+		var expires = "expires="+d.toUTCString();
+		document.cookie = cname + "=" + cvalue + "; " + expires;
+	}
+	function getCookie(cname) {
+		var name = cname + "=";
+		var ca = document.cookie.split(';');
+		for(var i=0; i<ca.length; i++) {
+			var c = ca[i];
+			while (c.charAt(0)==' ') c = c.substring(1);
+			if (c.indexOf(name) == 0) return c.substring(name.length,c.length);
+		}
+		return "";
+	}
+	function getProtectedCookie(cname) {
+		var digest_cname = new jsSHA(digest_salt + cname, "TEXT");
+		var protected_cname = digest_cname.getHash("SHA-512", "HEX");
+		var protected_cvalue = getCookie(protected_cname);
+		var cvalue, cvalue_proposed_hmac;
+		var cvalue_split = protected_cvalue.split(",", 2);
+		cvalue = cvalue_split[0];
+		cvalue_proposed_hmac = cvalue_split[1];
+		var digest_cvalue = new jsSHA(digest_salt + cname + cvalue, "TEXT");
+		var cvalue_hmac = digest_cvalue.getHMAC(hmac_key, "TEXT", "SHA-512", "HEX");
+		if (cvalue_proposed_hmac == cvalue_hmac) {
+			return cvalue;
+		} else {
+			return "";
+		}
+	}
+	function setProtectedCookie(cname, cvalue, exdays) {
+		var digest_cname = new jsSHA(digest_salt + cname, "TEXT");
+		var protected_cname = digest_cname.getHash("SHA-512", "HEX");
+		var digest_cvalue = new jsSHA(digest_salt + cname + cvalue, "TEXT");
+		var protected_cvalue = cvalue + "," + digest_cvalue.getHMAC(hmac_key, "TEXT", "SHA-512", "HEX");
+		return setCookie(protected_cname, protected_cvalue, exdays);
+	}
+	function suppressfordays(exdays) {
+		var d = new Date();
+		var expire_stamp = d.getTime() + (exdays*24*60*60*1000);
+		setProtectedCookie(cross_from_origin, expire_stamp, exdays);
+	}
+	function bypass() {
+		var d = new Date();
+		var current_stamp = d.getTime();
+		var expire_stamp = getProtectedCookie(cross_from_origin);
+		if (current_stamp <= expire_stamp) {
+			window.location.replace(cross_to_uri);
+		} else {
+			document.getElementById("content").style.display = "block";
+		}
+	}
+</script>
+<style type="text/css">
+#content { display: none; }
+	html, body {
+	margin: 0 auto;
+	width: 800px;
+}
+	h1, h2, h3, h4, h5, h6 {
+	text-align: center;
+}
+table {
+	margin: 1em auto;
+	border-collapse: collapse;
+	border-spacing: 0.5em;
+}
+th, td {
+	padding: 0;
+	border: 1px solid #7f7f7f;
+	padding: 0.25em 0.5em;
+}
+td {
+	width: fill-available;
+	text-align: center;
+}
+th {
+	white-space: nowrap;
+}
+th[scope=row] {
+	text-align: right;
+}
+a.button {
+	display: block;
+	width: fill-available;
+	padding: 0.25em 0.5em;
+	text-decoration: none;
+}
+td.button {
+	padding: 0;
+}
+a.safe {
+	background-color: rgba(0,255,0,0.5);
+	color: inherit;
+}
+a.approval {
+	background-color: rgba(255,255,0,0.5);
+	color: inherit;
+}
+</style>
+<title>Origin Crossing Gate</title>
+</head>
+<body onload="bypass()">
+	<div id="content">
+		<h1>Origin Crossing Gate</h1>
+		<table>
+			<caption>Proceed with Caution</caption>
+			<tr>
+				<th scope="row">From Origin</th>
+				<td><code>%s</code></td>
+			</tr>
+			<tr>
+				<th scope="row">To Origin</th>
+				<td class="button"><a class="button safe" href="%s"><code>%s</code></a></td>
+			</tr>
+			<tr>
+				<th scope="row">To URL</th>
+				<td class="button"><a class="button approval" href="%s"><code>%s</code></a></td>
+			</tr>
+		</table>
+		<table>
+			<caption>Trust This Origin Crossing and Suppress This Prompt For</caption>
+			<tr>
+				<th scope="row">Duration</th>
+				<td><a class="button" href="#" onclick="suppressfordays(1)">1 Day</a></td>
+				<td><a class="button" href="#" onclick="suppressfordays(30)">1 Month</a></td>
+				<td><a class="button" href="#" onclick="suppressfordays(365)">1 Year</a></td>
+			</tr>
+		<table>
+	</div>
+</body>
+</html>
+;
diff --git a/surf.c b/surf.c
index 91a69ed..0ecbdbd 100644
--- a/surf.c
+++ b/surf.c
@@ -34,6 +34,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 {
@@ -78,11 +79,14 @@ 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 *origin_uri = NULL;
+static char *referring_origin = NULL;
 static SoupCache *diskcache = NULL;
+static char *originprompt = NULL;
 
 static void addaccelgroup(Client *c);
 static void beforerequest(WebKitWebView *w, WebKitWebFrame *f,
@@ -111,6 +115,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);
@@ -144,10 +151,18 @@ 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 loadgate(Client *c, const char *cross_from_origin, const char *cross_to_uri);
+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 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);
@@ -161,7 +176,7 @@ 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);
@@ -175,6 +190,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);
@@ -252,7 +268,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;
 		}
 	}
@@ -422,6 +438,35 @@ 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 (!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 (origin_uri == NULL || 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) {
@@ -431,7 +476,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;
@@ -534,7 +579,7 @@ geturi(Client *c) {
 	char *uri;
 
 	if(!(uri = (char *)webkit_web_view_get_uri(c->view)))
-		uri = "about:blank";
+		uri = "";
 	return uri;
 }
 
@@ -640,6 +685,9 @@ loadstatuschange(WebKitWebView *view, GParamSpec *pspec, Client *c) {
 	switch(webkit_web_view_get_load_status (c->view)) {
 	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);
@@ -663,38 +711,63 @@ 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
+loadgate(Client *c, const char *cross_from_origin, const char *cross_to_uri) {
+	char *cross_to_origin = origingeturi(cross_to_uri);
+	char *content =         g_strdup_printf(originprompt, originpromptdigestsalt, originprompthmackey, cross_from_origin, cross_to_uri, cross_from_origin, cross_to_origin, cross_to_origin, cross_to_uri, cross_to_uri);
+
+	origin_uri = g_strdup(cross_to_uri);
+	webkit_web_view_load_string(c->view, content, "text/html", NULL, cross_to_origin);
+	c->progress = 0;
+	c->title = g_strdup("Origin Crossing Gate");
+	updatetitle(c);
 
-	/* prevents endless loop */
-	if(strcmp(u, geturi(c)) == 0) {
-		reload(c, &a);
+	g_free(cross_to_origin);
+	g_free(content);
+}
+
+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(uri, geturi(c)) == 0) {
+			reload(c, &a);
+		} else {
+			webkit_web_view_load_uri(c->view, uri);
+			c->progress = 0;
+			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, NavExplicit);
 	}
-	g_free(u);
 }
 
 static void
@@ -773,6 +846,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),
@@ -921,11 +997,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[16], *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";
@@ -949,8 +1026,20 @@ newwindow(Client *c, const Arg *arg, gboolean noembed) {
 		cmd[i++] = "-s";
 	if(showxid)
 		cmd[i++] = "-x";
+	if(sameoriginpolicy)
+		cmd[i++] = "-O";
+		cmd[i++] = originpromptfile;
 	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++] = "--";
@@ -959,6 +1048,7 @@ newwindow(Client *c, const Arg *arg, gboolean noembed) {
 		cmd[i++] = uri;
 	cmd[i++] = NULL;
 	spawn(NULL, &a);
+	g_free(origin_packed);
 }
 
 static gboolean
@@ -1009,11 +1099,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
@@ -1026,6 +1217,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;
@@ -1036,10 +1229,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;
+					}
+				}
 			}
 		}
 	}
@@ -1106,9 +1306,11 @@ setatom(Client *c, int a, const char *v) {
 }
 
 static void
-setup(void) {
+setup(const char *qualified_uri) {
 	char *proxy;
 	char *new_proxy;
+	char *origin;
+	char *originpath;
 	SoupURI *puri;
 	SoupSession *s;
 	GError *error = NULL;
@@ -1124,11 +1326,30 @@ setup(void) {
 	atoms[AtomGo] = XInternAtom(dpy, "_SURF_GO", False);
 	atoms[AtomUri] = XInternAtom(dpy, "_SURF_URI", False);
 
+	if (originpromptfile)
+		if (!g_file_get_contents(originpromptfile, &originprompt, NULL, NULL))
+			die("Could not open origin prompt file\n");
+
 	/* 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);
+
+		free(origin);
+	} else {
+		cookiefile = buildpath(cookiefile);
+		cachefolder = buildpath(cachefolder);
+	}
+
 	scriptfile = buildpath(scriptfile);
 	stylefile = buildpath(stylefile);
-	cachefolder = buildpath(cachefolder);
 
 	/* request handler */
 	s = webkit_get_default_session();
@@ -1332,6 +1553,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';
 
@@ -1365,6 +1588,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);
@@ -1374,11 +1605,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);
 		}
 
@@ -1388,6 +1622,8 @@ updatetitle(Client *c) {
 		gtk_window_set_title(GTK_WINDOW(c->win),
 				(c->title == NULL)? "" : c->title);
 	}
+
+	g_free(originstat);
 }
 
 static void
@@ -1431,6 +1667,7 @@ int
 main(int argc, char *argv[]) {
 	Arg arg;
 	Client *c;
+	char *qualified_uri = NULL;
 
 	memset(&arg, 0, sizeof(arg));
 
@@ -1487,6 +1724,13 @@ main(int argc, char *argv[]) {
 	case 'N':
 		enableinspector = 1;
 		break;
+	case 'o':
+		sameoriginpolicy = 0;
+		break;
+	case 'O':
+		sameoriginpolicy = 1;
+		originpromptfile = EARGF(usage());
+		break;
 	case 'p':
 		enableplugins = 0;
 		break;
@@ -1496,6 +1740,9 @@ main(int argc, char *argv[]) {
 	case 'r':
 		scriptfile = EARGF(usage());
 		break;
+	case 'R':
+		referring_origin = EARGF(usage());
+		break;
 	case 's':
 		enablescripts = 0;
 		break;
@@ -1520,13 +1767,25 @@ 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);
+	if(qualified_uri) {
+		if (originhas(qualified_uri)) {
+			origin_uri = qualified_uri;
+		}
+		if (sameoriginpolicy && referring_origin && (strcmp(referring_origin, "-") == 0 || !originmatch(referring_origin, qualified_uri))) {
+			loadgate(clients, referring_origin, qualified_uri);
+		} else {
+			arg.v = qualified_uri;
+			loaduri(clients, &arg, NavImplicit);
+		}
+		g_free(qualified_uri);
 	} else {
 		updatetitle(c);
 	}
@@ -1534,6 +1793,8 @@ main(int argc, char *argv[]) {
 	gtk_main();
 	cleanup();
 
+	g_free(qualified_uri);
+
 	return EXIT_SUCCESS;
 }
 
-- 
2.1.2

Reply via email to