mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 15:52:07 +00:00
Bug 1112922 - Implement request referrer correctly in Fetch API. r=bkelly
--HG-- extra : rebase_source : 226756fee8b777ed30b07cce0f3c5879d66ccf80
This commit is contained in:
parent
98a481bdeb
commit
bf76a3c4e1
@ -28,6 +28,7 @@
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "mozilla/dom/URLSearchParams.h"
|
||||
|
||||
#include "InternalRequest.h"
|
||||
#include "InternalResponse.h"
|
||||
|
||||
#include "WorkerPrivate.h"
|
||||
@ -173,6 +174,11 @@ public:
|
||||
nsCOMPtr<nsIPrincipal> principal = mResolver->GetWorkerPrivate()->GetPrincipal();
|
||||
nsCOMPtr<nsILoadGroup> loadGroup = mResolver->GetWorkerPrivate()->GetLoadGroup();
|
||||
nsRefPtr<FetchDriver> fetch = new FetchDriver(mRequest, principal, loadGroup);
|
||||
nsIDocument* doc = mResolver->GetWorkerPrivate()->GetDocument();
|
||||
if (doc) {
|
||||
fetch->SetReferrerPolicy(doc->GetReferrerPolicy());
|
||||
}
|
||||
|
||||
nsresult rv = fetch->Fetch(mResolver);
|
||||
// Right now we only support async fetch, which should never directly fail.
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
@ -204,13 +210,10 @@ FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput,
|
||||
}
|
||||
|
||||
nsRefPtr<InternalRequest> r = request->GetInternalRequest();
|
||||
if (!r->ReferrerIsNone()) {
|
||||
nsAutoCString ref;
|
||||
aRv = GetRequestReferrer(aGlobal, r, ref);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
r->SetReferrer(ref);
|
||||
|
||||
aRv = UpdateRequestReferrer(aGlobal, r);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
@ -230,6 +233,7 @@ FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput,
|
||||
nsCOMPtr<nsILoadGroup> loadGroup = doc->GetDocumentLoadGroup();
|
||||
nsRefPtr<FetchDriver> fetch =
|
||||
new FetchDriver(r, doc->NodePrincipal(), loadGroup);
|
||||
fetch->SetReferrerPolicy(doc->GetReferrerPolicy());
|
||||
aRv = fetch->Fetch(resolver);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
@ -357,15 +361,19 @@ WorkerFetchResolver::OnResponseEnd()
|
||||
}
|
||||
}
|
||||
|
||||
// Empty string for no-referrer. FIXME(nsm): Does returning empty string
|
||||
// actually lead to no-referrer in the base channel?
|
||||
// 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 returns the full API referrer URL of the relevant global.
|
||||
// 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
|
||||
GetRequestReferrer(nsIGlobalObject* aGlobal, const InternalRequest* aRequest, nsCString& aReferrer)
|
||||
UpdateRequestReferrer(nsIGlobalObject* aGlobal, InternalRequest* aRequest)
|
||||
{
|
||||
if (aRequest->ReferrerIsURL()) {
|
||||
aReferrer = aRequest->ReferrerAsURL();
|
||||
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;
|
||||
}
|
||||
|
||||
@ -373,24 +381,16 @@ GetRequestReferrer(nsIGlobalObject* aGlobal, const InternalRequest* aRequest, ns
|
||||
if (window) {
|
||||
nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
|
||||
if (doc) {
|
||||
nsCOMPtr<nsIURI> docURI = doc->GetDocumentURI();
|
||||
nsAutoCString origin;
|
||||
nsresult rv = nsContentUtils::GetASCIIOrigin(docURI, origin);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsAutoString referrer;
|
||||
doc->GetReferrer(referrer);
|
||||
aReferrer = NS_ConvertUTF16toUTF8(referrer);
|
||||
aRequest->SetReferrer(referrer);
|
||||
}
|
||||
} else {
|
||||
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(worker);
|
||||
worker->AssertIsOnWorkerThread();
|
||||
aReferrer = worker->GetLocationInfo().mHref;
|
||||
// XXX(nsm): Algorithm says "If source is not a URL..." but when is it
|
||||
// not a URL?
|
||||
WorkerPrivate::LocationInfo& info = worker->GetLocationInfo();
|
||||
aRequest->SetReferrer(NS_ConvertUTF8toUTF16(info.mHref));
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -40,7 +40,7 @@ FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput,
|
||||
const RequestInit& aInit, ErrorResult& aRv);
|
||||
|
||||
nsresult
|
||||
GetRequestReferrer(nsIGlobalObject* aGlobal, const InternalRequest* aRequest, nsCString& aReferrer);
|
||||
UpdateRequestReferrer(nsIGlobalObject* aGlobal, InternalRequest* aRequest);
|
||||
|
||||
/*
|
||||
* Creates an nsIInputStream based on the fetch specifications 'extract a byte
|
||||
|
@ -41,6 +41,7 @@ FetchDriver::FetchDriver(InternalRequest* aRequest, nsIPrincipal* aPrincipal,
|
||||
, mLoadGroup(aLoadGroup)
|
||||
, mRequest(aRequest)
|
||||
, mFetchRecursionCount(0)
|
||||
, mReferrerPolicy(net::RP_Default)
|
||||
, mResponseAvailableCalled(false)
|
||||
{
|
||||
}
|
||||
@ -386,16 +387,19 @@ FetchDriver::HttpFetch(bool aCORSFlag, bool aCORSPreflightFlag, bool aAuthentica
|
||||
httpChan->SetRequestHeader(headers[i].mName, headers[i].mValue, false /* merge */);
|
||||
}
|
||||
|
||||
// Step 2. Set the referrer. This is handled better in Bug 1112922.
|
||||
MOZ_ASSERT(mRequest->ReferrerIsURL());
|
||||
nsCString referrer = mRequest->ReferrerAsURL();
|
||||
// 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> uri;
|
||||
rv = NS_NewURI(getter_AddRefs(uri), referrer, nullptr, nullptr, ios);
|
||||
nsCOMPtr<nsIURI> refURI;
|
||||
rv = NS_NewURI(getter_AddRefs(refURI), referrer, nullptr, nullptr);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return FailWithNetworkError();
|
||||
}
|
||||
rv = httpChan->SetReferrer(uri);
|
||||
|
||||
rv = httpChan->SetReferrerWithPolicy(refURI, mReferrerPolicy);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return FailWithNetworkError();
|
||||
}
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "nsRefPtr.h"
|
||||
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/net/ReferrerPolicy.h"
|
||||
|
||||
class nsIOutputStream;
|
||||
class nsILoadGroup;
|
||||
@ -56,6 +57,14 @@ public:
|
||||
nsILoadGroup* aLoadGroup);
|
||||
NS_IMETHOD Fetch(FetchDriverObserver* aObserver);
|
||||
|
||||
void
|
||||
SetReferrerPolicy(net::ReferrerPolicy aPolicy)
|
||||
{
|
||||
// Cannot set policy after Fetch() has been called.
|
||||
MOZ_ASSERT(mFetchRecursionCount == 0);
|
||||
mReferrerPolicy = aPolicy;
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
nsCOMPtr<nsILoadGroup> mLoadGroup;
|
||||
@ -68,6 +77,7 @@ private:
|
||||
nsCOMPtr<nsIChannel> mOldRedirectChannel;
|
||||
nsCOMPtr<nsIChannel> mNewRedirectChannel;
|
||||
uint32_t mFetchRecursionCount;
|
||||
net::ReferrerPolicy mReferrerPolicy;
|
||||
|
||||
DebugOnly<bool> mResponseAvailableCalled;
|
||||
|
||||
|
@ -13,6 +13,11 @@
|
||||
#include "nsIContentPolicy.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#ifdef DEBUG
|
||||
#include "nsIURLParser.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#endif
|
||||
|
||||
class nsIDocument;
|
||||
class nsPIDOMWindow;
|
||||
@ -23,6 +28,8 @@ namespace dom {
|
||||
class FetchBodyStream;
|
||||
class Request;
|
||||
|
||||
#define kFETCH_CLIENT_REFERRER_STR "about:client"
|
||||
|
||||
class InternalRequest MOZ_FINAL
|
||||
{
|
||||
friend class Request;
|
||||
@ -38,14 +45,6 @@ public:
|
||||
FRAMETYPE_NONE,
|
||||
};
|
||||
|
||||
// Since referrer type can be none, client or a URL.
|
||||
enum ReferrerType
|
||||
{
|
||||
REFERRER_NONE = 0,
|
||||
REFERRER_CLIENT,
|
||||
REFERRER_URL,
|
||||
};
|
||||
|
||||
enum ResponseTainting
|
||||
{
|
||||
RESPONSETAINT_BASIC,
|
||||
@ -57,7 +56,7 @@ public:
|
||||
: mMethod("GET")
|
||||
, mHeaders(new InternalHeaders(HeadersGuardEnum::None))
|
||||
, mContextFrameType(FRAMETYPE_NONE)
|
||||
, mReferrerType(REFERRER_CLIENT)
|
||||
, mReferrer(NS_LITERAL_STRING(kFETCH_CLIENT_REFERRER_STR))
|
||||
, mMode(RequestMode::No_cors)
|
||||
, mCredentialsMode(RequestCredentials::Omit)
|
||||
, mResponseTainting(RESPONSETAINT_BASIC)
|
||||
@ -84,8 +83,7 @@ public:
|
||||
, mBodyStream(aOther.mBodyStream)
|
||||
, mContext(aOther.mContext)
|
||||
, mContextFrameType(aOther.mContextFrameType)
|
||||
, mReferrerType(aOther.mReferrerType)
|
||||
, mReferrerURL(aOther.mReferrerURL)
|
||||
, mReferrer(aOther.mReferrer)
|
||||
, mMode(aOther.mMode)
|
||||
, mCredentialsMode(aOther.mCredentialsMode)
|
||||
, mResponseTainting(aOther.mResponseTainting)
|
||||
@ -134,38 +132,51 @@ public:
|
||||
mURL.Assign(aURL);
|
||||
}
|
||||
|
||||
bool
|
||||
ReferrerIsNone() const
|
||||
void
|
||||
GetReferrer(nsAString& aReferrer) const
|
||||
{
|
||||
return mReferrerType == REFERRER_NONE;
|
||||
}
|
||||
|
||||
bool
|
||||
ReferrerIsURL() const
|
||||
{
|
||||
return mReferrerType == REFERRER_URL;
|
||||
}
|
||||
|
||||
bool
|
||||
ReferrerIsClient() const
|
||||
{
|
||||
return mReferrerType == REFERRER_CLIENT;
|
||||
}
|
||||
|
||||
nsCString
|
||||
ReferrerAsURL() const
|
||||
{
|
||||
MOZ_ASSERT(ReferrerIsURL());
|
||||
return mReferrerURL;
|
||||
aReferrer.Assign(mReferrer);
|
||||
}
|
||||
|
||||
void
|
||||
SetReferrer(const nsACString& aReferrer)
|
||||
SetReferrer(const nsAString& aReferrer)
|
||||
{
|
||||
// May be removed later.
|
||||
MOZ_ASSERT(!ReferrerIsNone());
|
||||
mReferrerType = REFERRER_URL;
|
||||
mReferrerURL.Assign(aReferrer);
|
||||
#ifdef DEBUG
|
||||
bool validReferrer = false;
|
||||
if (aReferrer.IsEmpty() ||
|
||||
aReferrer.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR)) {
|
||||
validReferrer = true;
|
||||
} else {
|
||||
nsCOMPtr<nsIURLParser> parser = do_GetService(NS_STDURLPARSER_CONTRACTID);
|
||||
if (!parser) {
|
||||
NS_WARNING("Could not get parser to validate URL!");
|
||||
} else {
|
||||
uint32_t schemePos;
|
||||
int32_t schemeLen;
|
||||
uint32_t authorityPos;
|
||||
int32_t authorityLen;
|
||||
uint32_t pathPos;
|
||||
int32_t pathLen;
|
||||
|
||||
NS_ConvertUTF16toUTF8 ref(aReferrer);
|
||||
nsresult rv = parser->ParseURL(ref.get(), ref.Length(),
|
||||
&schemePos, &schemeLen,
|
||||
&authorityPos, &authorityLen,
|
||||
&pathPos, &pathLen);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Invalid referrer URL!");
|
||||
} else if (schemeLen < 0 || authorityLen < 0) {
|
||||
NS_WARNING("Invalid referrer URL!");
|
||||
} else {
|
||||
validReferrer = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(validReferrer);
|
||||
#endif
|
||||
|
||||
mReferrer.Assign(aReferrer);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -292,10 +303,11 @@ private:
|
||||
nsContentPolicyType mContext;
|
||||
|
||||
ContextFrameType mContextFrameType;
|
||||
ReferrerType mReferrerType;
|
||||
|
||||
// When mReferrerType is REFERRER_URL.
|
||||
nsCString mReferrerURL;
|
||||
// Empty string: no-referrer
|
||||
// "about:client": client (default)
|
||||
// URL: an URL
|
||||
nsString mReferrer;
|
||||
|
||||
RequestMode mMode;
|
||||
RequestCredentials mCredentialsMode;
|
||||
|
@ -75,15 +75,9 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
GetReferrer(DOMString& aReferrer) const
|
||||
GetReferrer(nsAString& aReferrer) const
|
||||
{
|
||||
if (mRequest->ReferrerIsNone()) {
|
||||
aReferrer.AsAString() = EmptyString();
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME(nsm): Spec doesn't say what to do if referrer is client.
|
||||
aReferrer.AsAString() = NS_ConvertUTF8toUTF16(mRequest->mReferrerURL);
|
||||
mRequest->GetReferrer(aReferrer);
|
||||
}
|
||||
|
||||
InternalHeaders*
|
||||
|
@ -11,7 +11,7 @@ function testDefaultCtor() {
|
||||
is(req.method, "GET", "Default Request method is GET");
|
||||
ok(req.headers instanceof Headers, "Request should have non-null Headers object");
|
||||
is(req.url, self.location.href, "URL should be resolved with entry settings object's API base URL");
|
||||
is(req.referrer, "", "Default referrer is `client` which serializes to empty string.");
|
||||
is(req.referrer, "about:client", "Default referrer is `client` which serializes to about:client.");
|
||||
is(req.mode, "cors", "Request mode for string input is cors");
|
||||
is(req.credentials, "omit", "Default Request credentials is omit");
|
||||
|
||||
@ -19,7 +19,7 @@ function testDefaultCtor() {
|
||||
is(req.method, "GET", "Default Request method is GET");
|
||||
ok(req.headers instanceof Headers, "Request should have non-null Headers object");
|
||||
is(req.url, self.location.href, "URL should be resolved with entry settings object's API base URL");
|
||||
is(req.referrer, "", "Default referrer is `client` which serializes to empty string.");
|
||||
is(req.referrer, "about:client", "Default referrer is `client` which serializes to about:client.");
|
||||
is(req.mode, "cors", "Request mode string input is cors");
|
||||
is(req.credentials, "omit", "Default Request credentials is omit");
|
||||
}
|
||||
@ -37,7 +37,7 @@ function testClone() {
|
||||
is(req.headers.get('content-length'), "5", "Request content-length should be 5.");
|
||||
ok(req.url === (new URL("./cloned_request.txt", self.location.href)).href,
|
||||
"URL should be resolved with entry settings object's API base URL");
|
||||
ok(req.referrer === "", "Default referrer is `client` which serializes to empty string.");
|
||||
ok(req.referrer === "about:client", "Default referrer is `client` which serializes to about:client.");
|
||||
ok(req.mode === "same-origin", "Request mode is same-origin");
|
||||
ok(req.credentials === "same-origin", "Default credentials is same-origin");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user