Bug 1264792 - Update request'referrer policy when redirect.r=dragana. r=bkelly draft
authorThomas Nguyen <tnguyen@mozilla.com>
Mon, 14 Nov 2016 15:15:32 +0800
changeset 438247 37c504cf5b64b8d3a1ff740531521e2a21f3e7da
parent 438112 47e0584afe0ab0b867412189c610b302b6ba0ea7
child 438248 243d603cea4a8d89e67a63a4d2fa6f4c8ffdfd70
push id35665
push usertnguyen@mozilla.com
push dateMon, 14 Nov 2016 07:16:02 +0000
reviewersdragana, bkelly
bugs1264792
milestone52.0a1
Bug 1264792 - Update request'referrer policy when redirect.r=dragana. r=bkelly MozReview-Commit-ID: 3V6W0fuRomZ
dom/base/nsContentUtils.cpp
dom/cache/DBSchema.cpp
dom/fetch/FetchDriver.cpp
dom/fetch/FetchUtil.cpp
dom/fetch/FetchUtil.h
dom/fetch/InternalRequest.h
dom/webidl/Request.webidl
dom/workers/ScriptLoader.cpp
dom/workers/ServiceWorkerPrivate.cpp
dom/xhr/XMLHttpRequestMainThread.cpp
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/protocol/http/HttpChannelChild.h
netwerk/protocol/http/HttpChannelParent.cpp
netwerk/protocol/http/HttpChannelParent.h
netwerk/protocol/http/PHttpChannel.ipdl
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -8475,20 +8475,18 @@ nsContentUtils::SetFetchReferrerURIWithP
       referrerURI = docCurURI;
     }
   }
 
   if (!referrerURI) {
     referrerURI = principalURI;
   }
 
-  net::ReferrerPolicy referrerPolicy = aReferrerPolicy;
-  if (referrerPolicy == net::RP_Default) {
-    referrerPolicy = aDoc->GetReferrerPolicy();
-  }
+  net::ReferrerPolicy referrerPolicy = (aReferrerPolicy != net::RP_Unset) ?
+                                       aReferrerPolicy : net::RP_Default;
   return aChannel->SetReferrerWithPolicy(referrerURI, referrerPolicy);
 }
 
 // static
 net::ReferrerPolicy
 nsContentUtils::GetReferrerPolicyFromHeader(const nsAString& aHeader)
 {
   // Multiple headers could be concatenated into one comma-separated
--- a/dom/cache/DBSchema.cpp
+++ b/dom/cache/DBSchema.cpp
@@ -192,17 +192,20 @@ static_assert(int(HeadersGuardEnum::None
               int(HeadersGuardEnum::EndGuard_) == 5,
               "HeadersGuardEnum values are as expected");
 static_assert(int(ReferrerPolicy::_empty) == 0 &&
               int(ReferrerPolicy::No_referrer) == 1 &&
               int(ReferrerPolicy::No_referrer_when_downgrade) == 2 &&
               int(ReferrerPolicy::Origin) == 3 &&
               int(ReferrerPolicy::Origin_when_cross_origin) == 4 &&
               int(ReferrerPolicy::Unsafe_url) == 5 &&
-              int(ReferrerPolicy::EndGuard_) == 6,
+              int(ReferrerPolicy::Same_origin) == 6 &&
+              int(ReferrerPolicy::Strict_origin) == 7 &&
+              int(ReferrerPolicy::Strict_origin_when_cross_origin) == 8 &&
+              int(ReferrerPolicy::EndGuard_) == 9,
               "ReferrerPolicy values are as expected");
 static_assert(int(RequestMode::Same_origin) == 0 &&
               int(RequestMode::No_cors) == 1 &&
               int(RequestMode::Cors) == 2 &&
               int(RequestMode::Navigate) == 3 &&
               int(RequestMode::EndGuard_) == 4,
               "RequestMode values are as expected");
 static_assert(int(RequestCredentials::Omit) == 0 &&
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -30,16 +30,17 @@
 #include "nsHttpChannel.h"
 
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/workers/Workers.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "mozilla/Unused.h"
 
 #include "Fetch.h"
+#include "FetchUtil.h"
 #include "InternalRequest.h"
 #include "InternalResponse.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_ISUPPORTS(FetchDriver,
                   nsIStreamListener, nsIChannelEventSink, nsIInterfaceRequestor,
@@ -255,72 +256,40 @@ FetchDriver::HttpFetch()
     nsAutoCString method;
     mRequest->GetMethod(method);
     rv = httpChan->SetRequestMethod(method);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Set the same headers.
     SetRequestHeaders(httpChan);
 
-    // Step 2. Set the referrer.
-    nsAutoString referrer;
-    mRequest->GetReferrer(referrer);
+    net::ReferrerPolicy net_referrerPolicy = mRequest->GetEnvironmentReferrerPolicy();
+    // Step 6 of
+    // https://fetch.spec.whatwg.org/#main-fetch
+    // If request's referrer policy is the empty string and request's client is
+    // non-null, then set request's referrer policy to request's client's
+    // associated referrer policy.
+    // Basically, "client" is not in our implementation, we use
+    // EnvironmentReferrerPolicy of the worker or document context
+    if (mRequest->ReferrerPolicy_() == ReferrerPolicy::_empty) {
+      mRequest->SetReferrerPolicy(net_referrerPolicy);
+    }
+    // Step 7 of
+    // https://fetch.spec.whatwg.org/#main-fetch
+    // If request’s referrer policy is the empty string,
+    // then set request’s referrer policy to "no-referrer-when-downgrade".
+    if (mRequest->ReferrerPolicy_() == ReferrerPolicy::_empty) {
+      mRequest->SetReferrerPolicy(net::RP_No_Referrer_When_Downgrade);
+    }
 
-    // The Referrer Policy in Request can be used to override a referrer policy
-    // associated with an environment settings object.
-    // If there's no Referrer Policy in the request, it should be inherited
-    // from environment.
-    ReferrerPolicy referrerPolicy = mRequest->ReferrerPolicy_();
-    net::ReferrerPolicy net_referrerPolicy = mRequest->GetEnvironmentReferrerPolicy();
-    switch (referrerPolicy) {
-    case ReferrerPolicy::_empty:
-      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:
-      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,
-                                                         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."
-      nsCOMPtr<nsIURI> referrerURI;
-      rv = NS_NewURI(getter_AddRefs(referrerURI), referrer, nullptr, nullptr);
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      rv =
-        httpChan->SetReferrerWithPolicy(referrerURI,
-                                        referrerPolicy == ReferrerPolicy::_empty ?
-                                          mRequest->GetEnvironmentReferrerPolicy() :
-                                          net_referrerPolicy);
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
+    rv = FetchUtil::SetRequestReferrer(mPrincipal,
+                                       mDocument,
+                                       httpChan,
+                                       mRequest);
+    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,
 
     nsCOMPtr<nsIHttpChannelInternal> internalChan = do_QueryInterface(httpChan);
 
@@ -818,39 +787,25 @@ FetchDriver::AsyncOnChannelRedirect(nsIC
   mRequest->AddURL(spec, fragment);
   NS_ConvertUTF8toUTF16 tRPHeaderValue(tRPHeaderCValue);
   // updates request’s associated referrer policy according to the
   // Referrer-Policy header (if any).
   if (!tRPHeaderValue.IsEmpty()) {
     net::ReferrerPolicy net_referrerPolicy =
       nsContentUtils::GetReferrerPolicyFromHeader(tRPHeaderValue);
     if (net_referrerPolicy != net::RP_Unset) {
-      ReferrerPolicy referrerPolicy = mRequest->ReferrerPolicy_();
-      switch (net_referrerPolicy) {
-        case net::RP_No_Referrer:
-          referrerPolicy = ReferrerPolicy::No_referrer;
-          break;
-        case net::RP_No_Referrer_When_Downgrade:
-          referrerPolicy = ReferrerPolicy::No_referrer_when_downgrade;
-          break;
-        case net::RP_Origin:
-          referrerPolicy = ReferrerPolicy::Origin;
-          break;
-        case net::RP_Origin_When_Crossorigin:
-          referrerPolicy = ReferrerPolicy::Origin_when_cross_origin;
-          break;
-        case net::RP_Unsafe_URL:
-          referrerPolicy = ReferrerPolicy::Unsafe_url;
-          break;
-        default:
-          MOZ_ASSERT_UNREACHABLE("Invalid ReferrerPolicy value");
-          break;
+      mRequest->SetReferrerPolicy(net_referrerPolicy);
+      // Should update channel's referrer policy
+      if (httpChannel) {
+        rv = FetchUtil::SetRequestReferrer(mPrincipal,
+                                           mDocument,
+                                           httpChannel,
+                                           mRequest);
+        NS_ENSURE_SUCCESS(rv, rv);
       }
-
-      mRequest->SetReferrerPolicy(referrerPolicy);
     }
   }
 
   aCallback->OnRedirectVerifyCallback(NS_OK);
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/dom/fetch/FetchUtil.cpp
+++ b/dom/fetch/FetchUtil.cpp
@@ -1,15 +1,17 @@
 #include "FetchUtil.h"
 
 #include "nsError.h"
 #include "nsIUnicodeDecoder.h"
 #include "nsString.h"
+#include "nsIDocument.h"
 
 #include "mozilla/dom/EncodingUtils.h"
+#include "mozilla/dom/InternalRequest.h"
 
 namespace mozilla {
 namespace dom {
 
 // static
 nsresult
 FetchUtil::GetValidRequestMethod(const nsACString& aMethod, nsCString& outMethod)
 {
@@ -105,10 +107,64 @@ FetchUtil::ExtractHeader(nsACString::con
   if (!NS_IsReasonableHTTPHeaderValue(aHeaderValue)) {
     return false;
   }
   aHeaderValue.CompressWhitespace();
 
   return PushOverLine(aStart, aEnd);
 }
 
+// static
+nsresult
+FetchUtil::SetRequestReferrer(nsIPrincipal* aPrincipal,
+                              nsIDocument* aDoc,
+                              nsIHttpChannel* aChannel,
+                              InternalRequest* aRequest) {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsAutoString referrer;
+  aRequest->GetReferrer(referrer);
+  net::ReferrerPolicy policy = aRequest->GetReferrerPolicy();
+
+  nsresult rv = NS_OK;
+  if (referrer.IsEmpty()) {
+    // This is the case request’s referrer is "no-referrer"
+    rv = aChannel->SetReferrerWithPolicy(nullptr, net::RP_No_Referrer);
+    NS_ENSURE_SUCCESS(rv, rv);
+  } else if (referrer.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR)) {
+    rv = nsContentUtils::SetFetchReferrerURIWithPolicy(aPrincipal,
+                                                       aDoc,
+                                                       aChannel,
+                                                       policy);
+    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."
+    nsCOMPtr<nsIURI> referrerURI;
+    rv = NS_NewURI(getter_AddRefs(referrerURI), referrer, nullptr, nullptr);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = aChannel->SetReferrerWithPolicy(referrerURI, policy);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  nsCOMPtr<nsIURI> referrerURI;
+  aChannel->GetReferrer(getter_AddRefs(referrerURI));
+
+  // Step 8 https://fetch.spec.whatwg.org/#main-fetch
+  // If request’s referrer is not "no-referrer", set request’s referrer to
+  // the result of invoking determine request’s referrer.
+  if (referrerURI) {
+    nsAutoCString spec;
+    rv = referrerURI->GetSpec(spec);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    aRequest->SetReferrer(NS_ConvertUTF8toUTF16(spec));
+  } else {
+    aRequest->SetReferrer(EmptyString());
+  }
+
+  return NS_OK;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/fetch/FetchUtil.h
+++ b/dom/fetch/FetchUtil.h
@@ -3,19 +3,25 @@
 
 #include "nsString.h"
 #include "nsError.h"
 
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FormData.h"
 
+class nsIPrincipal;
+class nsIDocument;
+class nsIHttpChannel;
+
 namespace mozilla {
 namespace dom {
 
+class InternalRequest;
+
 class FetchUtil final
 {
 private:
   FetchUtil() = delete;
 
 public:
   /**
   * Sets outMethod to a valid HTTP request method string based on an input method.
@@ -30,13 +36,20 @@ public:
    * Extracts an HTTP header from a substring range.
    */
   static bool
   ExtractHeader(nsACString::const_iterator& aStart,
                 nsACString::const_iterator& aEnd,
                 nsCString& aHeaderName,
                 nsCString& aHeaderValue,
                 bool* aWasEmptyHeader);
+
+  static nsresult
+  SetRequestReferrer(nsIPrincipal* aPrincipal,
+                     nsIDocument* aDoc,
+                     nsIHttpChannel* aChannel,
+                     InternalRequest* aRequest);
+
 };
 
 } // namespace dom
 } // namespace mozilla
 #endif
--- a/dom/fetch/InternalRequest.h
+++ b/dom/fetch/InternalRequest.h
@@ -228,16 +228,82 @@ public:
   }
 
   void
   SetReferrerPolicy(ReferrerPolicy aReferrerPolicy)
   {
     mReferrerPolicy = aReferrerPolicy;
   }
 
+  void
+  SetReferrerPolicy(net::ReferrerPolicy aReferrerPolicy)
+  {
+    switch (aReferrerPolicy) {
+      case net::RP_Unset:
+        mReferrerPolicy = ReferrerPolicy::_empty;
+        break;
+      case net::RP_No_Referrer:
+        mReferrerPolicy = ReferrerPolicy::No_referrer;
+        break;
+      case net::RP_No_Referrer_When_Downgrade:
+        mReferrerPolicy = ReferrerPolicy::No_referrer_when_downgrade;
+        break;
+      case net::RP_Origin:
+        mReferrerPolicy = ReferrerPolicy::Origin;
+        break;
+      case net::RP_Origin_When_Crossorigin:
+        mReferrerPolicy = ReferrerPolicy::Origin_when_cross_origin;
+        break;
+      case net::RP_Unsafe_URL:
+        mReferrerPolicy = ReferrerPolicy::Unsafe_url;
+        break;
+      case net::RP_Same_Origin:
+        mReferrerPolicy = ReferrerPolicy::Same_origin;
+        break;
+      case net::RP_Strict_Origin:
+        mReferrerPolicy = ReferrerPolicy::Strict_origin;
+        break;
+      case net::RP_Strict_Origin_When_Cross_Origin:
+        mReferrerPolicy = ReferrerPolicy::Strict_origin_when_cross_origin;
+        break;
+      default:
+        MOZ_ASSERT_UNREACHABLE("Invalid ReferrerPolicy value");
+        break;
+    }
+  }
+
+  net::ReferrerPolicy
+  GetReferrerPolicy()
+  {
+    switch (mReferrerPolicy) {
+      case ReferrerPolicy::_empty:
+        return net::RP_Unset;
+      case ReferrerPolicy::No_referrer:
+        return net::RP_No_Referrer;
+      case ReferrerPolicy::No_referrer_when_downgrade:
+        return net::RP_No_Referrer_When_Downgrade;
+      case ReferrerPolicy::Origin:
+        return net::RP_Origin;
+      case ReferrerPolicy::Origin_when_cross_origin:
+        return net::RP_Origin_When_Crossorigin;
+      case ReferrerPolicy::Unsafe_url:
+        return net::RP_Unsafe_URL;
+      case ReferrerPolicy::Strict_origin:
+        return net::RP_Strict_Origin;
+      case ReferrerPolicy::Same_origin:
+        return net::RP_Same_Origin;
+      case ReferrerPolicy::Strict_origin_when_cross_origin:
+        return net::RP_Strict_Origin_When_Cross_Origin;
+      default:
+        MOZ_ASSERT_UNREACHABLE("Invalid ReferrerPolicy enum value?");
+        break;
+    }
+    return net::RP_Unset;
+  }
+
   net::ReferrerPolicy
   GetEnvironmentReferrerPolicy() const
   {
     return mEnvironmentReferrerPolicy;
   }
 
   void
   SetEnvironmentReferrerPolicy(net::ReferrerPolicy aReferrerPolicy)
--- a/dom/webidl/Request.webidl
+++ b/dom/webidl/Request.webidl
@@ -58,9 +58,13 @@ 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", "only-if-cached" };
 enum RequestRedirect { "follow", "error", "manual" };
-enum ReferrerPolicy { "", "no-referrer", "no-referrer-when-downgrade", "origin", "origin-when-cross-origin", "unsafe-url" };
+enum ReferrerPolicy {
+  "", "no-referrer", "no-referrer-when-downgrade", "origin",
+  "origin-when-cross-origin", "unsafe-url", "same-origin", "strict-origin",
+  "strict-origin-when-cross-origin"
+};
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -200,18 +200,20 @@ ChannelFromScriptURL(nsIPrincipal* princ
                        nullptr, // aCallbacks
                        aLoadFlags,
                        ios);
   }
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel)) {
+    mozilla::net::ReferrerPolicy referrerPolicy = parentDoc ?
+      parentDoc->GetReferrerPolicy() : mozilla::net::RP_Default;
     rv = nsContentUtils::SetFetchReferrerURIWithPolicy(principal, parentDoc,
-                                                       httpChannel, mozilla::net::RP_Default);
+                                                       httpChannel, referrerPolicy);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   channel.forget(aChannel);
   return rv;
 }
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -1336,37 +1336,53 @@ public:
     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;
+    } else {
+      // If there's no referrer Header, means the header was omitted for
+      // security/privacy reason.
+      mReferrer = EmptyCString();
     }
 
     uint32_t referrerPolicy = 0;
     rv = httpChannel->GetReferrerPolicy(&referrerPolicy);
     NS_ENSURE_SUCCESS(rv, rv);
     switch (referrerPolicy) {
+      case nsIHttpChannel::REFERRER_POLICY_UNSET:
+      mReferrerPolicy = ReferrerPolicy::_empty;
+      break;
     case nsIHttpChannel::REFERRER_POLICY_NO_REFERRER:
       mReferrerPolicy = ReferrerPolicy::No_referrer;
       break;
     case nsIHttpChannel::REFERRER_POLICY_ORIGIN:
       mReferrerPolicy = ReferrerPolicy::Origin;
       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;
+    case nsIHttpChannel::REFERRER_POLICY_SAME_ORIGIN:
+      mReferrerPolicy = ReferrerPolicy::Same_origin;
+      break;
+    case nsIHttpChannel::REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN:
+      mReferrerPolicy = ReferrerPolicy::Strict_origin_when_cross_origin;
+      break;
+    case nsIHttpChannel::REFERRER_POLICY_STRICT_ORIGIN:
+      mReferrerPolicy = ReferrerPolicy::Strict_origin;
+      break;
     default:
       MOZ_ASSERT_UNREACHABLE("Invalid Referrer Policy enum value?");
       break;
     }
 
     rv = httpChannel->GetRequestMethod(mMethod);
     NS_ENSURE_SUCCESS(rv, rv);
 
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -2536,19 +2536,20 @@ XMLHttpRequestMainThread::InitiateFetch(
       mAuthorRequestHeaders.Set("accept", NS_LITERAL_CSTRING("*/*"));
     }
 
     mAuthorRequestHeaders.ApplyToChannel(httpChannel);
 
     if (!IsSystemXHR()) {
       nsCOMPtr<nsPIDOMWindowInner> owner = GetOwner();
       nsCOMPtr<nsIDocument> doc = owner ? owner->GetExtantDoc() : nullptr;
+      mozilla::net::ReferrerPolicy referrerPolicy = doc ?
+        doc->GetReferrerPolicy() : mozilla::net::RP_Default;
       nsContentUtils::SetFetchReferrerURIWithPolicy(mPrincipal, doc,
-                                                    httpChannel,
-                                                    mozilla::net::RP_Default);
+                                                    httpChannel, referrerPolicy);
     }
 
     // Some extensions override the http protocol handler and provide their own
     // implementation. The channels returned from that implementation don't
     // always seem to 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 (see bug 529041).
     nsCOMPtr<nsIUploadChannel2> uploadChannel2 = do_QueryInterface(httpChannel);
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -1283,25 +1283,24 @@ HttpBaseChannel::SetReferrerWithPolicy(n
   ENSURE_CALLED_BEFORE_CONNECT();
 
   // clear existing referrer, if any
   mReferrer = nullptr;
   nsresult rv = mRequestHead.ClearHeader(nsHttp::Referer);
   if(NS_FAILED(rv)) {
     return rv;
   }
-  mReferrerPolicy = REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE;
+  mReferrerPolicy = referrerPolicy;
 
   if (!referrer) {
     return NS_OK;
   }
 
   // Don't send referrer at all when the meta referrer setting is "no-referrer"
   if (referrerPolicy == REFERRER_POLICY_NO_REFERRER) {
-    mReferrerPolicy = REFERRER_POLICY_NO_REFERRER;
     return NS_OK;
   }
 
   // 0: never send referer
   // 1: send referer for direct user action
   // 2: always send referer
   uint32_t userReferrerLevel = gHttpHandler->ReferrerLevel();
 
@@ -1576,17 +1575,16 @@ HttpBaseChannel::SetReferrerWithPolicy(n
     if (NS_FAILED(rv)) return rv;
   }
 
   // finally, remember the referrer URI and set the Referer header.
   rv = SetRequestHeader(NS_LITERAL_CSTRING("Referer"), spec, false);
   if (NS_FAILED(rv)) return rv;
 
   mReferrer = clone;
-  mReferrerPolicy = referrerPolicy;
   return NS_OK;
 }
 
 // Return the channel's proxy URI, or if it doesn't exist, the
 // channel's main URI.
 NS_IMETHODIMP
 HttpBaseChannel::GetProxyURI(nsIURI **aOut)
 {
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -1677,16 +1677,21 @@ HttpChannelChild::CompleteRedirectSetup(
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 HttpChannelChild::OnRedirectVerifyCallback(nsresult result)
 {
   LOG(("HttpChannelChild::OnRedirectVerifyCallback [this=%p]\n", this));
   nsresult rv;
   OptionalURIParams redirectURI;
+
+  uint32_t referrerPolicy = REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE;
+  OptionalURIParams referrerURI;
+  SerializeURI(nullptr, referrerURI);
+
   nsCOMPtr<nsIHttpChannel> newHttpChannel =
       do_QueryInterface(mRedirectChannelChild);
 
   if (NS_SUCCEEDED(result) && !mRedirectChannelChild) {
     // mRedirectChannelChild doesn't exist means we're redirecting to a protocol
     // that doesn't implement nsIChildChannel. The redirect result should be set
     // as failed by veto listeners and shouldn't enter this condition. As the
     // last resort, we synthesize the error result as NS_ERROR_DOM_BAD_URI here
@@ -1703,16 +1708,22 @@ HttpChannelChild::OnRedirectVerifyCallba
     newHttpChannel->SetOriginalURI(mOriginalURI);
 
     nsCOMPtr<nsILoadInfo> newLoadInfo;
     rv = newHttpChannel->GetLoadInfo(getter_AddRefs(newLoadInfo));
     if (NS_SUCCEEDED(rv) && newLoadInfo) {
       forceHSTSPriming = newLoadInfo->GetForceHSTSPriming();
       mixedContentWouldBlock = newLoadInfo->GetMixedContentWouldBlock();
     }
+
+    newHttpChannel->GetReferrerPolicy(&referrerPolicy);
+    nsCOMPtr<nsIURI> newChannelReferrerURI;
+    newHttpChannel->GetReferrer(getter_AddRefs(newChannelReferrerURI));
+
+    SerializeURI(newChannelReferrerURI, referrerURI);
   }
 
   if (mRedirectingForSubsequentSynthesizedResponse) {
     nsCOMPtr<nsIHttpChannelChild> httpChannelChild = do_QueryInterface(mRedirectChannelChild);
     RefPtr<HttpChannelChild> redirectedChannel =
         static_cast<HttpChannelChild*>(httpChannelChild.get());
     // redirectChannel will be NULL if mRedirectChannelChild isn't a
     // nsIHttpChannelChild (it could be a DataChannelChild).
@@ -1773,19 +1784,19 @@ HttpChannelChild::OnRedirectVerifyCallba
   bool chooseAppcache = false;
   nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
     do_QueryInterface(newHttpChannel);
   if (appCacheChannel) {
     appCacheChannel->GetChooseApplicationCache(&chooseAppcache);
   }
 
   if (mIPCOpen)
-    SendRedirect2Verify(result, *headerTuples, loadFlags, redirectURI,
-                        corsPreflightArgs, forceHSTSPriming,
-                        mixedContentWouldBlock, chooseAppcache);
+    SendRedirect2Verify(result, *headerTuples, loadFlags, referrerPolicy,
+                        referrerURI, redirectURI, corsPreflightArgs,
+                        forceHSTSPriming, mixedContentWouldBlock, chooseAppcache);
 
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // HttpChannelChild::nsIRequest
 //-----------------------------------------------------------------------------
 
@@ -2130,16 +2141,35 @@ HttpChannelChild::ContinueAsyncOpen()
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // HttpChannelChild::nsIHttpChannel
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
+HttpChannelChild::SetReferrerWithPolicy(nsIURI *referrer,
+                                       uint32_t referrerPolicy)
+{
+  ENSURE_CALLED_BEFORE_CONNECT();
+
+  // remove old referrer if any, loop backwards
+  for (int i = mClientSetRequestHeaders.Length() - 1; i >= 0; --i) {
+    if (NS_LITERAL_CSTRING("Referer").Equals(mClientSetRequestHeaders[i].mHeader)) {
+      mClientSetRequestHeaders.RemoveElementAt(i);
+    }
+  }
+
+  nsresult rv = HttpBaseChannel::SetReferrerWithPolicy(referrer, referrerPolicy);
+  if (NS_FAILED(rv))
+    return rv;
+  return NS_OK;
+
+}
+NS_IMETHODIMP
 HttpChannelChild::SetRequestHeader(const nsACString& aHeader,
                                    const nsACString& aValue,
                                    bool aMerge)
 {
   LOG(("HttpChannelChild::SetRequestHeader [this=%p]\n", this));
   nsresult rv = HttpBaseChannel::SetRequestHeader(aHeader, aValue, aMerge);
   if (NS_FAILED(rv))
     return rv;
--- a/netwerk/protocol/http/HttpChannelChild.h
+++ b/netwerk/protocol/http/HttpChannelChild.h
@@ -73,16 +73,17 @@ public:
   NS_IMETHOD Suspend() override;
   NS_IMETHOD Resume() override;
   // nsIChannel
   NS_IMETHOD GetSecurityInfo(nsISupports **aSecurityInfo) override;
   NS_IMETHOD AsyncOpen(nsIStreamListener *listener, nsISupports *aContext) override;
   NS_IMETHOD AsyncOpen2(nsIStreamListener *aListener) override;
 
   // HttpBaseChannel::nsIHttpChannel
+  NS_IMETHOD SetReferrerWithPolicy(nsIURI *referrer, uint32_t referrerPolicy) override;
   NS_IMETHOD SetRequestHeader(const nsACString& aHeader,
                               const nsACString& aValue,
                               bool aMerge) override;
   NS_IMETHOD SetEmptyRequestHeader(const nsACString& aHeader) override;
   NS_IMETHOD RedirectTo(nsIURI *newURI) override;
   NS_IMETHOD GetProtocolVersion(nsACString& aProtocolVersion) override;
   // nsIHttpChannelInternal
   NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey) override;
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -689,16 +689,18 @@ HttpChannelParent::RecvUpdateAssociatedC
   }
   return true;
 }
 
 bool
 HttpChannelParent::RecvRedirect2Verify(const nsresult& result,
                                        const RequestHeaderTuples& changedHeaders,
                                        const uint32_t& loadFlags,
+                                       const uint32_t& referrerPolicy,
+                                       const OptionalURIParams& aReferrerURI,
                                        const OptionalURIParams& aAPIRedirectURI,
                                        const OptionalCorsPreflightArgs& aCorsPreflightArgs,
                                        const bool& aForceHSTSPriming,
                                        const bool& aMixedContentWouldBlock,
                                        const bool& aChooseAppcache)
 {
   LOG(("HttpChannelParent::RecvRedirect2Verify [this=%p result=%x]\n",
        this, result));
@@ -740,16 +742,19 @@ HttpChannelParent::RecvRedirect2Verify(c
       if (aForceHSTSPriming) {
         nsCOMPtr<nsILoadInfo> newLoadInfo;
         rv = newHttpChannel->GetLoadInfo(getter_AddRefs(newLoadInfo));
         if (NS_SUCCEEDED(rv) && newLoadInfo) {
           newLoadInfo->SetHSTSPriming(aMixedContentWouldBlock);
         }
       }
 
+      nsCOMPtr<nsIURI> referrerUri = DeserializeURI(aReferrerURI);
+      newHttpChannel->SetReferrerWithPolicy(referrerUri, referrerPolicy);
+
       nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
         do_QueryInterface(newHttpChannel);
       if (appCacheChannel) {
         appCacheChannel->SetChooseApplicationCache(aChooseAppcache);
       }
     }
   }
 
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParent.h
@@ -147,16 +147,18 @@ protected:
   virtual bool RecvSetClassOfService(const uint32_t& cos) override;
   virtual bool RecvSetCacheTokenCachedCharset(const nsCString& charset) override;
   virtual bool RecvSuspend() override;
   virtual bool RecvResume() override;
   virtual bool RecvCancel(const nsresult& status) override;
   virtual bool RecvRedirect2Verify(const nsresult& result,
                                    const RequestHeaderTuples& changedHeaders,
                                    const uint32_t& loadFlags,
+                                   const uint32_t& referrerPolicy,
+                                   const OptionalURIParams& aReferrerURI,
                                    const OptionalURIParams& apiRedirectUri,
                                    const OptionalCorsPreflightArgs& aCorsPreflightArgs,
                                    const bool& aForceHSTSPriming,
                                    const bool& aMixedContentWouldBlock,
                                    const bool& aChooseAppcache) override;
   virtual bool RecvUpdateAssociatedContentSecurity(const int32_t& broken,
                                                    const int32_t& no) override;
   virtual bool RecvDocumentChannelCleanup() override;
--- a/netwerk/protocol/http/PHttpChannel.ipdl
+++ b/netwerk/protocol/http/PHttpChannel.ipdl
@@ -40,17 +40,19 @@ parent:
                                         int32_t no);
   async Suspend();
   async Resume();
 
   async Cancel(nsresult status);
 
   // Reports approval/veto of redirect by child process redirect observers
   async Redirect2Verify(nsresult result, RequestHeaderTuples changedHeaders,
-                        uint32_t loadFlags, OptionalURIParams apiRedirectTo,
+                        uint32_t loadFlags, uint32_t referrerPolicy,
+                        OptionalURIParams referrerUri,
+                        OptionalURIParams apiRedirectTo,
                         OptionalCorsPreflightArgs corsPreflightArgs,
                         bool forceHSTSPriming, bool mixedContentWouldBlock,
                         bool chooseAppcache);
 
   // For document loads we keep this protocol open after child's
   // OnStopRequest, and send this msg (instead of __delete__) to allow
   // partial cleanup on parent.
   async DocumentChannelCleanup();