зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1169044 - Patch 1 - Refactor setting referrer and referrer policy between fetch and XHR. r=khuey
--HG-- extra : rebase_source : 8b004536dab18706ae4b8c3f0c574f8769c05638
This commit is contained in:
Родитель
0c45c2cb42
Коммит
bd1323708a
|
@ -7862,3 +7862,54 @@ nsContentUtils::InternalContentPolicyTypeToExternal(nsContentPolicyType aType)
|
|||
return aType;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsContentUtils::SetFetchReferrerURIWithPolicy(nsIPrincipal* aPrincipal,
|
||||
nsIDocument* aDoc,
|
||||
nsIHttpChannel* aChannel)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aPrincipal);
|
||||
NS_ENSURE_ARG_POINTER(aChannel);
|
||||
|
||||
nsCOMPtr<nsIURI> principalURI;
|
||||
|
||||
if (IsSystemPrincipal(aPrincipal)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
aPrincipal->GetURI(getter_AddRefs(principalURI));
|
||||
|
||||
if (!aDoc) {
|
||||
return aChannel->SetReferrerWithPolicy(principalURI, net::RP_Default);
|
||||
}
|
||||
|
||||
// If it weren't for history.push/replaceState, we could just use the
|
||||
// principal's URI here. But since we want changes to the URI effected
|
||||
// by push/replaceState to be reflected in the XHR referrer, we have to
|
||||
// be more clever.
|
||||
//
|
||||
// If the document's original URI (before any push/replaceStates) matches
|
||||
// our principal, then we use the document's current URI (after
|
||||
// push/replaceStates). Otherwise (if the document is, say, a data:
|
||||
// URI), we just use the principal's URI.
|
||||
nsCOMPtr<nsIURI> docCurURI = aDoc->GetDocumentURI();
|
||||
nsCOMPtr<nsIURI> docOrigURI = aDoc->GetOriginalURI();
|
||||
|
||||
nsCOMPtr<nsIURI> referrerURI;
|
||||
|
||||
if (principalURI && docCurURI && docOrigURI) {
|
||||
bool equal = false;
|
||||
principalURI->Equals(docOrigURI, &equal);
|
||||
if (equal) {
|
||||
referrerURI = docCurURI;
|
||||
}
|
||||
}
|
||||
|
||||
if (!referrerURI) {
|
||||
referrerURI = principalURI;
|
||||
}
|
||||
|
||||
net::ReferrerPolicy referrerPolicy = aDoc->GetReferrerPolicy();
|
||||
return aChannel->SetReferrerWithPolicy(referrerURI, referrerPolicy);
|
||||
}
|
|
@ -2406,6 +2406,25 @@ public:
|
|||
|
||||
static already_AddRefed<nsPIWindowRoot> GetWindowRoot(nsIDocument* aDoc);
|
||||
|
||||
/*
|
||||
* Implements step 3.1 and 3.3 of the Determine request's Referrer algorithm
|
||||
* from the Referrer Policy specification.
|
||||
*
|
||||
* The referrer policy of the document is applied by Necko when using
|
||||
* channels.
|
||||
*
|
||||
* For documents representing an iframe srcdoc attribute, the document sets
|
||||
* its own URI correctly, so this method simply uses the document's original
|
||||
* or current URI as appropriate.
|
||||
*
|
||||
* aDoc may be null.
|
||||
*
|
||||
* https://w3c.github.io/webappsec/specs/referrer-policy/#determine-requests-referrer
|
||||
*/
|
||||
static nsresult SetFetchReferrerURIWithPolicy(nsIPrincipal* aPrincipal,
|
||||
nsIDocument* aDoc,
|
||||
nsIHttpChannel* aChannel);
|
||||
|
||||
private:
|
||||
static bool InitializeEventTable();
|
||||
|
||||
|
|
|
@ -2682,50 +2682,10 @@ nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable<RequestBody>& aBody)
|
|||
httpChannel->GetRequestMethod(method); // If GET, method name will be uppercase
|
||||
|
||||
if (!IsSystemXHR()) {
|
||||
// Get the referrer for the request.
|
||||
//
|
||||
// If it weren't for history.push/replaceState, we could just use the
|
||||
// principal's URI here. But since we want changes to the URI effected
|
||||
// by push/replaceState to be reflected in the XHR referrer, we have to
|
||||
// be more clever.
|
||||
//
|
||||
// If the document's original URI (before any push/replaceStates) matches
|
||||
// our principal, then we use the document's current URI (after
|
||||
// push/replaceStates). Otherwise (if the document is, say, a data:
|
||||
// URI), we just use the principal's URI.
|
||||
|
||||
nsCOMPtr<nsIURI> principalURI;
|
||||
mPrincipal->GetURI(getter_AddRefs(principalURI));
|
||||
|
||||
nsIScriptContext* sc = GetContextForEventHandlers(&rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCOMPtr<nsIDocument> doc =
|
||||
nsContentUtils::GetDocumentFromScriptContext(sc);
|
||||
|
||||
nsCOMPtr<nsIURI> docCurURI;
|
||||
nsCOMPtr<nsIURI> docOrigURI;
|
||||
net::ReferrerPolicy referrerPolicy = net::RP_Default;
|
||||
|
||||
if (doc) {
|
||||
docCurURI = doc->GetDocumentURI();
|
||||
docOrigURI = doc->GetOriginalURI();
|
||||
referrerPolicy = doc->GetReferrerPolicy();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> referrerURI;
|
||||
|
||||
if (principalURI && docCurURI && docOrigURI) {
|
||||
bool equal = false;
|
||||
principalURI->Equals(docOrigURI, &equal);
|
||||
if (equal) {
|
||||
referrerURI = docCurURI;
|
||||
}
|
||||
}
|
||||
|
||||
if (!referrerURI)
|
||||
referrerURI = principalURI;
|
||||
|
||||
httpChannel->SetReferrerWithPolicy(referrerURI, referrerPolicy);
|
||||
nsCOMPtr<nsPIDOMWindow> owner = GetOwner();
|
||||
nsCOMPtr<nsIDocument> doc = owner ? owner->GetExtantDoc() : nullptr;
|
||||
nsContentUtils::SetFetchReferrerURIWithPolicy(mPrincipal, doc,
|
||||
httpChannel);
|
||||
}
|
||||
|
||||
// Some extensions override the http protocol handler and provide their own
|
||||
|
|
|
@ -217,11 +217,6 @@ FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput,
|
|||
|
||||
nsRefPtr<InternalRequest> r = request->GetInternalRequest();
|
||||
|
||||
aRv = UpdateRequestReferrer(aGlobal, r);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal);
|
||||
nsCOMPtr<nsIDocument> doc;
|
||||
|
@ -398,58 +393,6 @@ WorkerFetchResolver::OnResponseEnd()
|
|||
}
|
||||
}
|
||||
|
||||
// This method sets the request's referrerURL, as specified by the "determine
|
||||
// request's referrer" steps from Referrer Policy [1].
|
||||
// The actual referrer policy and stripping is dealt with by HttpBaseChannel,
|
||||
// this always sets the full API referrer URL of the relevant global if it is
|
||||
// not already a url or no-referrer.
|
||||
// [1]: https://w3c.github.io/webappsec/specs/referrer-policy/#determine-requests-referrer
|
||||
nsresult
|
||||
UpdateRequestReferrer(nsIGlobalObject* aGlobal, InternalRequest* aRequest)
|
||||
{
|
||||
nsAutoString originalReferrer;
|
||||
aRequest->GetReferrer(originalReferrer);
|
||||
// If it is no-referrer ("") or a URL, don't modify.
|
||||
if (!originalReferrer.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal);
|
||||
if (window) {
|
||||
nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
|
||||
if (doc) {
|
||||
nsAutoString referrer;
|
||||
doc->GetReferrer(referrer);
|
||||
aRequest->SetReferrer(referrer);
|
||||
}
|
||||
} else if (NS_IsMainThread()) {
|
||||
// Pull the principal from the global for non-worker scripts.
|
||||
nsIPrincipal *principal = aGlobal->PrincipalOrNull();
|
||||
bool isNull;
|
||||
// Only set the referrer if the principal is present,
|
||||
// and the principal is not null or the system principal.
|
||||
if (principal &&
|
||||
NS_SUCCEEDED(principal->GetIsNullPrincipal(&isNull)) && !isNull &&
|
||||
!nsContentUtils::IsSystemPrincipal(principal)) {
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
if (NS_SUCCEEDED(principal->GetURI(getter_AddRefs(uri))) && uri) {
|
||||
nsAutoCString referrer;
|
||||
if (NS_SUCCEEDED(uri->GetSpec(referrer))) {
|
||||
aRequest->SetReferrer(NS_ConvertUTF8toUTF16(referrer));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(worker);
|
||||
worker->AssertIsOnWorkerThread();
|
||||
WorkerPrivate::LocationInfo& info = worker->GetLocationInfo();
|
||||
aRequest->SetReferrer(NS_ConvertUTF8toUTF16(info.mHref));
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
namespace {
|
||||
nsresult
|
||||
ExtractFromArrayBuffer(const ArrayBuffer& aBuffer,
|
||||
|
|
|
@ -416,21 +416,37 @@ FetchDriver::HttpFetch(bool aCORSFlag, bool aCORSPreflightFlag, bool aAuthentica
|
|||
// Step 2. Set the referrer.
|
||||
nsAutoString referrer;
|
||||
mRequest->GetReferrer(referrer);
|
||||
// The referrer should have already been resolved to a URL by the caller.
|
||||
MOZ_ASSERT(!referrer.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR));
|
||||
if (!referrer.IsEmpty()) {
|
||||
nsCOMPtr<nsIURI> refURI;
|
||||
rv = NS_NewURI(getter_AddRefs(refURI), referrer, nullptr, nullptr);
|
||||
if (referrer.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR)) {
|
||||
rv = nsContentUtils::SetFetchReferrerURIWithPolicy(mPrincipal,
|
||||
mDocument,
|
||||
httpChan);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return FailWithNetworkError();
|
||||
}
|
||||
} else if (referrer.IsEmpty()) {
|
||||
rv = httpChan->SetReferrerWithPolicy(nullptr, net::RP_No_Referrer);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return FailWithNetworkError();
|
||||
}
|
||||
} else {
|
||||
// From "Determine request's Referrer" step 3
|
||||
// "If request's referrer is a URL, let referrerSource be request's
|
||||
// referrer."
|
||||
//
|
||||
// This allows ServiceWorkers to function transparently when the referrer
|
||||
// of the intercepted request is already set.
|
||||
nsCOMPtr<nsIURI> referrerURI;
|
||||
rv = NS_NewURI(getter_AddRefs(referrerURI), referrer, nullptr, nullptr);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return FailWithNetworkError();
|
||||
}
|
||||
|
||||
net::ReferrerPolicy referrerPolicy = net::RP_Default;
|
||||
if (mDocument) {
|
||||
referrerPolicy = mDocument->GetReferrerPolicy();
|
||||
}
|
||||
|
||||
rv = httpChan->SetReferrerWithPolicy(refURI, referrerPolicy);
|
||||
// FIXME(nsm): Can we assert that this case can only happen in
|
||||
// ServiceWorkers and assume null mDocument?
|
||||
rv =
|
||||
httpChan->SetReferrerWithPolicy(nullptr,
|
||||
mDocument ? mDocument->GetReferrerPolicy() :
|
||||
net::RP_Default);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return FailWithNetworkError();
|
||||
}
|
||||
|
|
|
@ -1250,6 +1250,25 @@ function testRedirects() {
|
|||
return Promise.all(fetches);
|
||||
}
|
||||
|
||||
function testReferrer() {
|
||||
var referrer;
|
||||
if (self && self.location) {
|
||||
referrer = self.location.href;
|
||||
} else {
|
||||
referrer = document.documentURI;
|
||||
}
|
||||
|
||||
var dict = {
|
||||
'Referer': referrer
|
||||
};
|
||||
return fetch(corsServerPath + "headers=" + dict.toSource()).then(function(res) {
|
||||
is(res.status, 200, "expected correct referrer header to be sent");
|
||||
dump(res.statusText);
|
||||
}, function(e) {
|
||||
ok(false, "expected correct referrer header to be sent");
|
||||
});
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
testNoCorsCtor();
|
||||
|
||||
|
@ -1260,5 +1279,6 @@ function runTest() {
|
|||
.then(testSameOriginCredentials)
|
||||
.then(testCrossOriginCredentials)
|
||||
.then(testRedirects)
|
||||
.then(testReferrer)
|
||||
// Put more promise based tests here.
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче