Bug 1251872 - Part 1: Implement Request.referrerPolicy; r=jdm

This commit is contained in:
Ehsan Akhgari 2016-02-26 17:36:45 -05:00
parent 78b4ea3408
commit 937d4b75df
20 changed files with 297 additions and 53 deletions

View File

@ -8130,7 +8130,8 @@ nsContentUtils::IsPreloadType(nsContentPolicyType aType)
nsresult
nsContentUtils::SetFetchReferrerURIWithPolicy(nsIPrincipal* aPrincipal,
nsIDocument* aDoc,
nsIHttpChannel* aChannel)
nsIHttpChannel* aChannel,
mozilla::net::ReferrerPolicy aReferrerPolicy)
{
NS_ENSURE_ARG_POINTER(aPrincipal);
NS_ENSURE_ARG_POINTER(aChannel);
@ -8144,7 +8145,7 @@ nsContentUtils::SetFetchReferrerURIWithPolicy(nsIPrincipal* aPrincipal,
aPrincipal->GetURI(getter_AddRefs(principalURI));
if (!aDoc) {
return aChannel->SetReferrerWithPolicy(principalURI, net::RP_Default);
return aChannel->SetReferrerWithPolicy(principalURI, aReferrerPolicy);
}
// If it weren't for history.push/replaceState, we could just use the
@ -8173,7 +8174,10 @@ nsContentUtils::SetFetchReferrerURIWithPolicy(nsIPrincipal* aPrincipal,
referrerURI = principalURI;
}
net::ReferrerPolicy referrerPolicy = aDoc->GetReferrerPolicy();
net::ReferrerPolicy referrerPolicy = aReferrerPolicy;
if (referrerPolicy == net::RP_Default) {
referrerPolicy = aDoc->GetReferrerPolicy();
}
return aChannel->SetReferrerWithPolicy(referrerURI, referrerPolicy);
}

View File

@ -2523,7 +2523,8 @@ public:
*/
static nsresult SetFetchReferrerURIWithPolicy(nsIPrincipal* aPrincipal,
nsIDocument* aDoc,
nsIHttpChannel* aChannel);
nsIHttpChannel* aChannel,
mozilla::net::ReferrerPolicy aReferrerPolicy);
static bool PushEnabled(JSContext* aCx, JSObject* aObj);

View File

@ -2564,7 +2564,7 @@ nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable<RequestBody>& aBody)
nsCOMPtr<nsPIDOMWindowInner> owner = GetOwner();
nsCOMPtr<nsIDocument> doc = owner ? owner->GetExtantDoc() : nullptr;
nsContentUtils::SetFetchReferrerURIWithPolicy(mPrincipal, doc,
httpChannel);
httpChannel, mozilla::net::RP_Default);
}
// Some extensions override the http protocol handler and provide their own

View File

@ -980,7 +980,10 @@ DOMInterfaces = {
},
'Request': {
'binaryNames': { 'headers': 'headers_' },
'binaryNames': {
'headers': 'headers_',
'referrerPolicy': 'referrerPolicy_'
},
},
'Response': {

View File

@ -265,10 +265,36 @@ FetchDriver::HttpFetch()
// Step 2. Set the referrer.
nsAutoString referrer;
mRequest->GetReferrer(referrer);
ReferrerPolicy referrerPolicy = mRequest->ReferrerPolicy_();
net::ReferrerPolicy net_referrerPolicy = net::RP_Unset;
switch (referrerPolicy) {
case ReferrerPolicy::_empty:
net_referrerPolicy = net::RP_Default;
break;
case ReferrerPolicy::No_referrer:
net_referrerPolicy = net::RP_No_Referrer;
break;
case ReferrerPolicy::No_referrer_when_downgrade:
net_referrerPolicy = net::RP_No_Referrer_When_Downgrade;
break;
case ReferrerPolicy::Origin_only:
net_referrerPolicy = net::RP_Origin;
break;
case ReferrerPolicy::Origin_when_cross_origin:
net_referrerPolicy = net::RP_Origin_When_Crossorigin;
break;
case ReferrerPolicy::Unsafe_url:
net_referrerPolicy = net::RP_Unsafe_URL;
break;
default:
MOZ_ASSERT_UNREACHABLE("Invalid ReferrerPolicy enum value?");
break;
}
if (referrer.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR)) {
rv = nsContentUtils::SetFetchReferrerURIWithPolicy(mPrincipal,
mDocument,
httpChan);
httpChan,
net_referrerPolicy);
NS_ENSURE_SUCCESS(rv, rv);
} else if (referrer.IsEmpty()) {
rv = httpChan->SetReferrerWithPolicy(nullptr, net::RP_No_Referrer);
@ -277,19 +303,17 @@ FetchDriver::HttpFetch()
// From "Determine request's Referrer" step 3
// "If request's referrer is a URL, let referrerSource be request's
// referrer."
//
// XXXnsm - We never actually hit this from a fetch() call since both
// fetch and Request() create a new internal request whose referrer is
// always set to about:client. Should we just crash here instead until
// someone tries to use FetchDriver for non-fetch() APIs?
nsCOMPtr<nsIURI> referrerURI;
rv = NS_NewURI(getter_AddRefs(referrerURI), referrer, nullptr, nullptr);
NS_ENSURE_SUCCESS(rv, rv);
uint32_t documentReferrerPolicy = mDocument ? mDocument->GetReferrerPolicy() :
net::RP_Default;
rv =
httpChan->SetReferrerWithPolicy(referrerURI,
mDocument ? mDocument->GetReferrerPolicy() :
net::RP_Default);
referrerPolicy == ReferrerPolicy::_empty ?
documentReferrerPolicy :
net_referrerPolicy);
NS_ENSURE_SUCCESS(rv, rv);
}

View File

@ -37,6 +37,7 @@ InternalRequest::GetRequestConstructorCopy(nsIGlobalObject* aGlobal, ErrorResult
copy->mSameOriginDataURL = true;
copy->mPreserveContentCodings = true;
// The default referrer is already about:client.
copy->mReferrerPolicy = mReferrerPolicy;
copy->mContentPolicyType = nsIContentPolicy::TYPE_FETCH;
copy->mMode = mMode;
@ -77,6 +78,7 @@ InternalRequest::InternalRequest(const InternalRequest& aOther)
, mHeaders(new InternalHeaders(*aOther.mHeaders))
, mContentPolicyType(aOther.mContentPolicyType)
, mReferrer(aOther.mReferrer)
, mReferrerPolicy(aOther.mReferrerPolicy)
, mMode(aOther.mMode)
, mCredentialsMode(aOther.mCredentialsMode)
, mResponseTainting(aOther.mResponseTainting)

View File

@ -93,6 +93,7 @@ public:
, mHeaders(new InternalHeaders(HeadersGuardEnum::None))
, mContentPolicyType(nsIContentPolicy::TYPE_FETCH)
, mReferrer(NS_LITERAL_STRING(kFETCH_CLIENT_REFERRER_STR))
, mReferrerPolicy(ReferrerPolicy::_empty)
, mMode(RequestMode::No_cors)
, mCredentialsMode(RequestCredentials::Omit)
, mResponseTainting(LoadTainting::Basic)
@ -120,12 +121,14 @@ public:
RequestRedirect aRequestRedirect,
RequestCredentials aRequestCredentials,
const nsAString& aReferrer,
ReferrerPolicy aReferrerPolicy,
nsContentPolicyType aContentPolicyType)
: mMethod(aMethod)
, mURL(aURL)
, mHeaders(aHeaders)
, mContentPolicyType(aContentPolicyType)
, mReferrer(aReferrer)
, mReferrerPolicy(aReferrerPolicy)
, mMode(aMode)
, mCredentialsMode(aRequestCredentials)
, mResponseTainting(LoadTainting::Basic)
@ -230,6 +233,18 @@ public:
mReferrer.Assign(aReferrer);
}
ReferrerPolicy
ReferrerPolicy_() const
{
return mReferrerPolicy;
}
void
SetReferrerPolicy(ReferrerPolicy aReferrerPolicy)
{
mReferrerPolicy = aReferrerPolicy;
}
bool
SkipServiceWorker() const
{
@ -442,6 +457,7 @@ private:
// "about:client": client (default)
// URL: an URL
nsString mReferrer;
ReferrerPolicy mReferrerPolicy;
RequestMode mMode;
RequestCredentials mCredentialsMode;

View File

@ -353,6 +353,7 @@ Request::Constructor(const GlobalObject& aGlobal,
if (aInit.IsAnyMemberPresent()) {
request->SetReferrer(NS_LITERAL_STRING(kFETCH_CLIENT_REFERRER_STR));
request->SetReferrerPolicy(ReferrerPolicy::_empty);
}
if (aInit.mReferrer.WasPassed()) {
const nsString& referrer = aInit.mReferrer.Value();
@ -419,6 +420,10 @@ Request::Constructor(const GlobalObject& aGlobal,
}
}
if (aInit.mReferrerPolicy.WasPassed()) {
request->SetReferrerPolicy(aInit.mReferrerPolicy.Value());
}
if (mode != RequestMode::EndGuard_) {
request->ClearCreatedByFetchEvent();
request->SetMode(mode);

View File

@ -99,6 +99,12 @@ public:
mRequest->GetReferrer(aReferrer);
}
ReferrerPolicy
ReferrerPolicy_() const
{
return mRequest->ReferrerPolicy_();
}
InternalHeaders*
GetInternalHeaders() const
{

View File

@ -20,6 +20,7 @@ interface Request {
[Func="mozilla::dom::Request::RequestContextEnabled"]
readonly attribute RequestContext context;
readonly attribute USVString referrer;
readonly attribute ReferrerPolicy referrerPolicy;
readonly attribute RequestMode mode;
readonly attribute RequestCredentials credentials;
[Func="mozilla::dom::Request::RequestCacheEnabled"]
@ -40,6 +41,7 @@ dictionary RequestInit {
HeadersInit headers;
BodyInit? body;
USVString referrer;
ReferrerPolicy referrerPolicy;
RequestMode mode;
RequestCredentials credentials;
RequestCache cache;
@ -60,3 +62,4 @@ enum RequestMode { "same-origin", "no-cors", "cors", "navigate" };
enum RequestCredentials { "omit", "same-origin", "include" };
enum RequestCache { "default", "no-store", "reload", "no-cache", "force-cache" };
enum RequestRedirect { "follow", "error", "manual" };
enum ReferrerPolicy { "", "no-referrer", "no-referrer-when-downgrade", "origin-only", "origin-when-cross-origin", "unsafe-url" };

View File

@ -193,7 +193,7 @@ ChannelFromScriptURL(nsIPrincipal* principal,
if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel)) {
rv = nsContentUtils::SetFetchReferrerURIWithPolicy(principal, parentDoc,
httpChannel);
httpChannel, mozilla::net::RP_Default);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}

View File

@ -990,6 +990,7 @@ class FetchEventRunnable : public ExtendableFunctionalEventWorkerRunnable
nsContentPolicyType mContentPolicyType;
nsCOMPtr<nsIInputStream> mUploadStream;
nsCString mReferrer;
ReferrerPolicy mReferrerPolicy;
public:
FetchEventRunnable(WorkerPrivate* aWorkerPrivate,
KeepAliveToken* aKeepAliveToken,
@ -1013,6 +1014,7 @@ public:
, mRequestCredentials(RequestCredentials::Same_origin)
, mContentPolicyType(nsIContentPolicy::TYPE_INVALID)
, mReferrer(kFETCH_CLIENT_REFERRER_STR)
, mReferrerPolicy(ReferrerPolicy::_empty)
{
MOZ_ASSERT(aWorkerPrivate);
}
@ -1060,17 +1062,40 @@ public:
mContentPolicyType = loadInfo->InternalContentPolicyType();
nsCOMPtr<nsIURI> referrerURI;
rv = NS_GetReferrerFromChannel(channel, getter_AddRefs(referrerURI));
NS_ENSURE_SUCCESS(rv, rv);
if (referrerURI) {
rv = referrerURI->GetSpec(mReferrer);
NS_ENSURE_SUCCESS(rv, rv);
}
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
MOZ_ASSERT(httpChannel, "How come we don't have an HTTP channel?");
nsAutoCString referrer;
// Ignore the return value since the Referer header may not exist.
httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("Referer"), referrer);
if (!referrer.IsEmpty()) {
mReferrer = referrer;
}
uint32_t referrerPolicy = 0;
rv = httpChannel->GetReferrerPolicy(&referrerPolicy);
NS_ENSURE_SUCCESS(rv, rv);
switch (referrerPolicy) {
case nsIHttpChannel::REFERRER_POLICY_NO_REFERRER:
mReferrerPolicy = ReferrerPolicy::No_referrer;
break;
case nsIHttpChannel::REFERRER_POLICY_ORIGIN:
mReferrerPolicy = ReferrerPolicy::Origin_only;
break;
case nsIHttpChannel::REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE:
mReferrerPolicy = ReferrerPolicy::No_referrer_when_downgrade;
break;
case nsIHttpChannel::REFERRER_POLICY_ORIGIN_WHEN_XORIGIN:
mReferrerPolicy = ReferrerPolicy::Origin_when_cross_origin;
break;
case nsIHttpChannel::REFERRER_POLICY_UNSAFE_URL:
mReferrerPolicy = ReferrerPolicy::Unsafe_url;
break;
default:
MOZ_ASSERT_UNREACHABLE("Invalid Referrer Policy enum value?");
break;
}
rv = httpChannel->GetRequestMethod(mMethod);
NS_ENSURE_SUCCESS(rv, rv);
@ -1179,6 +1204,7 @@ private:
mRequestRedirect,
mRequestCredentials,
NS_ConvertUTF8toUTF16(mReferrer),
mReferrerPolicy,
mContentPolicyType);
internalReq->SetBody(mUploadStream);
// For Telemetry, note that this Request object was created by a Fetch event.

View File

@ -6,6 +6,3 @@
[RequestInit's mode is no-cors and integrity is not empty]
expected: FAIL
[Bad referrerPolicy init parameter value]
expected: FAIL

View File

@ -6,9 +6,6 @@
[Request interface: attribute destination]
expected: FAIL
[Request interface: attribute referrerPolicy]
expected: FAIL
[Request interface: attribute cache]
expected: FAIL
@ -21,9 +18,6 @@
[Request interface: new Request("") must inherit property "destination" with the proper type (4)]
expected: FAIL
[Request interface: new Request("") must inherit property "referrerPolicy" with the proper type (6)]
expected: FAIL
[Request interface: new Request("") must inherit property "cache" with the proper type (9)]
expected: FAIL

View File

@ -1,23 +1,5 @@
[request-init-001.sub.html]
type: testharness
[Check referrerPolicy init value of and associated getter]
expected: FAIL
[Check referrerPolicy init value of no-referrer and associated getter]
expected: FAIL
[Check referrerPolicy init value of no-referrer-when-downgrade and associated getter]
expected: FAIL
[Check referrerPolicy init value of origin-only and associated getter]
expected: FAIL
[Check referrerPolicy init value of origin-when-cross-origin and associated getter]
expected: FAIL
[Check referrerPolicy init value of unsafe-url and associated getter]
expected: FAIL
[Check integrity init value of and associated getter]
expected: FAIL

View File

@ -6,9 +6,6 @@
[Check destination attribute]
expected: FAIL
[Check referrerPolicy attribute]
expected: FAIL
[Check cache attribute]
expected: FAIL

View File

@ -0,0 +1,4 @@
[fetch-event.https.html]
type: testharness
prefs: [security.mixed_content.block_active_content:false,
security.mixed_content.block_display_content:false]

View File

@ -1,6 +1,7 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/get-host-info.sub.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<body>
<script>
@ -68,6 +69,171 @@ async_test(function(t) {
.catch(unreached_rejection(t));
}, 'Service Worker responds to fetch event with the referrer URL');
function run_referrer_policy_tests(frame, referrer, href, origin) {
return frame.contentWindow.fetch('resources/simple.html?referrerFull',
{method: "GET", referrer: referrer})
.then(function(response) { return response.text(); })
.then(function(response_text) {
assert_equals(
response_text,
'Referrer: ' + href + '\n' +
'ReferrerPolicy: no-referrer-when-downgrade',
'Service Worker should respond to fetch with the referrer URL when a member of RequestInit is present');
var http_url = get_host_info()['HTTP_ORIGIN'] + base_path() +
'/resources/simple.html?referrerFull';
return frame.contentWindow.fetch(http_url,
{method: "GET", referrer: referrer});
})
.then(function(response) { return response.text(); })
.then(function(response_text) {
assert_equals(
response_text,
'Referrer: about:client\n' +
'ReferrerPolicy: no-referrer-when-downgrade',
'Service Worker should respond to fetch with no referrer when a member of RequestInit is present with an HTTP request');
return frame.contentWindow.fetch('resources/simple.html?referrerFull',
{referrerPolicy: "", referrer: referrer});
})
.then(function(response) { return response.text(); })
.then(function(response_text) {
assert_equals(
response_text,
'Referrer: ' + href + '\n' +
'ReferrerPolicy: no-referrer-when-downgrade',
'Service Worker should respond to fetch with the referrer with ""');
var http_url = get_host_info()['HTTP_ORIGIN'] + base_path() +
'/resources/simple.html?referrerFull';
return frame.contentWindow.fetch(http_url,
{referrerPolicy: "", referrer: referrer});
})
.then(function(response) { return response.text(); })
.then(function(response_text) {
assert_equals(
response_text,
'Referrer: about:client\n' +
'ReferrerPolicy: no-referrer-when-downgrade',
'Service Worker should respond to fetch with no referrer with ""');
return frame.contentWindow.fetch('resources/simple.html?referrerFull',
{referrerPolicy: "origin-only", referrer: referrer});
})
.then(function(response) { return response.text(); })
.then(function(response_text) {
assert_equals(
response_text,
'Referrer: ' + origin + '\n' +
'ReferrerPolicy: origin-only',
'Service Worker should respond to fetch with the referrer origin with "origin-only" and a same origin request');
var http_url = get_host_info()['HTTP_ORIGIN'] + base_path() +
'/resources/simple.html?referrerFull';
return frame.contentWindow.fetch(http_url,
{referrerPolicy: "origin-only", referrer: referrer});
})
.then(function(response) { return response.text(); })
.then(function(response_text) {
assert_equals(
response_text,
'Referrer: ' + origin + '\n' +
'ReferrerPolicy: origin-only',
'Service Worker should respond to fetch with the referrer origin with "origin-only" and a cross origin request');
return frame.contentWindow.fetch('resources/simple.html?referrerFull',
{referrerPolicy: "origin-when-cross-origin", referrer: referrer});
})
.then(function(response) { return response.text(); })
.then(function(response_text) {
assert_equals(
response_text,
'Referrer: ' + href + '\n' +
'ReferrerPolicy: origin-when-cross-origin',
'Service Worker should respond to fetch with the referrer URL with "origin-when-cross-origin" and a same origin request');
var http_url = get_host_info()['HTTP_ORIGIN'] + base_path() +
'/resources/simple.html?referrerFull';
return frame.contentWindow.fetch(http_url,
{referrerPolicy: "origin-when-cross-origin", referrer: referrer});
})
.then(function(response) { return response.text(); })
.then(function(response_text) {
assert_equals(
response_text,
'Referrer: ' + origin + '\n' +
'ReferrerPolicy: origin-when-cross-origin',
'Service Worker should respond to fetch with the referrer origin with "origin-when-cross-origin" and a cross origin request');
return frame.contentWindow.fetch('resources/simple.html?referrerFull',
{referrerPolicy: "no-referrer-when-downgrade", referrer: referrer});
})
.then(function(response) { return response.text(); })
.then(function(response_text) {
assert_equals(
response_text,
'Referrer: ' + href + '\n' +
'ReferrerPolicy: no-referrer-when-downgrade',
'Service Worker should respond to fetch with no referrer with "no-referrer-when-downgrade" and a same origin request');
var http_url = get_host_info()['HTTP_ORIGIN'] + base_path() +
'/resources/simple.html?referrerFull';
return frame.contentWindow.fetch(http_url,
{referrerPolicy: "no-referrer-when-downgrade", referrer: referrer});
})
.then(function(response) { return response.text(); })
.then(function(response_text) {
assert_equals(
response_text,
'Referrer: about:client\n' +
'ReferrerPolicy: no-referrer-when-downgrade',
'Service Worker should respond to fetch with no referrer with "no-referrer-when-downgrade" and an HTTP request');
var http_url = get_host_info()['HTTP_ORIGIN'] + base_path() +
'/resources/simple.html?referrerFull';
return frame.contentWindow.fetch(http_url, {referrerPolicy: "unsafe-url", referrer: referrer});
})
.then(function(response) { return response.text(); })
.then(function(response_text) {
assert_equals(
response_text,
'Referrer: ' + href + '\n' +
'ReferrerPolicy: unsafe-url',
'Service Worker should respond to fetch with no referrer with "unsafe-url"');
return frame.contentWindow.fetch('resources/simple.html?referrerFull',
{referrerPolicy: "no-referrer", referrer: referrer});
})
.then(function(response) { return response.text(); })
.then(function(response_text) {
assert_equals(
response_text,
'Referrer: about:client\n' +
'ReferrerPolicy: no-referrer',
'Service Worker should respond to fetch with no referrer URL with "no-referrer"');
});
}
async_test(function(t) {
var scope = 'resources/simple.html?referrerPolicy';
var frame;
service_worker_unregister_and_register(t, worker, scope)
.then(function(reg) {
return wait_for_state(t, reg.installing, 'activated');
})
.then(function() { return with_iframe(scope); })
.then(function(f) {
frame = f;
assert_equals(
frame.contentDocument.body.textContent,
'ReferrerPolicy: no-referrer-when-downgrade',
'Service Worker should respond to fetch with the default referrer policy');
// First, run the referrer policy tests without passing a referrer in RequestInit.
return run_referrer_policy_tests(frame, undefined, frame.contentDocument.location.href,
frame.contentDocument.location.origin);
})
.then(function() {
// Now, run the referrer policy tests while passing a referrer in RequestInit.
var referrer = get_host_info()['HTTPS_ORIGIN'] + base_path() + 'fake-referrer';
return run_referrer_policy_tests(frame, 'fake-referrer', referrer,
frame.contentDocument.location.origin);
})
.then(function() {
frame.remove();
return service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
}, 'Service Worker responds to fetch event with the referrer URL');
async_test(function(t) {
var scope = 'resources/simple.html?clientId';
var frame;

View File

@ -11,6 +11,17 @@ function handleReferrer(event) {
['Referrer: ' + event.request.referrer])));
}
function handleReferrerPolicy(event) {
event.respondWith(new Response(new Blob(
['ReferrerPolicy: ' + event.request.referrerPolicy])));
}
function handleReferrerFull(event) {
event.respondWith(new Response(new Blob(
['Referrer: ' + event.request.referrer + '\n' +
'ReferrerPolicy: ' + event.request.referrerPolicy])));
}
function handleClientId(event) {
var body;
if (event.clientId !== null) {
@ -85,6 +96,8 @@ self.addEventListener('fetch', function(event) {
var handlers = [
{ pattern: '?string', fn: handleString },
{ pattern: '?blob', fn: handleBlob },
{ pattern: '?referrerFull', fn: handleReferrerFull },
{ pattern: '?referrerPolicy', fn: handleReferrerPolicy },
{ pattern: '?referrer', fn: handleReferrer },
{ pattern: '?clientId', fn: handleClientId },
{ pattern: '?ignore', fn: function() {} },

View File

@ -70,6 +70,7 @@
enum RequestCredentials { "omit", "same-origin", "include" };
enum RequestCache { "default", "no-store", "reload", "no-cache", "force-cache", "only-if-cached" };
enum RequestRedirect { "follow", "error", "manual" };
enum ReferrerPolicy { "", "no-referrer", "no-referrer-when-downgrade", "origin-only", "origin-when-cross-origin", "unsafe-url" };
</script>
<script>
var idlsArray = new IdlArray();