Referrer Policy: Add new policies to URLRequest.

This patch introduces two new referrer policy at the network level in order
to support stripping detail from the referrer header when a redirect
transitions across origins. These policies support both the "reduce referer
granularity" command-line flag, as well as the yet-to-be-implemented
"OriginWhenCrossOrigin" referrer policy specified at [1].

[1]: http://www.w3.org/TR/referrer-policy/#referrer-policy-state-origin-when-cross-origin

BUG=431711

Review URL: https://codereview.chromium.org/714813003

Cr-Commit-Position: refs/heads/master@{#305211}
diff --git a/build/android/pylib/gtest/filter/net_unittests_disabled b/build/android/pylib/gtest/filter/net_unittests_disabled
index 28e1db1..2632e7c 100644
--- a/build/android/pylib/gtest/filter/net_unittests_disabled
+++ b/build/android/pylib/gtest/filter/net_unittests_disabled
@@ -7,7 +7,6 @@
 SSLServerSocketTest.Handshake
 
 PythonUtils.PythonRunTime
-URLRequestTestHTTP.HTTPSToHTTPRedirectNoRefererTest
 VerifyEndEntity/CertVerifyProcWeakDigestTest.Verify/0
 VerifyEndEntity/CertVerifyProcWeakDigestTest.Verify/1
 VerifyEndEntity/CertVerifyProcWeakDigestTest.Verify/2
@@ -26,6 +25,13 @@
 VerifyRoot/CertVerifyProcWeakDigestTest.Verify/0
 VerifyRoot/CertVerifyProcWeakDigestTest.Verify/1
 VerifyRoot/CertVerifyProcWeakDigestTest.Verify/2
+
+# Can't spin up more than one SpawnedTestServer on Android.
+URLRequestTestReferrerPolicy.HTTPToCrossOriginHTTP
+URLRequestTestReferrerPolicy.HTTPSToCrossOriginHTTPS
+URLRequestTestReferrerPolicy.HTTPToHTTPS
+URLRequestTestReferrerPolicy.HTTPSToHTTP
+
 # Fail only on bots.
 HttpCache.RangeGET_Cancel
 HttpCache.RangeGET_Cancel2
diff --git a/chrome/browser/referrer_policy_browsertest.cc b/chrome/browser/referrer_policy_browsertest.cc
index 897cd62..9101d32 100644
--- a/chrome/browser/referrer_policy_browsertest.cc
+++ b/chrome/browser/referrer_policy_browsertest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/bind.h"
+#include "base/command_line.h"
 #include "base/prefs/pref_service.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
@@ -21,6 +22,7 @@
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test_utils.h"
 #include "net/test/spawned_test_server/spawned_test_server.h"
 #include "third_party/WebKit/public/web/WebInputEvent.h"
@@ -94,6 +96,8 @@
   std::string ReferrerPolicyToString(blink::WebReferrerPolicy referrer_policy) {
     switch (referrer_policy) {
       case blink::WebReferrerPolicyDefault:
+        return "no-meta";
+      case blink::WebReferrerPolicyNoReferrerWhenDowngrade:
         return "default";
       case blink::WebReferrerPolicyOrigin:
         return "origin";
@@ -111,16 +115,23 @@
 
   enum LinkType { REGULAR_LINK, LINK_WITH_TARGET_BLANK, };
 
-  enum RedirectType { NO_REDIRECT, SERVER_REDIRECT, SERVER_REDIRECT_ON_HTTP, };
+  enum RedirectType {
+    NO_REDIRECT,
+    SERVER_REDIRECT_FROM_HTTPS_TO_HTTP,
+    SERVER_REDIRECT_FROM_HTTP_TO_HTTP,
+    SERVER_REDIRECT_FROM_HTTP_TO_HTTPS
+  };
 
   std::string RedirectTypeToString(RedirectType redirect) {
     switch (redirect) {
       case NO_REDIRECT:
         return "none";
-      case SERVER_REDIRECT:
-        return "https";
-      case SERVER_REDIRECT_ON_HTTP:
-        return "http";
+      case SERVER_REDIRECT_FROM_HTTPS_TO_HTTP:
+        return "https2http";
+      case SERVER_REDIRECT_FROM_HTTP_TO_HTTP:
+        return "http2http";
+      case SERVER_REDIRECT_FROM_HTTP_TO_HTTPS:
+        return "http2https";
     }
     NOTREACHED();
     return "";
@@ -356,56 +367,37 @@
 
 // Content initiated navigation, from HTTP to HTTP via server redirect.
 IN_PROC_BROWSER_TEST_F(ReferrerPolicyTest, Redirect) {
-  RunReferrerTest(blink::WebReferrerPolicyOrigin,
-                  START_ON_HTTP,
-                  REGULAR_LINK,
-                  SERVER_REDIRECT,
-                  CURRENT_TAB,
-                  blink::WebMouseEvent::ButtonNone,
-                  EXPECT_ORIGIN_AS_REFERRER);
+  RunReferrerTest(blink::WebReferrerPolicyOrigin, START_ON_HTTP, REGULAR_LINK,
+                  SERVER_REDIRECT_FROM_HTTPS_TO_HTTP, CURRENT_TAB,
+                  blink::WebMouseEvent::ButtonNone, EXPECT_ORIGIN_AS_REFERRER);
 }
 
 // Content initiated navigation, from HTTPS to HTTP via server redirect.
 IN_PROC_BROWSER_TEST_F(ReferrerPolicyTest, HttpsRedirect) {
-  RunReferrerTest(blink::WebReferrerPolicyOrigin,
-                  START_ON_HTTPS,
-                  REGULAR_LINK,
-                  SERVER_REDIRECT,
-                  CURRENT_TAB,
-                  blink::WebMouseEvent::ButtonNone,
-                  EXPECT_ORIGIN_AS_REFERRER);
+  RunReferrerTest(blink::WebReferrerPolicyOrigin, START_ON_HTTPS, REGULAR_LINK,
+                  SERVER_REDIRECT_FROM_HTTPS_TO_HTTP, CURRENT_TAB,
+                  blink::WebMouseEvent::ButtonNone, EXPECT_ORIGIN_AS_REFERRER);
 }
 
 // User initiated navigation, from HTTP to HTTP via server redirect.
 IN_PROC_BROWSER_TEST_F(ReferrerPolicyTest, LeftClickRedirect) {
-  RunReferrerTest(blink::WebReferrerPolicyOrigin,
-                  START_ON_HTTP,
-                  REGULAR_LINK,
-                  SERVER_REDIRECT,
-                  CURRENT_TAB,
-                  blink::WebMouseEvent::ButtonLeft,
-                  EXPECT_ORIGIN_AS_REFERRER);
+  RunReferrerTest(blink::WebReferrerPolicyOrigin, START_ON_HTTP, REGULAR_LINK,
+                  SERVER_REDIRECT_FROM_HTTP_TO_HTTP, CURRENT_TAB,
+                  blink::WebMouseEvent::ButtonLeft, EXPECT_ORIGIN_AS_REFERRER);
 }
 
 // User initiated navigation, from HTTPS to HTTP via server redirect.
 IN_PROC_BROWSER_TEST_F(ReferrerPolicyTest, HttpsLeftClickRedirect) {
-  RunReferrerTest(blink::WebReferrerPolicyOrigin,
-                  START_ON_HTTPS,
-                  REGULAR_LINK,
-                  SERVER_REDIRECT,
-                  CURRENT_TAB,
-                  blink::WebMouseEvent::ButtonLeft,
-                  EXPECT_ORIGIN_AS_REFERRER);
+  RunReferrerTest(blink::WebReferrerPolicyOrigin, START_ON_HTTPS, REGULAR_LINK,
+                  SERVER_REDIRECT_FROM_HTTPS_TO_HTTP, CURRENT_TAB,
+                  blink::WebMouseEvent::ButtonLeft, EXPECT_ORIGIN_AS_REFERRER);
 }
 
 // User initiated navigation, middle click, from HTTP to HTTP via server
 // redirect.
 IN_PROC_BROWSER_TEST_F(ReferrerPolicyTest, MiddleClickRedirect) {
-  RunReferrerTest(blink::WebReferrerPolicyOrigin,
-                  START_ON_HTTP,
-                  REGULAR_LINK,
-                  SERVER_REDIRECT,
-                  NEW_BACKGROUND_TAB,
+  RunReferrerTest(blink::WebReferrerPolicyOrigin, START_ON_HTTP, REGULAR_LINK,
+                  SERVER_REDIRECT_FROM_HTTPS_TO_HTTP, NEW_BACKGROUND_TAB,
                   blink::WebMouseEvent::ButtonMiddle,
                   EXPECT_ORIGIN_AS_REFERRER);
 }
@@ -413,11 +405,8 @@
 // User initiated navigation, middle click, from HTTPS to HTTP via server
 // redirect.
 IN_PROC_BROWSER_TEST_F(ReferrerPolicyTest, HttpsMiddleClickRedirect) {
-  RunReferrerTest(blink::WebReferrerPolicyOrigin,
-                  START_ON_HTTPS,
-                  REGULAR_LINK,
-                  SERVER_REDIRECT,
-                  NEW_BACKGROUND_TAB,
+  RunReferrerTest(blink::WebReferrerPolicyOrigin, START_ON_HTTPS, REGULAR_LINK,
+                  SERVER_REDIRECT_FROM_HTTPS_TO_HTTP, NEW_BACKGROUND_TAB,
                   blink::WebMouseEvent::ButtonMiddle,
                   EXPECT_ORIGIN_AS_REFERRER);
 }
@@ -425,36 +414,27 @@
 // User initiated navigation, target blank, from HTTP to HTTP via server
 // redirect.
 IN_PROC_BROWSER_TEST_F(ReferrerPolicyTest, TargetBlankRedirect) {
-  RunReferrerTest(blink::WebReferrerPolicyOrigin,
-                  START_ON_HTTP,
-                  LINK_WITH_TARGET_BLANK,
-                  SERVER_REDIRECT,
-                  NEW_FOREGROUND_TAB,
-                  blink::WebMouseEvent::ButtonLeft,
+  RunReferrerTest(blink::WebReferrerPolicyOrigin, START_ON_HTTP,
+                  LINK_WITH_TARGET_BLANK, SERVER_REDIRECT_FROM_HTTPS_TO_HTTP,
+                  NEW_FOREGROUND_TAB, blink::WebMouseEvent::ButtonLeft,
                   EXPECT_ORIGIN_AS_REFERRER);
 }
 
 // User initiated navigation, target blank, from HTTPS to HTTP via server
 // redirect.
 IN_PROC_BROWSER_TEST_F(ReferrerPolicyTest, HttpsTargetBlankRedirect) {
-  RunReferrerTest(blink::WebReferrerPolicyOrigin,
-                  START_ON_HTTPS,
-                  LINK_WITH_TARGET_BLANK,
-                  SERVER_REDIRECT,
-                  NEW_FOREGROUND_TAB,
-                  blink::WebMouseEvent::ButtonLeft,
+  RunReferrerTest(blink::WebReferrerPolicyOrigin, START_ON_HTTPS,
+                  LINK_WITH_TARGET_BLANK, SERVER_REDIRECT_FROM_HTTPS_TO_HTTP,
+                  NEW_FOREGROUND_TAB, blink::WebMouseEvent::ButtonLeft,
                   EXPECT_ORIGIN_AS_REFERRER);
 }
 
 // User initiated navigation, middle click, target blank, from HTTP to HTTP via
 // server redirect.
 IN_PROC_BROWSER_TEST_F(ReferrerPolicyTest, MiddleClickTargetBlankRedirect) {
-  RunReferrerTest(blink::WebReferrerPolicyOrigin,
-                  START_ON_HTTP,
-                  LINK_WITH_TARGET_BLANK,
-                  SERVER_REDIRECT,
-                  NEW_FOREGROUND_TAB,
-                  blink::WebMouseEvent::ButtonMiddle,
+  RunReferrerTest(blink::WebReferrerPolicyOrigin, START_ON_HTTP,
+                  LINK_WITH_TARGET_BLANK, SERVER_REDIRECT_FROM_HTTPS_TO_HTTP,
+                  NEW_FOREGROUND_TAB, blink::WebMouseEvent::ButtonMiddle,
                   EXPECT_ORIGIN_AS_REFERRER);
 }
 
@@ -462,12 +442,9 @@
 // via server redirect.
 IN_PROC_BROWSER_TEST_F(ReferrerPolicyTest,
                        HttpsMiddleClickTargetBlankRedirect) {
-  RunReferrerTest(blink::WebReferrerPolicyOrigin,
-                  START_ON_HTTPS,
-                  LINK_WITH_TARGET_BLANK,
-                  SERVER_REDIRECT,
-                  NEW_FOREGROUND_TAB,
-                  blink::WebMouseEvent::ButtonMiddle,
+  RunReferrerTest(blink::WebReferrerPolicyOrigin, START_ON_HTTPS,
+                  LINK_WITH_TARGET_BLANK, SERVER_REDIRECT_FROM_HTTPS_TO_HTTP,
+                  NEW_FOREGROUND_TAB, blink::WebMouseEvent::ButtonMiddle,
                   EXPECT_ORIGIN_AS_REFERRER);
 }
 
@@ -475,39 +452,28 @@
 IN_PROC_BROWSER_TEST_F(ReferrerPolicyTest, ContextMenuRedirect) {
   ContextMenuNotificationObserver context_menu_observer(
       IDC_CONTENT_CONTEXT_OPENLINKNEWTAB);
-  RunReferrerTest(blink::WebReferrerPolicyOrigin,
-                  START_ON_HTTP,
-                  REGULAR_LINK,
-                  SERVER_REDIRECT,
-                  NEW_FOREGROUND_TAB,
-                  blink::WebMouseEvent::ButtonRight,
-                  EXPECT_ORIGIN_AS_REFERRER);
+  RunReferrerTest(blink::WebReferrerPolicyOrigin, START_ON_HTTP, REGULAR_LINK,
+                  SERVER_REDIRECT_FROM_HTTPS_TO_HTTP, NEW_FOREGROUND_TAB,
+                  blink::WebMouseEvent::ButtonRight, EXPECT_ORIGIN_AS_REFERRER);
 }
 
 // Context menu, from HTTPS to HTTP via server redirect.
 IN_PROC_BROWSER_TEST_F(ReferrerPolicyTest, HttpsContextMenuRedirect) {
   ContextMenuNotificationObserver context_menu_observer(
       IDC_CONTENT_CONTEXT_OPENLINKNEWTAB);
-  RunReferrerTest(blink::WebReferrerPolicyOrigin,
-                  START_ON_HTTPS,
-                  REGULAR_LINK,
-                  SERVER_REDIRECT,
-                  NEW_FOREGROUND_TAB,
-                  blink::WebMouseEvent::ButtonRight,
-                  EXPECT_ORIGIN_AS_REFERRER);
+  RunReferrerTest(blink::WebReferrerPolicyOrigin, START_ON_HTTPS, REGULAR_LINK,
+                  SERVER_REDIRECT_FROM_HTTPS_TO_HTTP, NEW_FOREGROUND_TAB,
+                  blink::WebMouseEvent::ButtonRight, EXPECT_ORIGIN_AS_REFERRER);
 }
 
 // Tests history navigation actions: Navigate from A to B with a referrer
 // policy, then navigate to C, back to B, and reload.
 IN_PROC_BROWSER_TEST_F(ReferrerPolicyTest, History) {
   // Navigate from A to B.
-  GURL start_url = RunReferrerTest(blink::WebReferrerPolicyOrigin,
-                                   START_ON_HTTPS,
-                                   REGULAR_LINK,
-                                   SERVER_REDIRECT,
-                                   CURRENT_TAB,
-                                   blink::WebMouseEvent::ButtonLeft,
-                                   EXPECT_ORIGIN_AS_REFERRER);
+  GURL start_url = RunReferrerTest(
+      blink::WebReferrerPolicyOrigin, START_ON_HTTPS, REGULAR_LINK,
+      SERVER_REDIRECT_FROM_HTTPS_TO_HTTP, CURRENT_TAB,
+      blink::WebMouseEvent::ButtonLeft, EXPECT_ORIGIN_AS_REFERRER);
 
   // Navigate to C.
   ui_test_utils::NavigateToURL(browser(), test_server_->GetURL(std::string()));
@@ -544,13 +510,10 @@
 // Tests that reloading a site for "request tablet version" correctly clears
 // the referrer.
 IN_PROC_BROWSER_TEST_F(ReferrerPolicyTest, RequestTabletSite) {
-  GURL start_url = RunReferrerTest(blink::WebReferrerPolicyOrigin,
-                                   START_ON_HTTPS,
-                                   REGULAR_LINK,
-                                   SERVER_REDIRECT_ON_HTTP,
-                                   CURRENT_TAB,
-                                   blink::WebMouseEvent::ButtonLeft,
-                                   EXPECT_ORIGIN_AS_REFERRER);
+  GURL start_url = RunReferrerTest(
+      blink::WebReferrerPolicyOrigin, START_ON_HTTPS, REGULAR_LINK,
+      SERVER_REDIRECT_FROM_HTTP_TO_HTTP, CURRENT_TAB,
+      blink::WebMouseEvent::ButtonLeft, EXPECT_ORIGIN_AS_REFERRER);
 
   base::string16 expected_title =
       GetExpectedTitle(start_url, EXPECT_EMPTY_REFERRER);
@@ -618,3 +581,20 @@
                 test_server_->GetURL("files/referrer-policy-log.html").spec(),
             title);
 }
+
+// Reduced 'referer' granularity flag tests.
+
+// User initiated navigation, from HTTP to HTTPS via server redirect.
+IN_PROC_BROWSER_TEST_F(ReferrerPolicyTest, HttpLeftClickRedirectDefaultNoFlag) {
+  RunReferrerTest(blink::WebReferrerPolicyDefault, START_ON_HTTP, REGULAR_LINK,
+                  SERVER_REDIRECT_FROM_HTTP_TO_HTTPS, CURRENT_TAB,
+                  blink::WebMouseEvent::ButtonLeft, EXPECT_FULL_REFERRER);
+}
+
+IN_PROC_BROWSER_TEST_F(ReferrerPolicyTest, HttpLeftClickRedirectDefaultFlag) {
+  CommandLine::ForCurrentProcess()->AppendSwitch(
+      switches::kReducedReferrerGranularity);
+  RunReferrerTest(blink::WebReferrerPolicyDefault, START_ON_HTTP, REGULAR_LINK,
+                  SERVER_REDIRECT_FROM_HTTP_TO_HTTPS, CURRENT_TAB,
+                  blink::WebMouseEvent::ButtonLeft, EXPECT_ORIGIN_AS_REFERRER);
+}
diff --git a/chrome/test/data/referrer_policy/referrer-policy-start.html b/chrome/test/data/referrer_policy/referrer-policy-start.html
index 5f5c457..ac580fa 100644
--- a/chrome/test/data/referrer_policy/referrer-policy-start.html
+++ b/chrome/test/data/referrer_policy/referrer-policy-start.html
@@ -18,26 +18,32 @@
     return;
   }
 
-  var meta = document.createElement("meta");
-  meta.name = "referrer";
-  meta.content = matches[kPolicy];
-  document.head.appendChild(meta);
+  if (matches[kPolicy] != "no-meta") {
+    var meta = document.createElement("meta");
+    meta.name = "referrer";
+    meta.content = matches[kPolicy];
+    document.head.appendChild(meta);
+  }
 
   var destination;
 
-  if (matches[kRedirect] == "false") {
+  if (matches[kRedirect] == "none") {
     destination = "http://127.0.0.1:" + matches[kPort] +
                   "/files/referrer-policy-log.html";
-  } else if (matches[kRedirect] == "http") {
+  } else if (matches[kRedirect] == "http2http") {
     destination = "http://127.0.0.1:" + matches[kPort] +
                   "/server-redirect?http://127.0.0.1:" + matches[kPort] +
                   "/files/referrer-policy-log.html";
+  } else if (matches[kRedirect] == "http2https") {
+    destination = "http://127.0.0.1:" + matches[kPort] +
+                  "/server-redirect?https://127.0.0.1:" + matches[kSslPort] +
+                  "/files/referrer-policy-log.html";
+  } else if (matches[kRedirect] == "https2http") {
+    destination = "https://127.0.0.1:" + matches[kSslPort] +
+                  "/server-redirect?http://127.0.0.1:" + matches[kPort] +
+                  "/files/referrer-policy-log.html";
   } else if (matches[kRedirect] == "echoheader") {
     destination = "http://127.0.0.1:" + matches[kPort] + "/echoheader?Referer";
-  } else {
-    destination = "https://127.0.0.1:" + matches[kSslPort] +
-                  "/server-redirect?http://127.0.0.1:" + matches[kPort] +
-                  "/files/referrer-policy-log.html";
   }
 
   if (matches[kLink] == "true") {
diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc
index c8341d6..85ac2bf 100644
--- a/content/browser/loader/resource_dispatcher_host_impl.cc
+++ b/content/browser/loader/resource_dispatcher_host_impl.cc
@@ -231,9 +231,9 @@
 }
 
 void SetReferrerForRequest(net::URLRequest* request, const Referrer& referrer) {
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   if (!referrer.url.is_valid() ||
-      base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kNoReferrers)) {
+      command_line->HasSwitch(switches::kNoReferrers)) {
     request->SetReferrer(std::string());
   } else {
     request->SetReferrer(referrer.url.spec());
@@ -247,10 +247,18 @@
     case blink::WebReferrerPolicyOrigin:
       net_referrer_policy = net::URLRequest::NEVER_CLEAR_REFERRER;
       break;
+    case blink::WebReferrerPolicyNoReferrerWhenDowngrade:
+      net_referrer_policy =
+          net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE;
+      break;
     case blink::WebReferrerPolicyDefault:
     default:
       net_referrer_policy =
-          net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE;
+          command_line->HasSwitch(switches::kReducedReferrerGranularity)
+              ? net::URLRequest::
+                    REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN
+              : net::URLRequest::
+                    CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE;
       break;
   }
   request->set_referrer_policy(net_referrer_policy);
diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc
index a2ce07e..c61a14b 100644
--- a/net/url_request/url_request.cc
+++ b/net/url_request/url_request.cc
@@ -650,12 +650,8 @@
 
   response_info_.was_cached = false;
 
-  // If the referrer is secure, but the requested URL is not, the referrer
-  // policy should be something non-default. If you hit this, please file a
-  // bug.
-  if (referrer_policy_ ==
-          CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE &&
-      GURL(referrer_).SchemeIsSecure() && !url().SchemeIsSecure()) {
+  if (GURL(referrer_) != URLRequestJob::ComputeReferrerForRedirect(
+                             referrer_policy_, referrer_, url())) {
     if (!network_delegate_ ||
         !network_delegate_->CancelURLRequestWithPolicyViolatingReferrerHeader(
             *this, url(), GURL(referrer_))) {
diff --git a/net/url_request/url_request.h b/net/url_request/url_request.h
index 6117419..af972bd 100644
--- a/net/url_request/url_request.h
+++ b/net/url_request/url_request.h
@@ -94,14 +94,22 @@
   // referrer header might be cleared, if the protocol changes from HTTPS to
   // HTTP. This is the default behavior of URLRequest, corresponding to
   // CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE. Alternatively, the
-  // referrer policy can be set to never change the referrer header. This
-  // behavior corresponds to NEVER_CLEAR_REFERRER. Embedders will want to use
-  // NEVER_CLEAR_REFERRER when implementing the meta-referrer support
-  // (http://wiki.whatwg.org/wiki/Meta_referrer) and sending requests with a
-  // non-default referrer policy. Only the default referrer policy requires
-  // the referrer to be cleared on transitions from HTTPS to HTTP.
+  // referrer policy can be set to strip the referrer down to an origin upon
+  // cross-origin navigation (ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN), or
+  // never change the referrer header (NEVER_CLEAR_REFERRER). Embedders will
+  // want to use these options when implementing referrer policy support
+  // (https://w3c.github.io/webappsec/specs/referrer-policy/).
+  //
+  // REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN is a slight variant
+  // on CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE: If the request
+  // downgrades from HTTPS to HTTP, the referrer will be cleared. If the request
+  // transitions cross-origin (but does not downgrade), the referrer's
+  // granularity will be reduced (currently stripped down to an origin rather
+  // than a full URL). Same-origin requests will send the full referrer.
   enum ReferrerPolicy {
     CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
+    REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN,
+    ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN,
     NEVER_CLEAR_REFERRER,
   };
 
diff --git a/net/url_request/url_request_job.cc b/net/url_request/url_request_job.cc
index 5d32c6f..3cd8cf4 100644
--- a/net/url_request/url_request_job.cc
+++ b/net/url_request/url_request_job.cc
@@ -20,7 +20,6 @@
 #include "net/base/network_delegate.h"
 #include "net/filter/filter.h"
 #include "net/http/http_response_headers.h"
-#include "net/url_request/url_request.h"
 
 namespace {
 
@@ -255,6 +254,42 @@
 void URLRequestJob::NotifyURLRequestDestroyed() {
 }
 
+// static
+GURL URLRequestJob::ComputeReferrerForRedirect(
+    URLRequest::ReferrerPolicy policy,
+    const std::string& referrer,
+    const GURL& redirect_destination) {
+  GURL original_referrer(referrer);
+  bool secure_referrer_but_insecure_destination =
+      original_referrer.SchemeIsSecure() &&
+      !redirect_destination.SchemeIsSecure();
+  bool same_origin =
+      original_referrer.GetOrigin() == redirect_destination.GetOrigin();
+  switch (policy) {
+    case URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE:
+      return secure_referrer_but_insecure_destination ? GURL()
+                                                      : original_referrer;
+
+    case URLRequest::REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN:
+      if (same_origin) {
+        return original_referrer;
+      } else if (secure_referrer_but_insecure_destination) {
+        return GURL();
+      } else {
+        return original_referrer.GetOrigin();
+      }
+
+    case URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN:
+      return same_origin ? original_referrer : original_referrer.GetOrigin();
+
+    case URLRequest::NEVER_CLEAR_REFERRER:
+      return original_referrer;
+  }
+
+  NOTREACHED();
+  return GURL();
+}
+
 URLRequestJob::~URLRequestJob() {
   base::PowerMonitor* power_monitor = base::PowerMonitor::Get();
   if (power_monitor)
@@ -863,15 +898,11 @@
         request_->first_party_for_cookies();
   }
 
-  // Suppress the referrer if we're redirecting out of https.
-  if (request_->referrer_policy() ==
-          URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE &&
-      GURL(request_->referrer()).SchemeIsSecure() &&
-      !redirect_info.new_url.SchemeIsSecure()) {
-    redirect_info.new_referrer.clear();
-  } else {
-    redirect_info.new_referrer = request_->referrer();
-  }
+  // Alter the referrer if redirecting cross-origin (especially HTTP->HTTPS).
+  redirect_info.new_referrer =
+      ComputeReferrerForRedirect(request_->referrer_policy(),
+                                 request_->referrer(),
+                                 redirect_info.new_url).spec();
 
   return redirect_info;
 }
diff --git a/net/url_request/url_request_job.h b/net/url_request/url_request_job.h
index e824689..ceb71f5 100644
--- a/net/url_request/url_request_job.h
+++ b/net/url_request/url_request_job.h
@@ -20,6 +20,7 @@
 #include "net/base/upload_progress.h"
 #include "net/cookies/canonical_cookie.h"
 #include "net/url_request/redirect_info.h"
+#include "net/url_request/url_request.h"
 #include "url/gurl.h"
 
 namespace net {
@@ -36,7 +37,6 @@
 class NetworkDelegate;
 class SSLCertRequestInfo;
 class SSLInfo;
-class URLRequest;
 class UploadDataStream;
 class URLRequestStatus;
 class X509Certificate;
@@ -227,6 +227,12 @@
   // canceled by an explicit NetworkDelegate::NotifyURLRequestDestroyed() call.
   virtual void NotifyURLRequestDestroyed();
 
+  // Given |policy|, |referrer|, and |redirect_destination|, returns the
+  // referrer URL mandated by |request|'s referrer policy.
+  static GURL ComputeReferrerForRedirect(URLRequest::ReferrerPolicy policy,
+                                         const std::string& referrer,
+                                         const GURL& redirect_destination);
+
  protected:
   friend class base::RefCounted<URLRequestJob>;
   ~URLRequestJob() override;
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index 1d777dc..c32d937 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -3913,32 +3913,6 @@
   }
 }
 
-TEST_F(URLRequestTestHTTP, HTTPSToHTTPRedirectNoRefererTest) {
-  ASSERT_TRUE(test_server_.Start());
-
-  SpawnedTestServer https_test_server(
-      SpawnedTestServer::TYPE_HTTPS, SpawnedTestServer::kLocalhost,
-      base::FilePath(FILE_PATH_LITERAL("net/data/ssl")));
-  ASSERT_TRUE(https_test_server.Start());
-
-  // An https server is sent a request with an https referer,
-  // and responds with a redirect to an http url. The http
-  // server should not be sent the referer.
-  GURL http_destination = test_server_.GetURL(std::string());
-  TestDelegate d;
-  scoped_ptr<URLRequest> req(default_context_.CreateRequest(
-      https_test_server.GetURL("server-redirect?" + http_destination.spec()),
-      DEFAULT_PRIORITY, &d, NULL));
-  req->SetReferrer("https://www.referrer.com/");
-  req->Start();
-  base::RunLoop().Run();
-
-  EXPECT_EQ(1, d.response_started_count());
-  EXPECT_EQ(1, d.received_redirect_count());
-  EXPECT_EQ(http_destination, req->url());
-  EXPECT_EQ(std::string(), req->referrer());
-}
-
 TEST_F(URLRequestTestHTTP, RedirectLoadTiming) {
   ASSERT_TRUE(test_server_.Start());
 
@@ -6727,6 +6701,219 @@
   EXPECT_EQ(2, default_network_delegate()->headers_received_count());
 }
 
+class URLRequestTestReferrerPolicy : public URLRequestTest {
+ public:
+  URLRequestTestReferrerPolicy() {}
+
+  void InstantiateSameOriginServers(SpawnedTestServer::Type origin_type) {
+    origin_server_.reset(new SpawnedTestServer(
+        origin_type, SpawnedTestServer::kLocalhost,
+        origin_type == SpawnedTestServer::TYPE_HTTPS
+            ? base::FilePath(FILE_PATH_LITERAL("net/data/ssl"))
+            : base::FilePath(
+                  FILE_PATH_LITERAL("net/data/url_request_unittest"))));
+    ASSERT_TRUE(origin_server_->Start());
+  }
+
+  void InstantiateCrossOriginServers(SpawnedTestServer::Type origin_type,
+                                     SpawnedTestServer::Type destination_type) {
+    origin_server_.reset(new SpawnedTestServer(
+        origin_type, SpawnedTestServer::kLocalhost,
+        origin_type == SpawnedTestServer::TYPE_HTTPS
+            ? base::FilePath(FILE_PATH_LITERAL("net/data/ssl"))
+            : base::FilePath(
+                  FILE_PATH_LITERAL("net/data/url_request_unittest"))));
+    ASSERT_TRUE(origin_server_->Start());
+
+    destination_server_.reset(new SpawnedTestServer(
+        destination_type, SpawnedTestServer::kLocalhost,
+        destination_type == SpawnedTestServer::TYPE_HTTPS
+            ? base::FilePath(FILE_PATH_LITERAL("net/data/ssl"))
+            : base::FilePath(
+                  FILE_PATH_LITERAL("net/data/url_request_unittest"))));
+    ASSERT_TRUE(destination_server_->Start());
+  }
+
+  void VerifyReferrerAfterRedirect(URLRequest::ReferrerPolicy policy,
+                                   const GURL& referrer,
+                                   const GURL& expected) {
+    // Create and execute the request: we'll only have a |destination_server_|
+    // if the origins are meant to be distinct. Otherwise, we'll use the
+    // |origin_server_| for both endpoints.
+    GURL destination_url =
+        destination_server_ ? destination_server_->GetURL("echoheader?Referer")
+                            : origin_server_->GetURL("echoheader?Referer");
+    GURL origin_url =
+        origin_server_->GetURL("server-redirect?" + destination_url.spec());
+
+    TestDelegate d;
+    scoped_ptr<URLRequest> req(
+        default_context_.CreateRequest(origin_url, DEFAULT_PRIORITY, &d, NULL));
+    req->set_referrer_policy(policy);
+    req->SetReferrer(referrer.spec());
+    req->Start();
+    base::RunLoop().Run();
+
+    EXPECT_EQ(1, d.response_started_count());
+    EXPECT_EQ(1, d.received_redirect_count());
+    EXPECT_EQ(destination_url, req->url());
+    EXPECT_TRUE(req->status().is_success());
+    EXPECT_EQ(200, req->response_headers()->response_code());
+
+    EXPECT_EQ(expected.spec(), req->referrer());
+    if (expected.is_empty())
+      EXPECT_EQ("None", d.data_received());
+    else
+      EXPECT_EQ(expected.spec(), d.data_received());
+  }
+
+  SpawnedTestServer* origin_server() const { return origin_server_.get(); }
+
+ private:
+  scoped_ptr<SpawnedTestServer> origin_server_;
+  scoped_ptr<SpawnedTestServer> destination_server_;
+};
+
+TEST_F(URLRequestTestReferrerPolicy, HTTPToSameOriginHTTP) {
+  InstantiateSameOriginServers(SpawnedTestServer::TYPE_HTTP);
+
+  VerifyReferrerAfterRedirect(
+      URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
+      origin_server()->GetURL("path/to/file.html"),
+      origin_server()->GetURL("path/to/file.html"));
+
+  VerifyReferrerAfterRedirect(
+      URLRequest::REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN,
+      origin_server()->GetURL("path/to/file.html"),
+      origin_server()->GetURL("path/to/file.html"));
+
+  VerifyReferrerAfterRedirect(
+      URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN,
+      origin_server()->GetURL("path/to/file.html"),
+      origin_server()->GetURL("path/to/file.html"));
+
+  VerifyReferrerAfterRedirect(URLRequest::NEVER_CLEAR_REFERRER,
+                              origin_server()->GetURL("path/to/file.html"),
+                              origin_server()->GetURL("path/to/file.html"));
+}
+
+TEST_F(URLRequestTestReferrerPolicy, HTTPToCrossOriginHTTP) {
+  InstantiateCrossOriginServers(SpawnedTestServer::TYPE_HTTP,
+                                SpawnedTestServer::TYPE_HTTP);
+
+  VerifyReferrerAfterRedirect(
+      URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
+      origin_server()->GetURL("path/to/file.html"),
+      origin_server()->GetURL("path/to/file.html"));
+
+  VerifyReferrerAfterRedirect(
+      URLRequest::REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN,
+      origin_server()->GetURL("path/to/file.html"),
+      origin_server()->GetURL(std::string()));
+
+  VerifyReferrerAfterRedirect(
+      URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN,
+      origin_server()->GetURL("path/to/file.html"),
+      origin_server()->GetURL(std::string()));
+
+  VerifyReferrerAfterRedirect(URLRequest::NEVER_CLEAR_REFERRER,
+                              origin_server()->GetURL("path/to/file.html"),
+                              origin_server()->GetURL("path/to/file.html"));
+}
+
+TEST_F(URLRequestTestReferrerPolicy, HTTPSToSameOriginHTTPS) {
+  InstantiateSameOriginServers(SpawnedTestServer::TYPE_HTTPS);
+
+  VerifyReferrerAfterRedirect(
+      URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
+      origin_server()->GetURL("path/to/file.html"),
+      origin_server()->GetURL("path/to/file.html"));
+
+  VerifyReferrerAfterRedirect(
+      URLRequest::REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN,
+      origin_server()->GetURL("path/to/file.html"),
+      origin_server()->GetURL("path/to/file.html"));
+
+  VerifyReferrerAfterRedirect(
+      URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN,
+      origin_server()->GetURL("path/to/file.html"),
+      origin_server()->GetURL("path/to/file.html"));
+
+  VerifyReferrerAfterRedirect(URLRequest::NEVER_CLEAR_REFERRER,
+                              origin_server()->GetURL("path/to/file.html"),
+                              origin_server()->GetURL("path/to/file.html"));
+}
+
+TEST_F(URLRequestTestReferrerPolicy, HTTPSToCrossOriginHTTPS) {
+  InstantiateCrossOriginServers(SpawnedTestServer::TYPE_HTTPS,
+                                SpawnedTestServer::TYPE_HTTPS);
+
+  VerifyReferrerAfterRedirect(
+      URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
+      origin_server()->GetURL("path/to/file.html"),
+      origin_server()->GetURL("path/to/file.html"));
+
+  VerifyReferrerAfterRedirect(
+      URLRequest::REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN,
+      origin_server()->GetURL("path/to/file.html"),
+      origin_server()->GetURL(std::string()));
+
+  VerifyReferrerAfterRedirect(
+      URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN,
+      origin_server()->GetURL("path/to/file.html"),
+      origin_server()->GetURL(std::string()));
+
+  VerifyReferrerAfterRedirect(URLRequest::NEVER_CLEAR_REFERRER,
+                              origin_server()->GetURL("path/to/file.html"),
+                              origin_server()->GetURL("path/to/file.html"));
+}
+
+TEST_F(URLRequestTestReferrerPolicy, HTTPToHTTPS) {
+  InstantiateCrossOriginServers(SpawnedTestServer::TYPE_HTTP,
+                                SpawnedTestServer::TYPE_HTTPS);
+
+  VerifyReferrerAfterRedirect(
+      URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
+      origin_server()->GetURL("path/to/file.html"),
+      origin_server()->GetURL("path/to/file.html"));
+
+  VerifyReferrerAfterRedirect(
+      URLRequest::REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN,
+      origin_server()->GetURL("path/to/file.html"),
+      origin_server()->GetURL(std::string()));
+
+  VerifyReferrerAfterRedirect(
+      URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN,
+      origin_server()->GetURL("path/to/file.html"),
+      origin_server()->GetURL(std::string()));
+
+  VerifyReferrerAfterRedirect(URLRequest::NEVER_CLEAR_REFERRER,
+                              origin_server()->GetURL("path/to/file.html"),
+                              origin_server()->GetURL("path/to/file.html"));
+}
+
+TEST_F(URLRequestTestReferrerPolicy, HTTPSToHTTP) {
+  InstantiateCrossOriginServers(SpawnedTestServer::TYPE_HTTPS,
+                                SpawnedTestServer::TYPE_HTTP);
+
+  VerifyReferrerAfterRedirect(
+      URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
+      origin_server()->GetURL("path/to/file.html"), GURL());
+
+  VerifyReferrerAfterRedirect(
+      URLRequest::REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN,
+      origin_server()->GetURL("path/to/file.html"), GURL());
+
+  VerifyReferrerAfterRedirect(
+      URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN,
+      origin_server()->GetURL("path/to/file.html"),
+      origin_server()->GetURL(std::string()));
+
+  VerifyReferrerAfterRedirect(URLRequest::NEVER_CLEAR_REFERRER,
+                              origin_server()->GetURL("path/to/file.html"),
+                              origin_server()->GetURL("path/to/file.html"));
+}
+
 class HTTPSRequestTest : public testing::Test {
  public:
   HTTPSRequestTest() : default_context_(true) {