Bug 1416344 - refactor computing referrer policy and remove uninitilized maybe value draft
authorThomas Nguyen <tnguyen@mozilla.com>
Mon, 13 Nov 2017 19:23:47 +0800
changeset 698001 9f69f6dfb0c0e00ea12d587ac6f6db3780496577
parent 697940 f0c0fb9182d695081edf170d8e3bcb8164f2c96a
child 740267 eb35e70ceb404fd5381c7a8f65145c01cf07cb1a
push id89169
push userbmo:tnguyen@mozilla.com
push dateWed, 15 Nov 2017 04:44:36 +0000
bugs1416344
milestone59.0a1
Bug 1416344 - refactor computing referrer policy and remove uninitilized maybe value MozReview-Commit-ID: 7VoRaUSE096
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/http/HttpBaseChannel.h
netwerk/test/unit/test_referrer_cross_origin.js
netwerk/test/unit/xpcshell.ini
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -1560,16 +1560,46 @@ HttpBaseChannel::SetReferrer(nsIURI *ref
 NS_IMETHODIMP
 HttpBaseChannel::GetReferrerPolicy(uint32_t *referrerPolicy)
 {
   NS_ENSURE_ARG_POINTER(referrerPolicy);
   *referrerPolicy = mReferrerPolicy;
   return NS_OK;
 }
 
+
+ /* Computing whether our URI is cross-origin may be expensive, so please do
+  * that in cases where we're going to use this information later on.
+  */
+bool
+HttpBaseChannel::IsCrossOriginWithReferrer()
+{
+  nsresult rv;
+  nsCOMPtr<nsIURI> triggeringURI;
+  if (mLoadInfo) {
+    nsCOMPtr<nsIPrincipal> triggeringPrincipal = mLoadInfo->TriggeringPrincipal();
+    if (triggeringPrincipal) {
+      triggeringPrincipal->GetURI(getter_AddRefs(triggeringURI));
+    }
+  }
+  if (triggeringURI) {
+    if (LOG_ENABLED()) {
+      nsAutoCString triggeringURISpec;
+      triggeringURI->GetAsciiSpec(triggeringURISpec);
+      LOG(("triggeringURI=%s\n", triggeringURISpec.get()));
+    }
+    nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+    rv = ssm->CheckSameOriginURI(triggeringURI, mURI, false);
+    return (NS_FAILED(rv));
+  }
+
+  LOG(("no triggering principal available via loadInfo, assuming load is cross-origin"));
+  return true;
+}
+
 NS_IMETHODIMP
 HttpBaseChannel::SetReferrerWithPolicy(nsIURI *referrer,
                                        uint32_t referrerPolicy)
 {
   ENSURE_CALLED_BEFORE_CONNECT();
 
   mReferrerPolicy = referrerPolicy;
 
@@ -1601,21 +1631,16 @@ HttpBaseChannel::SetReferrerWithPolicy(n
   // false: use real referrer
   // true: spoof with URI of the current request
   bool userSpoofReferrerSource = gHttpHandler->SpoofReferrerSource();
 
   // false: use real referrer when leaving .onion
   // true: use an empty referrer
   bool userHideOnionReferrerSource = gHttpHandler->HideOnionReferrerSource();
 
-  // 0: full URI
-  // 1: scheme+host+port+path
-  // 2: scheme+host+port
-  int userReferrerTrimmingPolicy = gHttpHandler->ReferrerTrimmingPolicy();
-
   // 0: send referer no matter what
   // 1: send referer ONLY when base domains match
   // 2: send referer ONLY when hosts match
   int userReferrerXOriginPolicy = gHttpHandler->ReferrerXOriginPolicy();
 
   // check referrer blocking pref
   uint32_t referrerLevel;
   if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) {
@@ -1762,95 +1787,67 @@ HttpBaseChannel::SetReferrerWithPolicy(n
     currentHost = referrerHost;
   }
 
   // strip away any userpass; we don't want to be giving out passwords ;-)
   // This is required by Referrer Policy stripping algorithm.
   rv = clone->SetUserPass(EmptyCString());
   if (NS_FAILED(rv)) return rv;
 
-  // Computing whether our URI is cross-origin may be expensive, so we only do
-  // that in cases where we're going to use this information later on.  The if
-  // condition below encodes those cases.  isCrossOrigin.isNothing() will return
-  // true otherwise.
-  Maybe<bool> isCrossOrigin;
-  if ((mReferrerPolicy == REFERRER_POLICY_SAME_ORIGIN ||
-       mReferrerPolicy == REFERRER_POLICY_ORIGIN_WHEN_XORIGIN ||
-       mReferrerPolicy == REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN ||
-       // If our referrer policy is origin-only or strict-origin, we will send
-       // the origin only no matter if we are cross origin, so in those cases we
-       // can also skip checking cross-origin-ness.
-       (gHttpHandler->ReferrerXOriginTrimmingPolicy() != 0 &&
-        mReferrerPolicy != REFERRER_POLICY_ORIGIN &&
-        mReferrerPolicy != REFERRER_POLICY_STRICT_ORIGIN)) &&
-      // 2 (origin-only) is already the strictest policy which we'd adopt if we
-      // were cross-origin, so there is no point to compute whether we are or
-      // not.
-      gHttpHandler->ReferrerTrimmingPolicy() != 2) {
-    // for cross-origin-based referrer changes (not just host-based), figure out
-    // if the referrer is being sent cross-origin.
-    nsCOMPtr<nsIURI> triggeringURI;
-    if (mLoadInfo) {
-      nsCOMPtr<nsIPrincipal> triggeringPrincipal = mLoadInfo->TriggeringPrincipal();
-      if (triggeringPrincipal) {
-        triggeringPrincipal->GetURI(getter_AddRefs(triggeringURI));
+  // 0: full URI
+  // 1: scheme+host+port+path
+  // 2: scheme+host+port
+  int userReferrerTrimmingPolicy = gHttpHandler->ReferrerTrimmingPolicy();
+  int userReferrerXOriginTrimmingPolicy =
+    gHttpHandler->ReferrerXOriginTrimmingPolicy();
+
+  switch (mReferrerPolicy) {
+    case REFERRER_POLICY_SAME_ORIGIN:
+      // Don't send referrer when the request is cross-origin and policy is "same-origin".
+      if (IsCrossOriginWithReferrer()) {
+        return NS_OK;
       }
-    }
-    if (triggeringURI) {
-      if (LOG_ENABLED()) {
-        nsAutoCString triggeringURISpec;
-        rv = triggeringURI->GetAsciiSpec(triggeringURISpec);
-        if (!NS_FAILED(rv)) {
-          LOG(("triggeringURI=%s\n", triggeringURISpec.get()));
+      break;
+
+    case REFERRER_POLICY_ORIGIN:
+    case REFERRER_POLICY_STRICT_ORIGIN:
+      userReferrerTrimmingPolicy = 2;
+      break;
+
+    case REFERRER_POLICY_ORIGIN_WHEN_XORIGIN:
+    case REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN:
+      if (userReferrerTrimmingPolicy != 2 && IsCrossOriginWithReferrer()) {
+        // Ignore set userReferrerTrimmingPolicy if it is already the strictest
+        // policy.
+        userReferrerTrimmingPolicy = 2;
+      }
+      break;
+
+    case REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE:
+    case REFERRER_POLICY_UNSAFE_URL:
+      if (userReferrerTrimmingPolicy != 2) {
+        // Ignore set userReferrerTrimmingPolicy if it is already the strictest
+        // policy. Apply the user cross-origin trimming policy if it's more
+        // restrictive than the general one.
+        if (userReferrerXOriginTrimmingPolicy != 0 && IsCrossOriginWithReferrer()) {
+          userReferrerTrimmingPolicy =
+            std::max(userReferrerTrimmingPolicy, userReferrerXOriginTrimmingPolicy);
         }
       }
-      nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
-      rv = ssm->CheckSameOriginURI(triggeringURI, mURI, false);
-      isCrossOrigin.emplace(NS_FAILED(rv));
-    } else {
-      LOG(("no triggering principal available via loadInfo, assuming load is cross-origin"));
-      isCrossOrigin.emplace(true);
-    }
-  }
-
-  // Don't send referrer when the request is cross-origin and policy is "same-origin".
-  if (mReferrerPolicy == REFERRER_POLICY_SAME_ORIGIN && *isCrossOrigin) {
-    return NS_OK;
+
+      break;
+
+    case REFERRER_POLICY_NO_REFERRER:
+    case REFERRER_POLICY_UNSET:
+    default:
+      MOZ_ASSERT_UNREACHABLE("Unexpected value");
+      break;
   }
 
   nsAutoCString spec;
-
-  // Apply the user cross-origin trimming policy if it's more
-  // restrictive than the general one.
-  int userReferrerXOriginTrimmingPolicy =
-    gHttpHandler->ReferrerXOriginTrimmingPolicy();
-  if (userReferrerXOriginTrimmingPolicy != 0 && *isCrossOrigin) {
-    userReferrerTrimmingPolicy =
-      std::max(userReferrerTrimmingPolicy, userReferrerXOriginTrimmingPolicy);
-  }
-
-  // site-specified referrer trimming may affect the trim level
-  // "unsafe-url" behaves like "origin" (send referrer in the same situations) but
-  // "unsafe-url" sends the whole referrer and origin removes the path.
-  // "origin-when-cross-origin" trims the referrer only when the request is
-  // cross-origin.
-  // "Strict" request from https->http case was bailed out, so here:
-  // "strict-origin" behaves the same as "origin".
-  // "strict-origin-when-cross-origin" behaves the same as "origin-when-cross-origin"
-  if (mReferrerPolicy == REFERRER_POLICY_ORIGIN ||
-      mReferrerPolicy == REFERRER_POLICY_STRICT_ORIGIN ||
-      ((mReferrerPolicy == REFERRER_POLICY_ORIGIN_WHEN_XORIGIN ||
-        mReferrerPolicy == REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN) &&
-        *isCrossOrigin)) {
-    // We can override the user trimming preference because "origin"
-    // (network.http.referer.trimmingPolicy = 2) is the strictest
-    // trimming policy that users can specify.
-    userReferrerTrimmingPolicy = 2;
-  }
-
   // check how much referer to send
   if (userReferrerTrimmingPolicy) {
     // All output strings start with: scheme+host+port
     // We want the IDN-normalized PrePath.  That's not something currently
     // available and there doesn't yet seem to be justification for adding it to
     // the interfaces, so just build it up ourselves from scheme+AsciiHostPort
     nsAutoCString scheme, asciiHostPort;
     rv = clone->GetScheme(scheme);
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -493,16 +493,18 @@ protected:
   nsCOMPtr<nsISupports> mListenerContext;
   // An instance of nsHTTPCompressConv
   nsCOMPtr<nsIStreamListener> mCompressListener;
 
 private:
   // Proxy release all members above on main thread.
   void ReleaseMainThreadOnlyReferences();
 
+  bool IsCrossOriginWithReferrer();
+
 protected:
   // Use Release-Acquire ordering to ensure the OMT ODA is ignored while channel
   // is canceled on main thread.
   Atomic<bool, ReleaseAcquire> mCanceled;
 
   nsTArray<Pair<nsString, nsString>> mSecurityConsoleMessages;
 
   nsCOMPtr<nsISupports>             mOwner;
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_referrer_cross_origin.js
@@ -0,0 +1,310 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+function test_policy(test) {
+  do_print("Running test: " + test.toSource());
+
+  let prefs = Services.prefs;
+
+  if (test.trimmingPolicy !== undefined) {
+    prefs.setIntPref("network.http.referer.trimmingPolicy",
+                     test.trimmingPolicy);
+  } else {
+    prefs.setIntPref("network.http.referer.trimmingPolicy", 0);
+  }
+
+  if (test.XOriginTrimmingPolicy !== undefined) {
+    prefs.setIntPref("network.http.referer.XOriginTrimmingPolicy",
+                     test.XOriginTrimmingPolicy);
+  } else {
+    prefs.setIntPref("network.http.referer.XOriginTrimmingPolicy", 0);
+  }
+
+  let referrer = NetUtil.newURI(test.referrer);
+  let triggeringPrincipal = Services.scriptSecurityManager.createCodebasePrincipal(referrer, {});
+  let chan = NetUtil.newChannel({
+    uri: test.url,
+    loadingPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
+    triggeringPrincipal: triggeringPrincipal,
+    contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER
+  });
+
+  chan.QueryInterface(Components.interfaces.nsIHttpChannel);
+  chan.setReferrerWithPolicy(referrer, test.policy);
+  if (test.expectedReferrerSpec === undefined) {
+    try {
+      chan.getRequestHeader("Referer");
+      do_throw("Should not find a Referer header!");
+    } catch(e) {
+    }
+    do_check_eq(chan.referrer, null);
+  } else {
+    let header = chan.getRequestHeader("Referer");
+    do_check_eq(header, test.expectedReferrerSpec);
+    do_check_eq(chan.referrer.asciiSpec, test.expectedReferrerSpec);
+  }
+}
+
+const nsIHttpChannel = Ci.nsIHttpChannel;
+var gTests = [
+  // Test same origin policy w/o cross origin
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_SAME_ORIGIN,
+    url: "https://test.example/foo?a",
+    referrer: "https://test.example/foo?a",
+    expectedReferrerSpec: "https://test.example/foo?a"
+  },
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_SAME_ORIGIN,
+    url: "https://test.example/foo?a",
+    referrer: "https://foo.example/foo?a",
+    expectedReferrerSpec: undefined
+  },
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_SAME_ORIGIN,
+    trimmingPolicy: 1,
+    url: "https://test.example/foo?a",
+    referrer: "https://test.example/foo?a",
+    expectedReferrerSpec: "https://test.example/foo"
+  },
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_SAME_ORIGIN,
+    trimmingPolicy: 1,
+    url: "https://test.example/foo?a",
+    referrer: "https://foo.example/foo?a",
+    expectedReferrerSpec: undefined
+  },
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_SAME_ORIGIN,
+    trimmingPolicy: 2,
+    url: "https://test.example/foo?a",
+    referrer: "https://test.example/foo?a",
+    expectedReferrerSpec: "https://test.example/"
+  },
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_SAME_ORIGIN,
+    trimmingPolicy: 2,
+    url: "https://test.example/foo?a",
+    referrer: "https://foo.example/foo?a",
+    expectedReferrerSpec: undefined
+  },
+
+  // Test origin when xorigin policy w/o cross origin
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_ORIGIN_WHEN_XORIGIN,
+    url: "https://test.example/foo?a",
+    referrer: "https://test.example/foo?a",
+    expectedReferrerSpec: "https://test.example/foo?a"
+  },
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_ORIGIN_WHEN_XORIGIN,
+    url: "https://test.example/foo?a",
+    referrer: "https://foo.example/foo?a",
+    expectedReferrerSpec: "https://foo.example/"
+  },
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_ORIGIN_WHEN_XORIGIN,
+    trimmingPolicy: 1,
+    url: "https://test.example/foo?a",
+    referrer: "https://test.example/foo?a",
+    expectedReferrerSpec: "https://test.example/foo"
+  },
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_ORIGIN_WHEN_XORIGIN,
+    trimmingPolicy: 1,
+    url: "https://test.example/foo?a",
+    referrer: "https://foo.example/foo?a",
+    expectedReferrerSpec: "https://foo.example/"
+  },
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_ORIGIN_WHEN_XORIGIN,
+    trimmingPolicy: 2,
+    url: "https://test.example/foo?a",
+    referrer: "https://test.example/foo?a",
+    expectedReferrerSpec: "https://test.example/"
+  },
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_ORIGIN_WHEN_XORIGIN,
+    trimmingPolicy: 2,
+    url: "https://test.example/foo?a",
+    referrer: "https://foo.example/foo?a",
+    expectedReferrerSpec: "https://foo.example/"
+  },
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_ORIGIN_WHEN_XORIGIN,
+    XOriginTrimmingPolicy: 1,
+    url: "https://test.example/foo?a",
+    referrer: "https://test.example/foo?a",
+    expectedReferrerSpec: "https://test.example/foo?a"
+  },
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_ORIGIN_WHEN_XORIGIN,
+    XOriginTrimmingPolicy: 1,
+    url: "https://test.example/foo?a",
+    referrer: "https://foo.example/foo?a",
+    expectedReferrerSpec: "https://foo.example/"
+  },
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_ORIGIN_WHEN_XORIGIN,
+    XOriginTrimmingPolicy: 2,
+    url: "https://test.example/foo?a",
+    referrer: "https://test.example/foo?a",
+    expectedReferrerSpec: "https://test.example/foo?a"
+  },
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_ORIGIN_WHEN_XORIGIN,
+    XOriginTrimmingPolicy: 2,
+    url: "https://test.example/foo?a",
+    referrer: "https://foo.example/foo?a",
+    expectedReferrerSpec: "https://foo.example/"
+  },
+
+  // Test strict origin when xorigin policy w/o cross origin
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN,
+    url: "https://test.example/foo?a",
+    referrer: "https://test.example/foo?a",
+    expectedReferrerSpec: "https://test.example/foo?a"
+  },
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN,
+    url: "https://test.example/foo?a",
+    referrer: "https://foo.example/foo?a",
+    expectedReferrerSpec: "https://foo.example/"
+  },
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN,
+    url: "http://test.example/foo?a",
+    referrer: "https://foo.example/foo?a",
+    expectedReferrerSpec: undefined
+  },
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN,
+    trimmingPolicy: 1,
+    url: "https://test.example/foo?a",
+    referrer: "https://test.example/foo?a",
+    expectedReferrerSpec: "https://test.example/foo"
+  },
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN,
+    trimmingPolicy: 1,
+    url: "https://test.example/foo?a",
+    referrer: "https://foo.example/foo?a",
+    expectedReferrerSpec: "https://foo.example/"
+  },
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN,
+    trimmingPolicy: 1,
+    url: "http://test.example/foo?a",
+    referrer: "https://foo.example/foo?a",
+    expectedReferrerSpec: undefined
+  },
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN,
+    trimmingPolicy: 2,
+    url: "https://test.example/foo?a",
+    referrer: "https://test.example/foo?a",
+    expectedReferrerSpec: "https://test.example/"
+  },
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN,
+    trimmingPolicy: 2,
+    url: "https://test.example/foo?a",
+    referrer: "https://foo.example/foo?a",
+    expectedReferrerSpec: "https://foo.example/"
+  },
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN,
+    trimmingPolicy: 2,
+    url: "http://test.example/foo?a",
+    referrer: "https://foo.example/foo?a",
+    expectedReferrerSpec: undefined
+  },
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN,
+    XOriginTrimmingPolicy: 1,
+    url: "https://test.example/foo?a",
+    referrer: "https://test.example/foo?a",
+    expectedReferrerSpec: "https://test.example/foo?a"
+  },
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN,
+    XOriginTrimmingPolicy: 1,
+    url: "https://test.example/foo?a",
+    referrer: "https://foo.example/foo?a",
+    expectedReferrerSpec: "https://foo.example/"
+  },
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN,
+    XOriginTrimmingPolicy: 1,
+    url: "http://test.example/foo?a",
+    referrer: "https://foo.example/foo?a",
+    expectedReferrerSpec: undefined
+  },
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN,
+    XOriginTrimmingPolicy: 2,
+    url: "https://test.example/foo?a",
+    referrer: "https://test.example/foo?a",
+    expectedReferrerSpec: "https://test.example/foo?a"
+  },
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN,
+    XOriginTrimmingPolicy: 2,
+    url: "https://test.example/foo?a",
+    referrer: "https://foo.example/foo?a",
+    expectedReferrerSpec: "https://foo.example/"
+  },
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN,
+    XOriginTrimmingPolicy: 2,
+    url: "http://test.example/foo?a",
+    referrer: "https://foo.example/foo?a",
+    expectedReferrerSpec: undefined
+  },
+
+  // Test mix and choose max of XOriginTrimmingPolicy and trimmingPolicy
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_UNSAFE_URL,
+    XOriginTrimmingPolicy: 2,
+    trimmingPolicy: 1,
+    url: "https://test.example/foo?a",
+    referrer: "https://test1.example/foo?a",
+    expectedReferrerSpec: "https://test1.example/"
+  },
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_UNSAFE_URL,
+    XOriginTrimmingPolicy: 2,
+    trimmingPolicy: 1,
+    url: "https://test.example/foo?a",
+    referrer: "https://test.example/foo?a",
+    expectedReferrerSpec: "https://test.example/foo"
+  },
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_UNSAFE_URL,
+    XOriginTrimmingPolicy: 1,
+    trimmingPolicy: 2,
+    url: "https://test.example/foo?a",
+    referrer: "https://test.example/foo?a",
+    expectedReferrerSpec: "https://test.example/"
+  },
+  {
+    policy: nsIHttpChannel.REFERRER_POLICY_UNSAFE_URL,
+    XOriginTrimmingPolicy: 1,
+    trimmingPolicy: 0,
+    url: "https://test.example/foo?a",
+    referrer: "https://test1.example/foo?a",
+    expectedReferrerSpec: "https://test1.example/foo"
+  },
+
+];
+
+function run_test() {
+  gTests.forEach(test => test_policy(test));
+  Services.prefs.clearUserPref("network.http.referer.trimmingPolicy");
+  Services.prefs.clearUserPref("network.http.referer.XOriginTrimmingPolicy");
+}
--- a/netwerk/test/unit/xpcshell.ini
+++ b/netwerk/test/unit/xpcshell.ini
@@ -333,16 +333,17 @@ run-sequentially = Hardcoded hash value 
 [test_unix_domain.js]
 # The xpcshell temp directory on Android doesn't seem to let us create
 # Unix domain sockets. (Perhaps it's a FAT filesystem?)
 skip-if = os == "android"
 [test_addr_in_use_error.js]
 [test_about_networking.js]
 [test_ping_aboutnetworking.js]
 [test_referrer.js]
+[test_referrer_cross_origin.js]
 [test_referrer_policy.js]
 [test_predictor.js]
 # Android version detection w/in gecko does not work right on infra, so we just
 # disable this test on all android versions, even though it's enabled on 2.3+ in
 # the wild.
 skip-if = os == "android"
 [test_signature_extraction.js]
 skip-if = os != "win"