Bug 1251872 - Part 1: Implement Request.referrerPolicy; r=jdm
authorEhsan Akhgari <ehsan@mozilla.com>
Fri, 26 Feb 2016 17:36:45 -0500
changeset 286663 a87e26a12a988360cbfdc2d3773ec7d83a84712d
parent 286662 7488a41b7ca5e04cdbd970c82b21bd1aa38706c5
child 286664 4d0d6bb9abee9f527a96ee2f5ddc308ce961acbe
push id72873
push usereakhgari@mozilla.com
push dateThu, 03 Mar 2016 21:44:36 +0000
treeherdermozilla-inbound@4d0d6bb9abee [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdm
bugs1251872
milestone47.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1251872 - Part 1: Implement Request.referrerPolicy; r=jdm
dom/base/nsContentUtils.cpp
dom/base/nsContentUtils.h
dom/base/nsXMLHttpRequest.cpp
dom/bindings/Bindings.conf
dom/fetch/FetchDriver.cpp
dom/fetch/InternalRequest.cpp
dom/fetch/InternalRequest.h
dom/fetch/Request.cpp
dom/fetch/Request.h
dom/webidl/Request.webidl
dom/workers/ScriptLoader.cpp
dom/workers/ServiceWorkerPrivate.cpp
testing/web-platform/meta/fetch/api/request/request-error.html.ini
testing/web-platform/meta/fetch/api/request/request-idl.html.ini
testing/web-platform/meta/fetch/api/request/request-init-001.sub.html.ini
testing/web-platform/meta/fetch/api/request/request-structure.html.ini
testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-event.https.html.ini
testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event.https.html
testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-event-test-worker.js
testing/web-platform/tests/fetch/api/request/request-idl.html
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -8125,31 +8125,32 @@ nsContentUtils::IsPreloadType(nsContentP
     return true;
   }
   return false;
 }
 
 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);
 
   nsCOMPtr<nsIURI> principalURI;
 
   if (IsSystemPrincipal(aPrincipal)) {
     return NS_OK;
   }
 
   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
   // 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
@@ -8168,17 +8169,20 @@ nsContentUtils::SetFetchReferrerURIWithP
       referrerURI = docCurURI;
     }
   }
 
   if (!referrerURI) {
     referrerURI = principalURI;
   }
 
-  net::ReferrerPolicy referrerPolicy = aDoc->GetReferrerPolicy();
+  net::ReferrerPolicy referrerPolicy = aReferrerPolicy;
+  if (referrerPolicy == net::RP_Default) {
+    referrerPolicy = aDoc->GetReferrerPolicy();
+  }
   return aChannel->SetReferrerWithPolicy(referrerURI, referrerPolicy);
 }
 
 // static
 bool
 nsContentUtils::PushEnabled(JSContext* aCx, JSObject* aObj)
 {
   if (NS_IsMainThread()) {
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2518,17 +2518,18 @@ public:
    * 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);
+                                                nsIHttpChannel* aChannel,
+                                                mozilla::net::ReferrerPolicy aReferrerPolicy);
 
   static bool PushEnabled(JSContext* aCx, JSObject* aObj);
 
   static bool IsNonSubresourceRequest(nsIChannel* aChannel);
 
   static uint32_t CookiesBehavior()
   {
     return sCookiesBehavior;
--- a/dom/base/nsXMLHttpRequest.cpp
+++ b/dom/base/nsXMLHttpRequest.cpp
@@ -2559,17 +2559,17 @@ nsXMLHttpRequest::Send(nsIVariant* aVari
 
   if (httpChannel) {
     httpChannel->GetRequestMethod(method); // If GET, method name will be uppercase
 
     if (!IsSystemXHR()) {
       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
     // implementation. The channels returned from that implementation doesn't
     // seem to always implement the nsIUploadChannel2 interface, presumably
     // because it's a new interface.
     // Eventually we should remove this and simply require that http channels
     // implement the new interface.
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -975,17 +975,20 @@ DOMInterfaces = {
     }
 },
 
 'Rect': {
     'nativeType': 'nsDOMCSSRect',
 },
 
 'Request': {
-    'binaryNames': { 'headers': 'headers_' },
+    'binaryNames': {
+        'headers': 'headers_',
+        'referrerPolicy': 'referrerPolicy_'
+    },
 },
 
 'Response': {
     'binaryNames': { 'headers': 'headers_' },
 },
 
 'RGBColor': {
     'nativeType': 'nsDOMCSSRGBColor',
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -260,41 +260,65 @@ FetchDriver::HttpFetch()
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Set the same headers.
     SetRequestHeaders(httpChan);
 
     // 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);
       NS_ENSURE_SUCCESS(rv, rv);
     } else {
       // 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);
     }
 
     // Bug 1120722 - Authorization will be handled later.
     // Auth may require prompting, we don't support it yet.
     // The next patch in this same bug prevents this from aborting the request.
     // Credentials checks for CORS are handled by nsCORSListenerProxy,
 
--- a/dom/fetch/InternalRequest.cpp
+++ b/dom/fetch/InternalRequest.cpp
@@ -32,16 +32,17 @@ InternalRequest::GetRequestConstructorCo
   copy->mBodyStream = mBodyStream;
   copy->mForceOriginHeader = true;
   // The "client" is not stored in our implementation. Fetch API users should
   // use the appropriate window/document/principal and other Gecko security
   // mechanisms as appropriate.
   copy->mSameOriginDataURL = true;
   copy->mPreserveContentCodings = true;
   // The default referrer is already about:client.
+  copy->mReferrerPolicy = mReferrerPolicy;
 
   copy->mContentPolicyType = nsIContentPolicy::TYPE_FETCH;
   copy->mMode = mMode;
   copy->mCredentialsMode = mCredentialsMode;
   copy->mCacheMode = mCacheMode;
   copy->mRedirectMode = mRedirectMode;
   copy->mCreatedByFetchEvent = mCreatedByFetchEvent;
   return copy.forget();
@@ -72,16 +73,17 @@ InternalRequest::Clone()
 }
 
 InternalRequest::InternalRequest(const InternalRequest& aOther)
   : mMethod(aOther.mMethod)
   , mURL(aOther.mURL)
   , mHeaders(new InternalHeaders(*aOther.mHeaders))
   , mContentPolicyType(aOther.mContentPolicyType)
   , mReferrer(aOther.mReferrer)
+  , mReferrerPolicy(aOther.mReferrerPolicy)
   , mMode(aOther.mMode)
   , mCredentialsMode(aOther.mCredentialsMode)
   , mResponseTainting(aOther.mResponseTainting)
   , mCacheMode(aOther.mCacheMode)
   , mRedirectMode(aOther.mRedirectMode)
   , mAuthenticationFlag(aOther.mAuthenticationFlag)
   , mForceOriginHeader(aOther.mForceOriginHeader)
   , mPreserveContentCodings(aOther.mPreserveContentCodings)
--- a/dom/fetch/InternalRequest.h
+++ b/dom/fetch/InternalRequest.h
@@ -88,16 +88,17 @@ class InternalRequest final
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(InternalRequest)
 
   InternalRequest()
     : mMethod("GET")
     , 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)
     , mCacheMode(RequestCache::Default)
     , mRedirectMode(RequestRedirect::Follow)
     , mAuthenticationFlag(false)
     , mForceOriginHeader(false)
     , mPreserveContentCodings(false)
@@ -115,22 +116,24 @@ public:
 
   InternalRequest(const nsACString& aURL,
                   const nsACString& aMethod,
                   already_AddRefed<InternalHeaders> aHeaders,
                   RequestMode aMode,
                   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)
     , mCacheMode(RequestCache::Default)
     , mRedirectMode(aRequestRedirect)
     , mAuthenticationFlag(false)
     , mForceOriginHeader(false)
     , mPreserveContentCodings(false)
@@ -225,16 +228,28 @@ public:
     }
 
     MOZ_ASSERT(validReferrer);
 #endif
 
     mReferrer.Assign(aReferrer);
   }
 
+  ReferrerPolicy
+  ReferrerPolicy_() const
+  {
+    return mReferrerPolicy;
+  }
+
+  void
+  SetReferrerPolicy(ReferrerPolicy aReferrerPolicy)
+  {
+    mReferrerPolicy = aReferrerPolicy;
+  }
+
   bool
   SkipServiceWorker() const
   {
     return mSkipServiceWorker;
   }
 
   void
   SetSkipServiceWorker()
@@ -437,16 +452,17 @@ private:
   nsCOMPtr<nsIInputStream> mBodyStream;
 
   nsContentPolicyType mContentPolicyType;
 
   // Empty string: no-referrer
   // "about:client": client (default)
   // URL: an URL
   nsString mReferrer;
+  ReferrerPolicy mReferrerPolicy;
 
   RequestMode mMode;
   RequestCredentials mCredentialsMode;
   LoadTainting mResponseTainting;
   RequestCache mCacheMode;
   RequestRedirect mRedirectMode;
 
   bool mAuthenticationFlag;
--- a/dom/fetch/Request.cpp
+++ b/dom/fetch/Request.cpp
@@ -348,16 +348,17 @@ Request::Constructor(const GlobalObject&
   if (mode == RequestMode::Navigate ||
       (aInit.IsAnyMemberPresent() && request->Mode() == RequestMode::Navigate)) {
     aRv.ThrowTypeError<MSG_INVALID_REQUEST_MODE>(NS_LITERAL_STRING("navigate"));
     return nullptr;
   }
 
   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();
     if (referrer.IsEmpty()) {
       request->SetReferrer(NS_LITERAL_STRING(""));
     } else {
       nsAutoString referrerURL;
       if (NS_IsMainThread()) {
@@ -414,16 +415,20 @@ Request::Constructor(const GlobalObject&
             return nullptr;
           }
         }
       }
       request->SetReferrer(referrerURL);
     }
   }
 
+  if (aInit.mReferrerPolicy.WasPassed()) {
+    request->SetReferrerPolicy(aInit.mReferrerPolicy.Value());
+  }
+
   if (mode != RequestMode::EndGuard_) {
     request->ClearCreatedByFetchEvent();
     request->SetMode(mode);
   }
 
   if (credentials != RequestCredentials::EndGuard_) {
     request->ClearCreatedByFetchEvent();
     request->SetCredentialsMode(credentials);
--- a/dom/fetch/Request.h
+++ b/dom/fetch/Request.h
@@ -94,16 +94,22 @@ public:
   }
 
   void
   GetReferrer(nsAString& aReferrer) const
   {
     mRequest->GetReferrer(aReferrer);
   }
 
+  ReferrerPolicy
+  ReferrerPolicy_() const
+  {
+    return mRequest->ReferrerPolicy_();
+  }
+
   InternalHeaders*
   GetInternalHeaders() const
   {
     return mRequest->Headers();
   }
 
   Headers* Headers_();
 
--- a/dom/webidl/Request.webidl
+++ b/dom/webidl/Request.webidl
@@ -15,16 +15,17 @@ typedef unsigned long nsContentPolicyTyp
 interface Request {
   readonly attribute ByteString method;
   readonly attribute USVString url;
   [SameObject] readonly attribute Headers headers;
 
   [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"]
   readonly attribute RequestCache cache;
   readonly attribute RequestRedirect redirect;
 
   [Throws,
    NewObject] Request clone();
@@ -35,16 +36,17 @@ interface Request {
 };
 Request implements Body;
 
 dictionary RequestInit {
   ByteString method;
   HeadersInit headers;
   BodyInit? body;
   USVString referrer;
+  ReferrerPolicy referrerPolicy;
   RequestMode mode;
   RequestCredentials credentials;
   RequestCache cache;
   RequestRedirect redirect;
 };
 
 // Gecko currently does not ship RequestContext, so please don't use it in IDL
 // that is exposed to script.
@@ -55,8 +57,9 @@ enum RequestContext {
   "sharedworker", "subresource", "style", "track", "video", "worker", "xmlhttprequest",
   "xslt"
 };
 
 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" };
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -188,17 +188,17 @@ ChannelFromScriptURL(nsIPrincipal* princ
                        aLoadFlags,
                        ios);
   }
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   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;
     }
   }
 
   channel.forget(aChannel);
   return rv;
 }
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -985,16 +985,17 @@ class FetchEventRunnable : public Extend
   nsString mClientId;
   bool mIsReload;
   RequestMode mRequestMode;
   RequestRedirect mRequestRedirect;
   RequestCredentials mRequestCredentials;
   nsContentPolicyType mContentPolicyType;
   nsCOMPtr<nsIInputStream> mUploadStream;
   nsCString mReferrer;
+  ReferrerPolicy mReferrerPolicy;
 public:
   FetchEventRunnable(WorkerPrivate* aWorkerPrivate,
                      KeepAliveToken* aKeepAliveToken,
                      nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
                      // CSP checks might require the worker script spec
                      // later on.
                      const nsACString& aScriptSpec,
                      nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration,
@@ -1008,16 +1009,17 @@ public:
     , mIsReload(aIsReload)
     , mRequestMode(RequestMode::No_cors)
     , mRequestRedirect(RequestRedirect::Follow)
     // By default we set it to same-origin since normal HTTP fetches always
     // send credentials to same-origin websites unless explicitly forbidden.
     , mRequestCredentials(RequestCredentials::Same_origin)
     , mContentPolicyType(nsIContentPolicy::TYPE_INVALID)
     , mReferrer(kFETCH_CLIENT_REFERRER_STR)
+    , mReferrerPolicy(ReferrerPolicy::_empty)
   {
     MOZ_ASSERT(aWorkerPrivate);
   }
 
   NS_DECL_ISUPPORTS_INHERITED
 
   NS_IMETHOD
   VisitHeader(const nsACString& aHeader, const nsACString& aValue) override
@@ -1055,26 +1057,49 @@ public:
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsILoadInfo> loadInfo;
     rv = channel->GetLoadInfo(getter_AddRefs(loadInfo));
     NS_ENSURE_SUCCESS(rv, rv);
 
     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;
     }
 
-    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
-    MOZ_ASSERT(httpChannel, "How come we don't have an HTTP channel?");
+    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);
 
     nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(httpChannel);
     NS_ENSURE_TRUE(internalChannel, NS_ERROR_NOT_AVAILABLE);
 
     mRequestMode = InternalRequest::MapChannelToRequestMode(channel);
@@ -1174,16 +1199,17 @@ private:
 
     RefPtr<InternalRequest> internalReq = new InternalRequest(mSpec,
                                                               mMethod,
                                                               internalHeaders.forget(),
                                                               mRequestMode,
                                                               mRequestRedirect,
                                                               mRequestCredentials,
                                                               NS_ConvertUTF8toUTF16(mReferrer),
+                                                              mReferrerPolicy,
                                                               mContentPolicyType);
     internalReq->SetBody(mUploadStream);
     // For Telemetry, note that this Request object was created by a Fetch event.
     internalReq->SetCreatedByFetchEvent();
 
     nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(globalObj.GetAsSupports());
     if (NS_WARN_IF(!global)) {
       return false;
--- a/testing/web-platform/meta/fetch/api/request/request-error.html.ini
+++ b/testing/web-platform/meta/fetch/api/request/request-error.html.ini
@@ -1,11 +1,8 @@
 [request-error.html]
   type: testharness
   [RequestInit's window is not null]
     expected: FAIL
 
   [RequestInit's mode is no-cors and integrity is not empty]
     expected: FAIL
 
-  [Bad referrerPolicy init parameter value]
-    expected: FAIL
-
--- a/testing/web-platform/meta/fetch/api/request/request-idl.html.ini
+++ b/testing/web-platform/meta/fetch/api/request/request-idl.html.ini
@@ -1,32 +1,26 @@
 [request-idl.html]
   type: testharness
   [Request interface: attribute type]
     expected: FAIL
 
   [Request interface: attribute destination]
     expected: FAIL
 
-  [Request interface: attribute referrerPolicy]
-    expected: FAIL
-
   [Request interface: attribute cache]
     expected: FAIL
 
   [Request interface: attribute integrity]
     expected: FAIL
 
   [Request interface: new Request("") must inherit property "type" with the proper type (3)]
     expected: FAIL
 
   [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
 
   [Request interface: new Request("") must inherit property "integrity" with the proper type (11)]
     expected: FAIL
 
--- a/testing/web-platform/meta/fetch/api/request/request-init-001.sub.html.ini
+++ b/testing/web-platform/meta/fetch/api/request/request-init-001.sub.html.ini
@@ -1,26 +1,8 @@
 [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
 
   [Check integrity init value of AZERTYUIOP1234567890 and associated getter]
     expected: FAIL
 
--- a/testing/web-platform/meta/fetch/api/request/request-structure.html.ini
+++ b/testing/web-platform/meta/fetch/api/request/request-structure.html.ini
@@ -1,17 +1,14 @@
 [request-structure.html]
   type: testharness
   [Check type attribute]
     expected: FAIL
 
   [Check destination attribute]
     expected: FAIL
 
-  [Check referrerPolicy attribute]
-    expected: FAIL
-
   [Check cache attribute]
     expected: FAIL
 
   [Check integrity attribute]
     expected: FAIL
 
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-event.https.html.ini
@@ -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]
--- a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event.https.html
+++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event.https.html
@@ -1,11 +1,12 @@
 <!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>
 var worker = 'resources/fetch-event-test-worker.js';
 
 async_test(function(t) {
     var scope = 'resources/simple.html?string';
     service_worker_unregister_and_register(t, worker, scope)
@@ -63,16 +64,181 @@ async_test(function(t) {
             'Referrer: ' + document.location.href,
             'Service Worker should respond to fetch with the referrer URL');
           frame.remove();
           return service_worker_unregister_and_done(t, scope);
         })
       .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;
     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); })
--- a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-event-test-worker.js
+++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-event-test-worker.js
@@ -6,16 +6,27 @@ function handleBlob(event) {
   event.respondWith(new Response(new Blob(['Test blob'])));
 }
 
 function handleReferrer(event) {
   event.respondWith(new Response(new Blob(
     ['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) {
     body = 'Client ID Found: ' + event.clientId;
   } else {
     body = 'Client ID Not Found';
   }
   event.respondWith(new Response(body));
@@ -80,16 +91,18 @@ function handleFragmentCheck(event) {
   event.respondWith(new Response(body));
 }
 
 self.addEventListener('fetch', function(event) {
     var url = event.request.url;
     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() {} },
       { pattern: '?null', fn: handleNullBody },
       { pattern: '?fetch', fn: handleFetch },
       { pattern: '?form-post', fn: handleFormPost },
       { pattern: '?multiple-respond-with', fn: handleMultipleRespondWith },
       { pattern: '?used-check', fn: handleUsedCheck },
--- a/testing/web-platform/tests/fetch/api/request/request-idl.html
+++ b/testing/web-platform/tests/fetch/api/request/request-idl.html
@@ -65,16 +65,17 @@
       };
 
       enum RequestType { "", "audio", "font", "image", "script", "style", "track", "video" };
       enum RequestDestination { "", "document", "sharedworker", "subresource", "unknown", "worker" };
       enum RequestMode { "navigate", "same-origin", "no-cors", "cors" };
       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();
       var idl = document.getElementById("body-idl").innerHTML
       idl += document.getElementById("request-idl").innerHTML
 
       idlsArray.add_idls(idl);
       idlsArray.add_untested_idls("interface Headers {};");