Merge mozilla-central to inbound. a=merge CLOSED TREE
authorGurzau Raul <rgurzau@mozilla.com>
Fri, 03 May 2019 01:19:30 +0300
changeset 531206 0c983ed54af0730f3360b76156b8e0fa41cc4ebc
parent 531205 f9d1ae270d58849485b95fb8d246196a18f76b30 (current diff)
parent 531119 bdf36faf780ba706bbebc8cbcd32ba892a899440 (diff)
child 531207 6159d01c9341088edcabb318259c8ce6201c61ac
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone68.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
Merge mozilla-central to inbound. a=merge CLOSED TREE
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -5750,22 +5750,22 @@ nsBrowserAccess.prototype = {
       else
         aWhere = Services.prefs.getIntPref("browser.link.open_newwindow");
     }
 
     let referrerInfo;
     if (aFlags & Ci.nsIBrowserDOMWindow.OPEN_NO_REFERRER) {
       referrerInfo = new ReferrerInfo(Ci.nsIHttpChannel.REFERRER_POLICY_UNSET, false, null);
     } else {
-      referrerInfo = new ReferrerInfo(Ci.nsIHttpChannel.REFERRER_POLICY_UNSET, true,
+      referrerInfo = new ReferrerInfo((aOpener && aOpener.document) ?
+        aOpener.document.referrerPolicy : Ci.nsIHttpChannel.REFERRER_POLICY_UNSET,
+        true,
         aOpener ? makeURI(aOpener.location.href) : null);
     }
-    if (aOpener && aOpener.document) {
-      referrerInfo.referrerPolicy = aOpener.document.referrerPolicy;
-    }
+
     let isPrivate = aOpener
                   ? PrivateBrowsingUtils.isContentWindowPrivate(aOpener)
                   : PrivateBrowsingUtils.isWindowPrivate(window);
 
     switch (aWhere) {
       case Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW :
         // FIXME: Bug 408379. So how come this doesn't send the
         // referrer like the other loads do?
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -15,16 +15,21 @@ XPCOMUtils.defineLazyModuleGetters(this,
   LoginHelper: "resource://gre/modules/LoginHelper.jsm",
   LoginManagerContextMenu: "resource://gre/modules/LoginManagerContextMenu.jsm",
   WebNavigationFrames: "resource://gre/modules/WebNavigationFrames.jsm",
   ContextualIdentityService: "resource://gre/modules/ContextualIdentityService.jsm",
   DevToolsShim: "chrome://devtools-startup/content/DevToolsShim.jsm",
   NetUtil: "resource://gre/modules/NetUtil.jsm",
 });
 
+XPCOMUtils.defineLazyGetter(this, "ReferrerInfo", () =>
+  Components.Constructor("@mozilla.org/referrer-info;1",
+                         "nsIReferrerInfo",
+                         "init"));
+
 var gContextMenuContentData = null;
 
 function setContextMenuContentData(data) {
   gContextMenuContentData = data;
 }
 
 function openContextMenu(aMessage) {
   let data = aMessage.data;
@@ -40,19 +45,16 @@ function openContextMenu(aMessage) {
 
   if (spellInfo) {
     spellInfo.target = aMessage.target.messageManager;
   }
 
   let documentURIObject = makeURI(data.docLocation,
                                   data.charSet,
                                   makeURI(data.baseURI));
-  let ReferrerInfo = Components.Constructor("@mozilla.org/referrer-info;1",
-                                        "nsIReferrerInfo",
-                                        "init");
   let referrerInfo = new ReferrerInfo(
     data.referrerPolicy,
     !data.context.linkHasNoReferrer,
     documentURIObject);
   let frameReferrerInfo = new ReferrerInfo(
     data.referrerPolicy,
     !data.context.linkHasNoReferrer,
     data.referrer ? makeURI(data.referrer) : null);
@@ -807,17 +809,18 @@ nsContextMenu.prototype = {
     }
 
     let referrerInfo = gContextMenuContentData.referrerInfo;
     // If we want to change userContextId, we must be sure that we don't
     // propagate the referrer.
     if (("userContextId" in params &&
         params.userContextId != gContextMenuContentData.userContextId) ||
       this.onPlainTextLink) {
-      referrerInfo.sendReferrer = false;
+      referrerInfo = new ReferrerInfo(referrerInfo.referrerPolicy, false,
+        referrerInfo.originalReferrer);
     }
 
     params.referrerInfo = referrerInfo;
     return params;
   },
 
   // Open linked-to URL in a new window.
   openLink() {
@@ -1233,17 +1236,22 @@ nsContextMenu.prototype = {
       flags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
 
     if (channel instanceof Ci.nsICachingChannel)
       flags |= Ci.nsICachingChannel.LOAD_BYPASS_LOCAL_CACHE_IF_BUSY;
 
     channel.loadFlags |= flags;
 
     if (channel instanceof Ci.nsIHttpChannel) {
-      channel.referrer = docURI;
+      let referrerInfo = new ReferrerInfo(
+        Ci.nsIHttpChannel.REFERRER_POLICY_UNSET,
+        true,
+        docURI);
+
+      channel.referrerInfo = referrerInfo;
       if (channel instanceof Ci.nsIHttpChannelInternal)
         channel.forceAllowThirdPartyCookie = true;
     }
 
     // fallback to the old way if we don't see the headers quickly
     var timeToWait =
       Services.prefs.getIntPref("browser.download.saveLinkAsFilenameTimeout");
     var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -15,16 +15,21 @@ XPCOMUtils.defineLazyModuleGetters(this,
   PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
   ShellService: "resource:///modules/ShellService.jsm",
 });
 
 XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
                                    "@mozilla.org/browser/aboutnewtab-service;1",
                                    "nsIAboutNewTabService");
 
+XPCOMUtils.defineLazyGetter(this, "ReferrerInfo", () =>
+  Components.Constructor("@mozilla.org/referrer-info;1",
+                         "nsIReferrerInfo",
+                         "init"));
+
 Object.defineProperty(this, "BROWSER_NEW_TAB_URL", {
   enumerable: true,
   get() {
     if (PrivateBrowsingUtils.isWindowPrivate(window)) {
       if (!PrivateBrowsingUtils.permanentPrivateBrowsing &&
           !aboutNewTabService.overridden) {
         return "about:privatebrowsing";
       }
@@ -290,19 +295,16 @@ function openUILinkIn(url, where, aAllow
 
   openLinkIn(url, where, params);
 }
 
 /* eslint-disable complexity */
 function openLinkIn(url, where, params) {
   if (!where || !url)
     return;
-  let ReferrerInfo = Components.Constructor("@mozilla.org/referrer-info;1",
-                                            "nsIReferrerInfo",
-                                            "init");
 
   var aFromChrome           = params.fromChrome;
   var aAllowThirdPartyFixup = params.allowThirdPartyFixup;
   var aPostData             = params.postData;
   var aCharset              = params.charset;
   var aReferrerInfo       = params.referrerInfo ? params.referrerInfo
     : new ReferrerInfo(Ci.nsIHttpChannel.REFERRER_POLICY_UNSET, true, null);
   var aRelatedToCurrent     = params.relatedToCurrent;
@@ -379,17 +381,18 @@ function openLinkIn(url, where, params) 
   aTriggeringPrincipal = useOAForPrincipal(aTriggeringPrincipal);
 
   if (!w || where == "window") {
     let features = "chrome,dialog=no,all";
     if (aIsPrivate) {
       features += ",private";
       // To prevent regular browsing data from leaking to private browsing sites,
       // strip the referrer when opening a new private window. (See Bug: 1409226)
-      aReferrerInfo.sendReferrer = false;
+      aReferrerInfo = new ReferrerInfo(aReferrerInfo.referrerPolicy, false,
+        aReferrerInfo.originalReferrer);
     }
 
     // This propagates to window.arguments.
     var sa = Cc["@mozilla.org/array;1"].
              createInstance(Ci.nsIMutableArray);
 
     var wuri = Cc["@mozilla.org/supports-string;1"].
                createInstance(Ci.nsISupportsString);
--- a/browser/components/urlbar/UrlbarView.jsm
+++ b/browser/components/urlbar/UrlbarView.jsm
@@ -431,16 +431,17 @@ class UrlbarView {
     let tagsContainer = this._createElement("span");
     tagsContainer.className = "urlbarView-tags";
     content.appendChild(tagsContainer);
     item._elements.set("tagsContainer", tagsContainer);
 
     let titleSeparator = this._createElement("span");
     titleSeparator.className = "urlbarView-title-separator";
     content.appendChild(titleSeparator);
+    item._elements.set("titleSeparator", titleSeparator);
 
     let action = this._createElement("span");
     action.className = "urlbarView-secondary urlbarView-action";
     content.appendChild(action);
     item._elements.set("action", action);
 
     let url = this._createElement("span");
     url.className = "urlbarView-secondary urlbarView-url";
@@ -527,16 +528,17 @@ class UrlbarView {
     let url = item._elements.get("url");
     if (setURL) {
       this._addTextContentWithHighlights(url, result.payload.displayUrl,
                                          result.payloadHighlights.displayUrl || []);
     } else {
       url.textContent = "";
     }
     item._elements.get("action").textContent = action;
+    item._elements.get("titleSeparator").hidden = !action && !setURL;
   }
 
   _removeStaleRows() {
     let row = this._rows.lastElementChild;
     while (row) {
       let next = row.previousElementSibling;
       if (row.hasAttribute("stale")) {
         row.remove();
--- a/browser/components/urlbar/tests/browser/browser.ini
+++ b/browser/components/urlbar/tests/browser/browser.ini
@@ -170,8 +170,9 @@ support-files =
   searchSuggestionEngineSlow.xml
   searchSuggestionEngine.sjs
 [browser_urlbarTokenAlias.js]
 [browser_urlbarUpdateForDomainCompletion.js]
 [browser_urlbarValueOnTabSwitch.js]
 [browser_userTypedValue.js]
 support-files = file_userTypedValue.html
 [browser_view_resultDisplay.js]
+[browser_view_resultTypes_display.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/urlbar/tests/browser/browser_view_resultTypes_display.js
@@ -0,0 +1,289 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const {SyncedTabs} = ChromeUtils.import("resource://services-sync/SyncedTabs.jsm");
+
+const TEST_ENGINE_BASENAME = "searchSuggestionEngine.xml";
+
+function assertElementsDisplayed(details, expected) {
+  Assert.equal(details.type, expected.type,
+    "Should be displaying a row of the correct type");
+  Assert.equal(details.title, expected.title,
+    "Should be displaying the correct title");
+  if (expected.separator) {
+    Assert.notEqual(window.getComputedStyle(details.element.separator).display,
+                    "none", "Should be displaying a separator");
+  } else {
+    Assert.equal(window.getComputedStyle(details.element.separator).display,
+                 "none", "Should not be displaying a separator");
+  }
+}
+
+add_task(async function setup() {
+  await SpecialPowers.pushPrefEnv({set: [
+    ["browser.urlbar.suggest.searches", false],
+    // Disable search suggestions in the urlbar.
+    ["browser.urlbar.suggest.searches", false],
+    // Clear historical search suggestions to avoid interference from previous
+    // tests.
+    ["browser.urlbar.maxHistoricalSearchSuggestions", 0],
+    // Use the default matching bucket configuration.
+    ["browser.urlbar.matchBuckets", "general:5,suggestion:4"],
+    // Turn autofill off.
+    ["browser.urlbar.autoFill", false],
+    // Special prefs for remote tabs.
+    ["services.sync.username", "fake"],
+    ["services.sync.syncedTabs.showRemoteTabs", true],
+  ]});
+
+  let engine = await SearchTestUtils.promiseNewSearchEngine(
+    getRootDirectory(gTestPath) + TEST_ENGINE_BASENAME);
+  let oldDefaultEngine = await Services.search.getDefault();
+  await Services.search.setDefault(engine);
+
+  registerCleanupFunction(async () => {
+    await Services.search.setDefault(oldDefaultEngine);
+  });
+});
+
+add_task(async function test_tab_switch_result() {
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla");
+
+  await BrowserTestUtils.withNewTab({gBrowser}, async () => {
+    await UrlbarTestUtils.promiseAutocompleteResultPopup({
+      window,
+      waitForFocus,
+      value: "about:mozilla",
+      fireInputEvent: true,
+    });
+
+    const details = await UrlbarTestUtils.getDetailsOfResultAt(window, 1);
+
+    assertElementsDisplayed(details, {
+      separator: true,
+      title: "about:mozilla",
+      type: UrlbarUtils.RESULT_TYPE.TAB_SWITCH,
+    });
+  });
+
+  BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function test_search_result() {
+  Services.prefs.setBoolPref("browser.urlbar.suggest.searches", true);
+
+  await BrowserTestUtils.withNewTab({gBrowser}, async () => {
+    await UrlbarTestUtils.promiseAutocompleteResultPopup({
+      window,
+      waitForFocus,
+      value: "foo",
+      fireInputEvent: true,
+    });
+
+    const details = await UrlbarTestUtils.getDetailsOfResultAt(window, 1);
+
+    // We'll initially display no separator.
+    assertElementsDisplayed(details, {
+      separator: false,
+      title: "foofoo",
+      type: UrlbarUtils.RESULT_TYPE.SEARCH,
+    });
+
+    EventUtils.sendKey("down");
+
+    // We should now be displaying one.
+    assertElementsDisplayed(details, {
+      separator: true,
+      title: "foofoo",
+      type: UrlbarUtils.RESULT_TYPE.SEARCH,
+    });
+  });
+
+  await PlacesUtils.history.clear();
+  Services.prefs.setBoolPref("browser.urlbar.suggest.searches", false);
+});
+
+add_task(async function test_url_result() {
+  await PlacesTestUtils.addVisits([{
+    uri: "http://example.com",
+    title: "example",
+    transition: Ci.nsINavHistoryService.TRANSITION_TYPED,
+  }]);
+
+  await BrowserTestUtils.withNewTab({gBrowser}, async () => {
+    await UrlbarTestUtils.promiseAutocompleteResultPopup({
+      window,
+      waitForFocus,
+      value: "example",
+      fireInputEvent: true,
+    });
+
+    const details = await UrlbarTestUtils.getDetailsOfResultAt(window, 1);
+
+    assertElementsDisplayed(details, {
+      separator: true,
+      title: "example",
+      type: UrlbarUtils.RESULT_TYPE.URL,
+    });
+  });
+
+  await PlacesUtils.history.clear();
+});
+
+add_task(async function test_keyword_result() {
+  const TEST_URL = `${TEST_BASE_URL}print_postdata.sjs`;
+
+  await PlacesUtils.keywords.insert({
+    keyword: "get",
+    url: TEST_URL + "?q=%s",
+  });
+
+  await BrowserTestUtils.withNewTab({gBrowser}, async () => {
+    await UrlbarTestUtils.promiseAutocompleteResultPopup({
+      window,
+      waitForFocus,
+      value: "get ",
+      fireInputEvent: true,
+    });
+
+    let details = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
+
+    assertElementsDisplayed(details, {
+      separator: true,
+      title: "example.com",
+      type: UrlbarUtils.RESULT_TYPE.KEYWORD,
+    });
+
+    await UrlbarTestUtils.promiseAutocompleteResultPopup({
+      window,
+      waitForFocus,
+      value: "get test",
+      fireInputEvent: true,
+    });
+
+    details = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
+
+    assertElementsDisplayed(details, {
+      separator: false,
+      title: "example.com: test",
+      type: UrlbarUtils.RESULT_TYPE.KEYWORD,
+    });
+  });
+});
+
+add_task(async function test_omnibox_result() {
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "omnibox": {
+        "keyword": "omniboxtest",
+      },
+
+      background() {
+        /* global browser */
+        browser.omnibox.setDefaultSuggestion({
+          description: "doit",
+        });
+        // Just do nothing for this test.
+        browser.omnibox.onInputEntered.addListener(() => {
+        });
+        browser.omnibox.onInputChanged.addListener((text, suggest) => {
+          suggest([]);
+        });
+      },
+    },
+  });
+
+  await extension.startup();
+
+  await BrowserTestUtils.withNewTab({gBrowser}, async () => {
+    await UrlbarTestUtils.promiseAutocompleteResultPopup({
+      window,
+      waitForFocus,
+      value: "omniboxtest ",
+      fireInputEvent: true,
+    });
+
+    const details = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
+
+    assertElementsDisplayed(details, {
+      separator: true,
+      title: "Generated extension",
+      type: UrlbarUtils.RESULT_TYPE.OMNIBOX,
+    });
+  });
+
+  await extension.unload();
+});
+
+add_task(async function test_remote_tab_result() {
+  // Clear history so that history added by previous tests doesn't mess up this
+  // test when it selects results in the urlbar.
+  await PlacesUtils.history.clear();
+  await PlacesUtils.bookmarks.eraseEverything();
+
+  const REMOTE_TAB = {
+    "id": "7cqCr77ptzX3",
+    "type": "client",
+    "lastModified": 1492201200,
+    "name": "zcarter's Nightly on MacBook-Pro-25",
+    "clientType": "desktop",
+    "tabs": [
+      {
+        "type": "tab",
+        "title": "Test Remote",
+        "url": "http://example.com",
+        "icon": UrlbarUtils.ICON.DEFAULT,
+        "client": "7cqCr77ptzX3",
+        "lastUsed": 1452124677,
+      },
+    ],
+  };
+
+  const sandbox = sinon.createSandbox();
+
+  let originalSyncedTabsInternal = SyncedTabs._internal;
+  SyncedTabs._internal = {
+    isConfiguredToSyncTabs: true,
+    hasSyncedThisSession: true,
+    getTabClients() { return Promise.resolve([]); },
+    syncTabs() { return Promise.resolve(); },
+  };
+
+  // Tell the Sync XPCOM service it is initialized.
+  let weaveXPCService = Cc["@mozilla.org/weave/service;1"]
+                          .getService(Ci.nsISupports)
+                          .wrappedJSObject;
+  let oldWeaveServiceReady = weaveXPCService.ready;
+  weaveXPCService.ready = true;
+
+  sandbox.stub(SyncedTabs._internal, "getTabClients")
+         .callsFake(() => Promise.resolve(Cu.cloneInto([REMOTE_TAB], {})));
+
+  registerCleanupFunction(async function() {
+    sandbox.restore();
+    weaveXPCService.ready = oldWeaveServiceReady;
+    SyncedTabs._internal = originalSyncedTabsInternal;
+    await PlacesUtils.history.clear();
+    await PlacesUtils.bookmarks.eraseEverything();
+    Services.telemetry.setEventRecordingEnabled("navigation", false);
+  });
+
+  await BrowserTestUtils.withNewTab({gBrowser}, async () => {
+    await UrlbarTestUtils.promiseAutocompleteResultPopup({
+      window,
+      waitForFocus,
+      value: "example",
+      fireInputEvent: true,
+    });
+
+    const details = await UrlbarTestUtils.getDetailsOfResultAt(window, 1);
+
+    assertElementsDisplayed(details, {
+      separator: true,
+      title: "Test Remote",
+      type: UrlbarUtils.RESULT_TYPE.REMOTE_TAB,
+    });
+  });
+});
--- a/build/mozconfig.cache
+++ b/build/mozconfig.cache
@@ -9,37 +9,56 @@ if test -z "$bucket" -a -z "$SCCACHE_DIS
 
     # prevent rerun if az is set, or wget is not available
     if test -z "$availability_zone" -a -x "$(command -v wget)"; then
         if test -n "${TASKCLUSTER_WORKER_GROUP}"; then
             # TASKCLUSTER_WORKER_GROUP is just the region now, so
             # stick an extra character on to make the already-convoluted logic
             # here simpler.
             availability_zone="${TASKCLUSTER_WORKER_GROUP}x"
+        elif [ -n "${SCCACHE_GCS_KEY_PATH}" ]; then
+            # gcp availability_zone is of the form <region>-<letter> where region is e.g. us-west2, and az is us-west2-a
+            gcp_zone=$(wget -T 1 -t 1 -q -O - http://169.254.169.254/computeMetadata/v1beta1/instance/zone || true)
+            availability_zone=${gcp_zone##*/}
         else
             # timeout after 1 second, and don't retry (failure indicates instance is not in ec2 or network issue)
-            # availability_zone is of the form <region><letter> where region is e.g. us-west-2, and az is us-west-2a
+            # ec2 availability_zone is of the form <region><letter> where region is e.g. us-west-2, and az is us-west-2a
             availability_zone=$(wget -T 1 -t 1 -q -O - http://169.254.169.254/latest/meta-data/placement/availability-zone || true)
         fi
         if test -z "$availability_zone" -o "$availability_zone" = "not-ec2"; then
             availability_zone=not-ec2
+        elif [ -n "${SCCACHE_GCS_KEY_PATH}" ]; then
+            # gcp region is az with last two letters trimmed
+            region=${availability_zone::${#availability_zone}-2}
+            case "${GECKO_HEAD_REPOSITORY}" in
+            *hg.mozilla.org/try*)
+                bucket=taskcluster-level-1-sccache-${region}
+                ;;
+            *hg.mozilla.org/integration/autoland*|*hg.mozilla.org/integration/mozilla-inbound*)
+                bucket=taskcluster-level-3-sccache-${region}
+                ;;
+            esac
         else
-            # region is az with last letter trimmed
+            # ec2 region is az with last letter trimmed
             region=${availability_zone%?}
             # set S3 bucket according to tree (level)
             case "${GECKO_HEAD_REPOSITORY}" in
             *hg.mozilla.org/try*)
                 bucket=taskcluster-level-1-sccache-${region}
                 ;;
             *hg.mozilla.org/integration/autoland*|*hg.mozilla.org/integration/mozilla-inbound*)
                 bucket=taskcluster-level-3-sccache-${region}
                 ;;
             esac
         fi
     fi
 fi
 
 if test -n "$bucket"; then
-    mk_add_options "export SCCACHE_BUCKET=$bucket"
+    if [ -n "${SCCACHE_GCS_KEY_PATH}" ]; then
+        mk_add_options "export SCCACHE_GCS_BUCKET=$bucket"
+    else
+        mk_add_options "export SCCACHE_BUCKET=$bucket"
+    fi
     export CCACHE="$topsrcdir/sccache2/sccache"
     export SCCACHE_VERBOSE_STATS=1
     mk_add_options MOZBUILD_MANAGE_SCCACHE_DAEMON=${topsrcdir}/sccache2/sccache
 fi
--- a/devtools/server/actors/network-monitor/network-observer.js
+++ b/devtools/server/actors/network-monitor/network-observer.js
@@ -526,18 +526,20 @@ NetworkObserver.prototype = {
     event.private = httpActivity.private;
     event.headersSize = 0;
     event.startedDateTime =
       (timestamp ? new Date(Math.round(timestamp / 1000)) : new Date())
       .toISOString();
     event.fromCache = fromCache;
     event.fromServiceWorker = fromServiceWorker;
     event.isThirdPartyTrackingResource = channel.isThirdPartyTrackingResource();
+    const referrerInfo = channel.referrerInfo;
     event.referrerPolicy =
-      Services.netUtils.getReferrerPolicyString(channel.referrerPolicy);
+      Services.netUtils.getReferrerPolicyString(referrerInfo ?
+          referrerInfo.referrerPolicy : Ci.nsIHttpChannel.REFERRER_POLICY_UNSET);
     httpActivity.fromServiceWorker = fromServiceWorker;
 
     if (extraStringData) {
       event.headersSize = extraStringData.length;
     }
 
     // Determine the cause and if this is an XHR request.
     let causeType = Ci.nsIContentPolicy.TYPE_OTHER;
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -10076,21 +10076,19 @@ nsresult nsDocShell::DoURILoad(nsDocShel
     }
   }
 
   // hack
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
   nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(
       do_QueryInterface(channel));
   nsCOMPtr<nsIURI> referrer;
-  uint32_t referrerPolicy = RP_Unset;
   nsIReferrerInfo* referrerInfo = aLoadState->GetReferrerInfo();
   if (referrerInfo) {
     referrerInfo->GetOriginalReferrer(getter_AddRefs(referrer));
-    referrerInfo->GetReferrerPolicy(&referrerPolicy);
   }
   if (httpChannelInternal) {
     if (aLoadState->HasLoadFlags(INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES)) {
       rv = httpChannelInternal->SetThirdPartyFlags(
           nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW);
       MOZ_ASSERT(NS_SUCCEEDED(rv));
     }
     if (aLoadState->FirstParty()) {
@@ -10178,20 +10176,19 @@ nsresult nsDocShell::DoURILoad(nsDocShel
     }
   }
 
   if (httpChannel) {
     if (aLoadState->HeadersStream()) {
       rv = AddHeadersToChannel(aLoadState->HeadersStream(), httpChannel);
     }
     // Set the referrer explicitly
-    if (referrer &&
-        !(aLoadState->HasLoadFlags(INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER))) {
-      // Referrer is currenly only set for link clicks here.
-      rv = httpChannel->SetReferrerWithPolicy(referrer, referrerPolicy);
+    // Referrer is currenly only set for link clicks here.
+    if (referrerInfo) {
+      rv = httpChannel->SetReferrerInfo(referrerInfo);
       MOZ_ASSERT(NS_SUCCEEDED(rv));
     }
   }
 
   nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(channel);
   if (scriptChannel) {
     // Allow execution against our context if the principals match
     scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
@@ -10572,25 +10569,18 @@ nsresult nsDocShell::ScrollToAnchor(bool
   }
 
   return NS_OK;
 }
 
 void nsDocShell::SetupReferrerInfoFromChannel(nsIChannel* aChannel) {
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
   if (httpChannel) {
-    nsCOMPtr<nsIURI> referrer;
-    nsresult rv = httpChannel->GetReferrer(getter_AddRefs(referrer));
-    if (NS_SUCCEEDED(rv)) {
-      uint32_t referrerPolicy;
-      rv = httpChannel->GetReferrerPolicy(&referrerPolicy);
-      if (NS_SUCCEEDED(rv)) {
-        SetReferrerInfo(new ReferrerInfo(referrer, referrerPolicy));
-      }
-    }
+    nsCOMPtr<nsIReferrerInfo> referrerInfo = httpChannel->GetReferrerInfo();
+    SetReferrerInfo(referrerInfo);
   }
 }
 
 bool nsDocShell::OnNewURI(nsIURI* aURI, nsIChannel* aChannel,
                           nsIPrincipal* aTriggeringPrincipal,
                           nsIPrincipal* aPrincipalToInherit, uint32_t aLoadType,
                           nsIContentSecurityPolicy* aCsp,
                           bool aFireOnLocationChange, bool aAddToGlobalHistory,
@@ -11347,18 +11337,17 @@ nsresult nsDocShell::AddToSessionHistory
     }
   }
 
   // Get the post data & referrer
   nsCOMPtr<nsIInputStream> inputStream;
   nsCOMPtr<nsIURI> originalURI;
   nsCOMPtr<nsIURI> resultPrincipalURI;
   bool loadReplace = false;
-  nsCOMPtr<nsIURI> referrerURI;
-  uint32_t referrerPolicy = RP_Unset;
+  nsCOMPtr<nsIReferrerInfo> referrerInfo;
   uint32_t cacheKey = 0;
   nsCOMPtr<nsIPrincipal> triggeringPrincipal = aTriggeringPrincipal;
   nsCOMPtr<nsIPrincipal> principalToInherit = aPrincipalToInherit;
   nsCOMPtr<nsIContentSecurityPolicy> csp = aCsp;
   bool expired = false;
   bool discardLayoutState = false;
   nsCOMPtr<nsICacheInfoChannel> cacheChannel;
   if (aChannel) {
@@ -11380,19 +11369,17 @@ nsresult nsDocShell::AddToSessionHistory
       nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
       if (uploadChannel) {
         uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
       }
       httpChannel->GetOriginalURI(getter_AddRefs(originalURI));
       uint32_t loadFlags;
       aChannel->GetLoadFlags(&loadFlags);
       loadReplace = loadFlags & nsIChannel::LOAD_REPLACE;
-      rv = httpChannel->GetReferrer(getter_AddRefs(referrerURI));
-      MOZ_ASSERT(NS_SUCCEEDED(rv));
-      rv = httpChannel->GetReferrerPolicy(&referrerPolicy);
+      rv = httpChannel->GetReferrerInfo(getter_AddRefs(referrerInfo));
       MOZ_ASSERT(NS_SUCCEEDED(rv));
 
       discardLayoutState = ShouldDiscardLayoutState(httpChannel);
     }
 
     nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
     if (!triggeringPrincipal) {
       triggeringPrincipal = loadInfo->TriggeringPrincipal();
@@ -11434,17 +11421,17 @@ nsresult nsDocShell::AddToSessionHistory
                 cacheKey,             // CacheKey
                 mContentTypeHint,     // Content-type
                 triggeringPrincipal,  // Channel or provided principal
                 principalToInherit, csp, mHistoryID, mDynamicallyCreated);
 
   entry->SetOriginalURI(originalURI);
   entry->SetResultPrincipalURI(resultPrincipalURI);
   entry->SetLoadReplace(loadReplace);
-  entry->SetReferrerInfo(new ReferrerInfo(referrerURI, referrerPolicy));
+  entry->SetReferrerInfo(referrerInfo);
   nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(aChannel);
   if (inStrmChan) {
     bool isSrcdocChannel;
     inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
     if (isSrcdocChannel) {
       nsAutoString srcdoc;
       inStrmChan->GetSrcdocData(srcdoc);
       entry->SetSrcdocData(srcdoc);
@@ -12611,20 +12598,21 @@ nsDocShell::OnLinkClickSync(
     // CSP from the NodePrincipal(). After Bug 965637 we can fall back to
     // querying the CSP from the document (aContent->OwnerDoc()).
     aContent->NodePrincipal()->GetCsp(getter_AddRefs(csp));
   }
 
   uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
   if (IsElementAnchorOrArea(aContent)) {
     MOZ_ASSERT(aContent->IsHTMLElement());
-    nsAutoString referrer;
-    aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::rel, referrer);
+    nsAutoString relString;
+    aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::rel,
+                                   relString);
     nsWhitespaceTokenizerTemplate<nsContentUtils::IsHTMLWhitespace> tok(
-        referrer);
+        relString);
 
     bool targetBlank = aTargetSpec.LowerCaseEqualsLiteral("_blank");
     bool explicitOpenerSet = false;
 
     // The opener behaviour follows a hierarchy, such that if a higher
     // priority behaviour is specified, it always takes priority. That
     // priority is currently: norefrerer > noopener > opener > default
 
@@ -12660,41 +12648,41 @@ nsDocShell::OnLinkClickSync(
     }
   }
 
   // Get the owner document of the link that was clicked, this will be
   // the document that the link is in, or the last document that the
   // link was in. From that document, we'll get the URI to use as the
   // referrer, since the current URI in this docshell may be a
   // new document that we're in the process of loading.
-  RefPtr<Document> refererDoc = aContent->OwnerDoc();
-  NS_ENSURE_TRUE(refererDoc, NS_ERROR_UNEXPECTED);
-
-  // Now check that the refererDoc's inner window is the current inner
+  RefPtr<Document> referrerDoc = aContent->OwnerDoc();
+  NS_ENSURE_TRUE(referrerDoc, NS_ERROR_UNEXPECTED);
+
+  // Now check that the referrerDoc's inner window is the current inner
   // window for mScriptGlobal.  If it's not, then we don't want to
   // follow this link.
-  nsPIDOMWindowInner* refererInner = refererDoc->GetInnerWindow();
-  NS_ENSURE_TRUE(refererInner, NS_ERROR_UNEXPECTED);
+  nsPIDOMWindowInner* referrerInner = referrerDoc->GetInnerWindow();
+  NS_ENSURE_TRUE(referrerInner, NS_ERROR_UNEXPECTED);
   if (!mScriptGlobal ||
-      mScriptGlobal->GetCurrentInnerWindow() != refererInner) {
+      mScriptGlobal->GetCurrentInnerWindow() != referrerInner) {
     // We're no longer the current inner window
     return NS_OK;
   }
 
-  nsCOMPtr<nsIURI> referrer = refererDoc->GetDocumentURI();
-  uint32_t refererPolicy = refererDoc->GetReferrerPolicy();
+  nsCOMPtr<nsIURI> referrer = referrerDoc->GetDocumentURI();
+  uint32_t referrerPolicy = referrerDoc->GetReferrerPolicy();
 
   // get referrer attribute from clicked link and parse it
   // if per element referrer is enabled, the element referrer overrules
   // the document wide referrer
   if (IsElementAnchorOrArea(aContent)) {
     net::ReferrerPolicy refPolEnum =
         aContent->AsElement()->GetReferrerPolicyAsEnum();
     if (refPolEnum != RP_Unset) {
-      refererPolicy = refPolEnum;
+      referrerPolicy = refPolEnum;
     }
   }
 
   // referrer could be null here in some odd cases, but that's ok,
   // we'll just load the link w/o sending a referrer in those cases.
 
   // If this is an anchor element, grab its type property to use as a hint
   nsAutoString typeHint;
@@ -12714,17 +12702,17 @@ nsDocShell::OnLinkClickSync(
   uint32_t loadType = inOnLoadHandler ? LOAD_NORMAL_REPLACE : LOAD_LINK;
 
   if (aIsUserTriggered) {
     flags |= INTERNAL_LOAD_FLAGS_IS_USER_TRIGGERED;
   }
 
   bool sendReferrer = !(flags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER);
   nsCOMPtr<nsIReferrerInfo> referrerInfo =
-      new ReferrerInfo(referrer, refererPolicy, sendReferrer);
+      new ReferrerInfo(referrer, referrerPolicy, sendReferrer);
   RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aURI);
   loadState->SetReferrerInfo(referrerInfo);
   loadState->SetTriggeringPrincipal(triggeringPrincipal);
   loadState->SetPrincipalToInherit(aContent->NodePrincipal());
   loadState->SetCsp(csp);
   loadState->SetLoadFlags(flags);
   loadState->SetTarget(aTargetSpec);
   loadState->SetTypeHint(NS_ConvertUTF16toUTF8(typeHint));
@@ -12732,18 +12720,17 @@ nsDocShell::OnLinkClickSync(
   loadState->SetPostDataStream(aPostDataStream);
   loadState->SetHeadersStream(aHeadersDataStream);
   loadState->SetLoadType(loadType);
   loadState->SetFirstParty(true);
   loadState->SetSourceDocShell(this);
   nsresult rv = InternalLoad(loadState, aDocShell, aRequest);
 
   if (NS_SUCCEEDED(rv)) {
-    nsPingListener::DispatchPings(this, aContent, aURI, referrer,
-                                  refererPolicy);
+    nsPingListener::DispatchPings(this, aContent, aURI, referrerInfo);
   }
   return rv;
 }
 
 NS_IMETHODIMP
 nsDocShell::OnOverLink(nsIContent* aContent, nsIURI* aURI,
                        const nsAString& aTargetSpec) {
   if (aContent->IsEditable()) {
--- a/docshell/base/nsPingListener.cpp
+++ b/docshell/base/nsPingListener.cpp
@@ -74,19 +74,18 @@ static void OnPingTimeout(nsITimer* aTim
   }
 }
 
 struct MOZ_STACK_CLASS SendPingInfo {
   int32_t numPings;
   int32_t maxPings;
   bool requireSameHost;
   nsIURI* target;
-  nsIURI* referrer;
+  nsIReferrerInfo* referrerInfo;
   nsIDocShell* docShell;
-  uint32_t referrerPolicy;
 };
 
 static void SendPing(void* aClosure, nsIContent* aContent, nsIURI* aURI,
                      nsIIOService* aIOService) {
   SendPingInfo* info = static_cast<SendPingInfo*>(aClosure);
   if (info->maxPings > -1 && info->numPings >= info->maxPings) {
     return;
   }
@@ -146,52 +145,54 @@ static void SendPing(void* aClosure, nsI
     rv = httpChan->SetRequestHeader(NS_LITERAL_CSTRING("Ping-To"), pingTo,
                                     false);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
   }
 
   nsCOMPtr<nsIScriptSecurityManager> sm =
       do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
 
-  if (sm && info->referrer) {
-    bool referrerIsSecure;
+  if (sm && info->referrerInfo) {
+    nsCOMPtr<nsIURI> referrer = info->referrerInfo->GetOriginalReferrer();
+    bool referrerIsSecure = false;
     uint32_t flags = nsIProtocolHandler::URI_IS_POTENTIALLY_TRUSTWORTHY;
-    rv = NS_URIChainHasFlags(info->referrer, flags, &referrerIsSecure);
+    if (referrer) {
+      rv = NS_URIChainHasFlags(referrer, flags, &referrerIsSecure);
+    }
 
     // Default to sending less data if NS_URIChainHasFlags() fails.
     referrerIsSecure = NS_FAILED(rv) || referrerIsSecure;
 
     bool isPrivateWin = false;
     if (doc) {
       isPrivateWin =
           doc->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId > 0;
     }
 
     bool sameOrigin = NS_SUCCEEDED(
-        sm->CheckSameOriginURI(info->referrer, aURI, false, isPrivateWin));
+        sm->CheckSameOriginURI(referrer, aURI, false, isPrivateWin));
 
     // If both the address of the document containing the hyperlink being
     // audited and "ping URL" have the same origin or the document containing
     // the hyperlink being audited was not retrieved over an encrypted
     // connection, send a Ping-From header.
     if (sameOrigin || !referrerIsSecure) {
       nsAutoCString pingFrom;
-      if (NS_SUCCEEDED(info->referrer->GetSpec(pingFrom))) {
+      if (NS_SUCCEEDED(referrer->GetSpec(pingFrom))) {
         rv = httpChan->SetRequestHeader(NS_LITERAL_CSTRING("Ping-From"),
                                         pingFrom, false);
         MOZ_ASSERT(NS_SUCCEEDED(rv));
       }
     }
 
     // If the document containing the hyperlink being audited was not retrieved
     // over an encrypted connection and its address does not have the same
     // origin as "ping URL", send a referrer.
-    if (!sameOrigin && !referrerIsSecure) {
-      rv =
-          httpChan->SetReferrerWithPolicy(info->referrer, info->referrerPolicy);
+    if (!sameOrigin && !referrerIsSecure && info->referrerInfo) {
+      rv = httpChan->SetReferrerInfo(info->referrerInfo);
       MOZ_ASSERT(NS_SUCCEEDED(rv));
     }
   }
 
   nsCOMPtr<nsIUploadChannel2> uploadChan = do_QueryInterface(httpChan);
   if (!uploadChan) {
     return;
   }
@@ -282,33 +283,32 @@ static void ForEachPing(nsIContent* aCon
     // Explicitly not allow loading data: URIs
     if (!net::SchemeIsData(uri)) {
       aCallback(aClosure, aContent, uri, ios);
     }
   }
 }
 
 // Spec: http://whatwg.org/specs/web-apps/current-work/#ping
-/*static*/
-void nsPingListener::DispatchPings(nsIDocShell* aDocShell, nsIContent* aContent,
-                                   nsIURI* aTarget, nsIURI* aReferrer,
-                                   uint32_t aReferrerPolicy) {
+/*static*/ void nsPingListener::DispatchPings(nsIDocShell* aDocShell,
+                                              nsIContent* aContent,
+                                              nsIURI* aTarget,
+                                              nsIReferrerInfo* aReferrerInfo) {
   SendPingInfo info;
 
   if (!PingsEnabled(&info.maxPings, &info.requireSameHost)) {
     return;
   }
   if (info.maxPings == 0) {
     return;
   }
 
   info.numPings = 0;
   info.target = aTarget;
-  info.referrer = aReferrer;
-  info.referrerPolicy = aReferrerPolicy;
+  info.referrerInfo = aReferrerInfo;
   info.docShell = aDocShell;
 
   ForEachPing(aContent, SendPing, &info);
 }
 
 nsPingListener::~nsPingListener() {
   if (mTimer) {
     mTimer->Cancel();
--- a/docshell/base/nsPingListener.h
+++ b/docshell/base/nsPingListener.h
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsPingListener_h__
 #define nsPingListener_h__
 
 #include "nsIStreamListener.h"
-
+#include "nsIReferrerInfo.h"
 #include "nsCOMPtr.h"
 
 namespace mozilla {
 namespace dom {
 class DocGroup;
 }
 }  // namespace mozilla
 
@@ -31,18 +31,17 @@ class nsPingListener final : public nsIS
 
   nsPingListener() {}
 
   void SetLoadGroup(nsILoadGroup* aLoadGroup) { mLoadGroup = aLoadGroup; }
 
   nsresult StartTimeout(mozilla::dom::DocGroup* aDocGroup);
 
   static void DispatchPings(nsIDocShell* aDocShell, nsIContent* aContent,
-                            nsIURI* aTarget, nsIURI* aReferrer,
-                            uint32_t aReferrerPolicy);
+                            nsIURI* aTarget, nsIReferrerInfo* aReferrerInfo);
 
  private:
   ~nsPingListener();
 
   nsCOMPtr<nsILoadGroup> mLoadGroup;
   nsCOMPtr<nsITimer> mTimer;
 };
 
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -211,16 +211,17 @@
 #include "mozilla/dom/Comment.h"
 #include "nsTextNode.h"
 #include "mozilla/dom/Link.h"
 #include "mozilla/dom/HTMLCollectionBinding.h"
 #include "mozilla/dom/HTMLElementBinding.h"
 #include "nsXULAppAPI.h"
 #include "mozilla/dom/Touch.h"
 #include "mozilla/dom/TouchEvent.h"
+#include "ReferrerInfo.h"
 
 #include "mozilla/Preferences.h"
 
 #include "imgILoader.h"
 #include "imgRequestProxy.h"
 #include "nsWrapperCacheInlines.h"
 #include "nsSandboxFlags.h"
 #include "mozilla/dom/AnimatableBinding.h"
@@ -992,17 +993,19 @@ nsresult ExternalResourceMap::PendingLoa
                      nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS,
                      nsIContentPolicy::TYPE_OTHER,
                      nullptr,  // aPerformanceStorage
                      loadGroup);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
   if (httpChannel) {
-    rv = httpChannel->SetReferrerWithPolicy(aReferrer, aReferrerPolicy);
+    nsCOMPtr<nsIReferrerInfo> referrerInfo =
+        new ReferrerInfo(aReferrer, aReferrerPolicy);
+    rv = httpChannel->SetReferrerInfoWithoutClone(referrerInfo);
     Unused << NS_WARN_IF(NS_FAILED(rv));
   }
 
   mURI = aURI;
 
   return channel->AsyncOpen(this);
 }
 
--- a/dom/base/EventSource.cpp
+++ b/dom/base/EventSource.cpp
@@ -42,16 +42,17 @@
 #include "nsIContentSecurityPolicy.h"
 #include "nsContentUtils.h"
 #include "mozilla/Preferences.h"
 #include "xpcpublic.h"
 #include "nsWrapperCacheInlines.h"
 #include "mozilla/Attributes.h"
 #include "nsError.h"
 #include "mozilla/Encoding.h"
+#include "ReferrerInfo.h"
 
 namespace mozilla {
 namespace dom {
 
 static LazyLogModule gEventSourceLog("EventSource");
 
 #define SPACE_CHAR (char16_t)0x0020
 #define CR_CHAR (char16_t)0x000D
@@ -89,17 +90,17 @@ class EventSourceImpl final : public nsI
 
   void Close();
 
   void Init(nsIPrincipal* aPrincipal, const nsAString& aURL, ErrorResult& aRv);
 
   nsresult GetBaseURI(nsIURI** aBaseURI);
 
   void SetupHttpChannel();
-  nsresult SetupReferrerPolicy();
+  nsresult SetupReferrerInfo();
   nsresult InitChannelAndRequestEventSource();
   nsresult ResetConnection();
   void ResetDecoder();
   nsresult SetReconnectionTimeout();
 
   void AnnounceConnection();
   void DispatchAllMessageEvents();
   nsresult RestartConnection();
@@ -804,18 +805,18 @@ EventSourceImpl::AsyncOnChannelRedirect(
   }
 
   // update our channel
 
   mHttpChannel = do_QueryInterface(aNewChannel);
   NS_ENSURE_STATE(mHttpChannel);
 
   SetupHttpChannel();
-  // The HTTP impl already copies over the referrer and referrer policy on
-  // redirects, so we don't need to SetupReferrerPolicy().
+  // The HTTP impl already copies over the referrer info on
+  // redirects, so we don't need to SetupReferrerInfo().
 
   if ((aFlags & nsIChannelEventSink::REDIRECT_PERMANENT) != 0) {
     rv = NS_GetFinalChannelURI(mHttpChannel, getter_AddRefs(mSrc));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   aCallback->OnRedirectVerifyCallback(NS_OK);
 
@@ -924,23 +925,24 @@ void EventSourceImpl::SetupHttpChannel()
   if (NS_FAILED(rv)) {
     MOZ_LOG(gEventSourceLog, LogLevel::Warning,
             ("SetupHttpChannel. rv=%x (%s)", uint32_t(rv), eventId.get()));
   }
 #endif
   Unused << rv;
 }
 
-nsresult EventSourceImpl::SetupReferrerPolicy() {
+nsresult EventSourceImpl::SetupReferrerInfo() {
   AssertIsOnMainThread();
   MOZ_ASSERT(!IsShutDown());
   nsCOMPtr<Document> doc = mEventSource->GetDocumentIfCurrent();
   if (doc) {
-    nsresult rv = mHttpChannel->SetReferrerWithPolicy(doc->GetDocumentURI(),
-                                                      doc->GetReferrerPolicy());
+    nsCOMPtr<nsIReferrerInfo> referrerInfo =
+        new ReferrerInfo(doc->GetDocumentURI(), doc->GetReferrerPolicy());
+    nsresult rv = mHttpChannel->SetReferrerInfoWithoutClone(referrerInfo);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 nsresult EventSourceImpl::InitChannelAndRequestEventSource() {
   AssertIsOnMainThread();
@@ -996,17 +998,17 @@ nsresult EventSourceImpl::InitChannelAnd
   }
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   mHttpChannel = do_QueryInterface(channel);
   NS_ENSURE_TRUE(mHttpChannel, NS_ERROR_NO_INTERFACE);
 
   SetupHttpChannel();
-  rv = SetupReferrerPolicy();
+  rv = SetupReferrerInfo();
   NS_ENSURE_SUCCESS(rv, rv);
 
 #ifdef DEBUG
   {
     nsCOMPtr<nsIInterfaceRequestor> notificationCallbacks;
     mHttpChannel->GetNotificationCallbacks(
         getter_AddRefs(notificationCallbacks));
     MOZ_ASSERT(!notificationCallbacks);
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -68,16 +68,17 @@
 #include "nsICookieService.h"
 #include "nsIStringStream.h"
 #include "nsIHttpChannel.h"
 #include "nsIHttpChannelInternal.h"
 #include "nsStreamUtils.h"
 #include "WidgetUtils.h"
 #include "nsIPresentationService.h"
 #include "nsIScriptError.h"
+#include "ReferrerInfo.h"
 
 #include "nsIExternalProtocolHandler.h"
 #include "BrowserChild.h"
 #include "URIUtils.h"
 
 #include "mozilla/dom/MediaDevices.h"
 #include "MediaManager.h"
 
@@ -1146,18 +1147,20 @@ bool Navigator::SendBeaconInternal(const
   }
 
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
   if (!httpChannel) {
     // Beacon spec only supports HTTP requests at this time
     aRv.Throw(NS_ERROR_DOM_BAD_URI);
     return false;
   }
-  mozilla::net::ReferrerPolicy referrerPolicy = doc->GetReferrerPolicy();
-  rv = httpChannel->SetReferrerWithPolicy(documentURI, referrerPolicy);
+
+  nsCOMPtr<nsIReferrerInfo> referrerInfo =
+      new ReferrerInfo(doc->GetDocumentURI(), doc->GetReferrerPolicy());
+  rv = httpChannel->SetReferrerInfoWithoutClone(referrerInfo);
   MOZ_ASSERT(NS_SUCCEEDED(rv));
 
   nsCOMPtr<nsIInputStream> in;
   nsAutoCString contentTypeWithCharset;
   nsAutoCString charset;
   uint64_t length = 0;
 
   if (aBody) {
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -244,16 +244,17 @@
 #include "nsPluginHost.h"
 #include "nsIBrowser.h"
 #include "mozilla/HangAnnotations.h"
 #include "mozilla/Encoding.h"
 #include "nsXULElement.h"
 #include "mozilla/RecordReplay.h"
 #include "nsThreadManager.h"
 #include "nsIBidiKeyboard.h"
+#include "ReferrerInfo.h"
 
 #if defined(XP_WIN)
 // Undefine LoadImage to prevent naming conflict with Windows.
 #  undef LoadImage
 #endif
 
 extern "C" int MOZ_XMLTranslateEntity(const char* ptr, const char* end,
                                       const char** next, char16_t* result);
@@ -7975,18 +7976,20 @@ nsresult nsContentUtils::SetFetchReferre
   nsCOMPtr<nsIURI> principalURI;
 
   if (aPrincipal->IsSystemPrincipal()) {
     return NS_OK;
   }
 
   aPrincipal->GetURI(getter_AddRefs(principalURI));
 
+  nsCOMPtr<nsIReferrerInfo> referrerInfo;
   if (!aDoc) {
-    return aChannel->SetReferrerWithPolicy(principalURI, aReferrerPolicy);
+    referrerInfo = new ReferrerInfo(principalURI, aReferrerPolicy);
+    return aChannel->SetReferrerInfoWithoutClone(referrerInfo);
   }
 
   // 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
@@ -8005,17 +8008,18 @@ nsresult nsContentUtils::SetFetchReferre
       referrerURI = docCurURI;
     }
   }
 
   if (!referrerURI) {
     referrerURI = principalURI;
   }
 
-  return aChannel->SetReferrerWithPolicy(referrerURI, aReferrerPolicy);
+  referrerInfo = new ReferrerInfo(referrerURI, aReferrerPolicy);
+  return aChannel->SetReferrerInfoWithoutClone(referrerInfo);
 }
 
 // static
 net::ReferrerPolicy nsContentUtils::GetReferrerPolicyFromHeader(
     const nsAString& aHeader) {
   // Multiple headers could be concatenated into one comma-separated
   // list of policies. Need to tokenize the multiple headers.
   nsCharSeparatedTokenizer tokenizer(aHeader, ',');
@@ -9777,18 +9781,22 @@ bool nsContentUtils::AttemptLargeAllocat
   NS_ENSURE_TRUE(wbc3, false);
 
   nsCOMPtr<nsIURI> uri;
   rv = aChannel->GetURI(getter_AddRefs(uri));
   NS_ENSURE_SUCCESS(rv, false);
   NS_ENSURE_TRUE(uri, false);
 
   nsCOMPtr<nsIURI> referrer;
-  rv = aChannel->GetReferrer(getter_AddRefs(referrer));
+  nsCOMPtr<nsIReferrerInfo> referrerInfo;
+  rv = aChannel->GetReferrerInfo(getter_AddRefs(referrerInfo));
   NS_ENSURE_SUCCESS(rv, false);
+  if (referrerInfo) {
+    referrer = referrerInfo->GetComputedReferrer();
+  }
 
   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
   nsCOMPtr<nsIPrincipal> triggeringPrincipal = loadInfo->TriggeringPrincipal();
 
   // Currently we query the CSP from the triggeringPrincipal within the
   // loadInfo. After Bug 965637, we can query the CSP from the loadInfo, which
   // internally queries the CSP from the Client.
   nsCOMPtr<nsIContentSecurityPolicy> csp;
--- a/dom/base/nsObjectLoadingContent.cpp
+++ b/dom/base/nsObjectLoadingContent.cpp
@@ -89,16 +89,17 @@
 #include "mozilla/dom/HTMLObjectElementBinding.h"
 #include "mozilla/dom/HTMLEmbedElement.h"
 #include "mozilla/dom/HTMLObjectElement.h"
 #include "mozilla/net/UrlClassifierFeatureFactory.h"
 #include "mozilla/LoadInfo.h"
 #include "mozilla/PresShell.h"
 #include "nsChannelClassifier.h"
 #include "nsFocusManager.h"
+#include "ReferrerInfo.h"
 
 #ifdef XP_WIN
 // Thanks so much, Microsoft! :(
 #  ifdef CreateEvent
 #    undef CreateEvent
 #  endif
 #endif  // XP_WIN
 
@@ -2323,18 +2324,19 @@ nsresult nsObjectLoadingContent::OpenCha
   if (inherit) {
     nsCOMPtr<nsILoadInfo> loadinfo = chan->LoadInfo();
     loadinfo->SetPrincipalToInherit(thisContent->NodePrincipal());
   }
 
   // Referrer
   nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(chan));
   if (httpChan) {
-    rv = httpChan->SetReferrerWithPolicy(doc->GetDocumentURI(),
-                                         doc->GetReferrerPolicy());
+    nsCOMPtr<nsIReferrerInfo> referrerInfo =
+        new ReferrerInfo(doc->GetDocumentURI(), doc->GetReferrerPolicy());
+    rv = httpChan->SetReferrerInfoWithoutClone(referrerInfo);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
 
     // Set the initiator type
     nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChan));
     if (timedChannel) {
       timedChannel->SetInitiatorType(thisContent->LocalName());
     }
 
--- a/dom/base/nsSyncLoadService.cpp
+++ b/dom/base/nsSyncLoadService.cpp
@@ -19,16 +19,17 @@
 #include "nsString.h"
 #include "nsWeakReference.h"
 #include "mozilla/dom/Document.h"
 #include "nsIPrincipal.h"
 #include "nsContentUtils.h"  // for kLoadAsData
 #include "nsThreadUtils.h"
 #include "nsNetUtil.h"
 #include "nsStreamUtils.h"
+#include "ReferrerInfo.h"
 #include <algorithm>
 
 using namespace mozilla;
 
 using mozilla::net::ReferrerPolicy;
 
 /**
  * This class manages loading a single XML document
@@ -132,17 +133,19 @@ nsresult nsSyncLoader::LoadDocument(nsIC
         NS_LITERAL_CSTRING(
             "text/xml,application/xml,application/xhtml+xml,*/*;q=0.1"),
         false);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
     nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
     nsCOMPtr<nsIURI> loaderUri;
     loadInfo->TriggeringPrincipal()->GetURI(getter_AddRefs(loaderUri));
     if (loaderUri) {
-      rv = http->SetReferrerWithPolicy(loaderUri, aReferrerPolicy);
+      nsCOMPtr<nsIReferrerInfo> referrerInfo =
+          new ReferrerInfo(loaderUri, aReferrerPolicy);
+      rv = http->SetReferrerInfoWithoutClone(referrerInfo);
       MOZ_ASSERT(NS_SUCCEEDED(rv));
     }
   }
 
   // Hook us up to listen to redirects and the like.
   // Do this before setting up the cross-site proxy since
   // that installs its own proxies.
   mChannel->SetNotificationCallbacks(this);
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -601,17 +601,17 @@ nsresult FetchDriver::HttpFetch(
     }
     // Step 6 of https://fetch.spec.whatwg.org/#main-fetch
     // If request’s referrer policy is the empty string,
     // then set request’s referrer policy to the user-set default policy.
     if (mRequest->ReferrerPolicy_() == ReferrerPolicy::_empty) {
       nsCOMPtr<nsILoadInfo> loadInfo = httpChan->LoadInfo();
       bool isPrivate = loadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
       net::ReferrerPolicy referrerPolicy = static_cast<net::ReferrerPolicy>(
-          NS_GetDefaultReferrerPolicy(httpChan, uri, isPrivate));
+          ReferrerInfo::GetDefaultReferrerPolicy(httpChan, uri, isPrivate));
       mRequest->SetReferrerPolicy(referrerPolicy);
     }
 
     rv = FetchUtil::SetRequestReferrer(mPrincipal, mDocument, httpChan,
                                        mRequest);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Bug 1120722 - Authorization will be handled later.
--- a/dom/fetch/FetchUtil.cpp
+++ b/dom/fetch/FetchUtil.cpp
@@ -109,52 +109,56 @@ bool FetchUtil::ExtractHeader(nsACString
 }
 
 // static
 nsresult FetchUtil::SetRequestReferrer(nsIPrincipal* aPrincipal, Document* aDoc,
                                        nsIHttpChannel* aChannel,
                                        InternalRequest* aRequest) {
   MOZ_ASSERT(NS_IsMainThread());
 
+  nsresult rv = NS_OK;
   nsAutoString referrer;
   aRequest->GetReferrer(referrer);
+
   net::ReferrerPolicy policy = aRequest->GetReferrerPolicy();
-
-  nsresult rv = NS_OK;
+  nsCOMPtr<nsIReferrerInfo> referrerInfo;
   if (referrer.IsEmpty()) {
     // This is the case request’s referrer is "no-referrer"
-    rv = aChannel->SetReferrerWithPolicy(nullptr, net::RP_No_Referrer);
+    referrerInfo = new ReferrerInfo(nullptr, net::RP_No_Referrer);
+    rv = aChannel->SetReferrerInfoWithoutClone(referrerInfo);
     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);
+    referrerInfo = new ReferrerInfo(referrerURI, policy);
+    rv = aChannel->SetReferrerInfoWithoutClone(referrerInfo);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  nsCOMPtr<nsIURI> referrerURI;
-  Unused << aChannel->GetReferrer(getter_AddRefs(referrerURI));
+  nsCOMPtr<nsIURI> computedReferrer;
+  referrerInfo = aChannel->GetReferrerInfo();
+  if (referrerInfo) {
+    computedReferrer = referrerInfo->GetComputedReferrer();
+  }
 
   // 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) {
+  if (computedReferrer) {
     nsAutoCString spec;
-    rv = referrerURI->GetSpec(spec);
+    rv = computedReferrer->GetSpec(spec);
     NS_ENSURE_SUCCESS(rv, rv);
-
     aRequest->SetReferrer(NS_ConvertUTF8toUTF16(spec));
   } else {
     aRequest->SetReferrer(EmptyString());
   }
 
   return NS_OK;
 }
 
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -117,16 +117,17 @@
 #include "nsNodeInfoManager.h"
 #include "nsPresContext.h"
 #include "nsQueryObject.h"
 #include "nsRange.h"
 #include "nsSize.h"
 #include "nsThreadUtils.h"
 #include "nsURIHashKey.h"
 #include "nsVideoFrame.h"
+#include "ReferrerInfo.h"
 #include "xpcpublic.h"
 #include <algorithm>
 #include <cmath>
 #include <limits>
 
 mozilla::LazyLogModule gMediaElementLog("nsMediaElement");
 static mozilla::LazyLogModule gMediaElementEventsLog("nsMediaElementEvents");
 
@@ -6206,18 +6207,19 @@ void HTMLMediaElement::SetRequestHeaders
   // Content-Duration and a length spec in the container are not present either)
   // and from seeking. So, disable the standard "Accept-Encoding: gzip,deflate"
   // that we usually send. See bug 614760.
   DebugOnly<nsresult> rv = aChannel->SetRequestHeader(
       NS_LITERAL_CSTRING("Accept-Encoding"), EmptyCString(), false);
   MOZ_ASSERT(NS_SUCCEEDED(rv));
 
   // Set the Referer header
-  rv = aChannel->SetReferrerWithPolicy(OwnerDoc()->GetDocumentURI(),
-                                       OwnerDoc()->GetReferrerPolicy());
+  nsCOMPtr<nsIReferrerInfo> referrerInfo = new ReferrerInfo(
+      OwnerDoc()->GetDocumentURI(), OwnerDoc()->GetReferrerPolicy());
+  rv = aChannel->SetReferrerInfoWithoutClone(referrerInfo);
   MOZ_ASSERT(NS_SUCCEEDED(rv));
 }
 
 void HTMLMediaElement::FireTimeUpdate(bool aPeriodic) {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
 
   TimeStamp now = TimeStamp::Now();
   double time = CurrentTime();
--- a/dom/interfaces/security/nsIReferrerInfo.idl
+++ b/dom/interfaces/security/nsIReferrerInfo.idl
@@ -2,34 +2,45 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 #include "nsISerializable.idl"
 
 interface nsIURI;
 
+native URIRef(already_AddRefed<nsIURI>);
+
 [scriptable, builtinclass, uuid(081cdc36-f2e2-4f94-87bf-78578f06f1eb)]
 interface nsIReferrerInfo : nsISerializable
 {
   /**
   * The original referrer URI which indicates the full referrer before applying
   * referrer policy
   */
   [infallible] readonly attribute nsIURI originalReferrer;
 
   /**
   * Referrer policy which is applied to the referrer
   */
-  [infallible] attribute unsigned long referrerPolicy;
+  [infallible] readonly attribute unsigned long referrerPolicy;
 
   /**
   * Indicates if the referrer should not be sent or not even when it's available.
   */
-  [infallible] attribute boolean sendReferrer;
+  [infallible] readonly attribute boolean sendReferrer;
+
+  /**
+   * Get the computed referrer, if one has been set.  The computed referrer is
+   * the original referrer manipulated by the referrer-policy. Use the result of
+   * this function as the actual referrer value for the channel.
+   */
+
+  [must_use, noscript, nostdcall, notxpcom]
+  URIRef GetComputedReferrer();
 
   /**
    * Initialize method.
    * @param aReferrerPolicy referrer policy of the created object
    * @param aSendReferrer sendReferrer of the created object, defaults to false
    * @param aOriginalReferrer the original referrer, defaults to null.
    */
 
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -90,16 +90,17 @@
 #include "nsPluginNativeWindow.h"
 #include "nsIContentPolicy.h"
 #include "nsContentPolicyUtils.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Telemetry.h"
 #include "nsIImageLoadingContent.h"
 #include "mozilla/Preferences.h"
 #include "nsVersionComparator.h"
+#include "ReferrerInfo.h"
 
 #include "mozilla/dom/Promise.h"
 
 #if defined(XP_WIN)
 #  include "nsIWindowMediator.h"
 #  include "nsIBaseWindow.h"
 #  include "windows.h"
 #  include "winbase.h"
@@ -3130,18 +3131,19 @@ nsresult nsPluginHost::NewPluginURLStrea
 
       if (!referer) {
         if (!doc) {
           return NS_ERROR_FAILURE;
         }
         referer = doc->GetDocumentURI();
         referrerPolicy = doc->GetReferrerPolicy();
       }
-
-      rv = httpChannel->SetReferrerWithPolicy(referer, referrerPolicy);
+      nsCOMPtr<nsIReferrerInfo> referrerInfo =
+          new mozilla::dom::ReferrerInfo(referer, referrerPolicy);
+      rv = httpChannel->SetReferrerInfoWithoutClone(referrerInfo);
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     if (aPostStream) {
       // XXX it's a bit of a hack to rewind the postdata stream
       // here but it has to be done in case the post data is
       // being reused multiple times.
       nsCOMPtr<nsISeekableStream> postDataSeekable(
--- a/dom/script/ScriptLoader.cpp
+++ b/dom/script/ScriptLoader.cpp
@@ -59,16 +59,17 @@
 #include "nsContentCreatorFunctions.h"
 #include "nsProxyRelease.h"
 #include "nsSandboxFlags.h"
 #include "nsContentTypeParser.h"
 #include "nsINetworkPredictor.h"
 #include "nsMimeTypes.h"
 #include "mozilla/ConsoleReportCollector.h"
 #include "mozilla/LoadInfo.h"
+#include "ReferrerInfo.h"
 
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Unused.h"
 #include "nsIScriptError.h"
 #include "nsIAsyncOutputStream.h"
@@ -1349,18 +1350,19 @@ nsresult ScriptLoader::StartLoad(ScriptL
     if (nsJSUtils::BinASTEncodingEnabled() &&
         aRequest->ShouldAcceptBinASTEncoding()) {
       acceptTypes = APPLICATION_JAVASCRIPT_BINAST ", */*";
     }
     rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
                                        acceptTypes, false);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
 
-    rv = httpChannel->SetReferrerWithPolicy(aRequest->mReferrer,
-                                            aRequest->ReferrerPolicy());
+    nsCOMPtr<nsIReferrerInfo> referrerInfo =
+        new ReferrerInfo(aRequest->mReferrer, aRequest->ReferrerPolicy());
+    rv = httpChannel->SetReferrerInfoWithoutClone(referrerInfo);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
 
     nsCOMPtr<nsIHttpChannelInternal> internalChannel(
         do_QueryInterface(httpChannel));
     if (internalChannel) {
       rv = internalChannel->SetIntegrityMetadata(
           aRequest->mIntegrity.GetIntegrityString());
       MOZ_ASSERT(NS_SUCCEEDED(rv));
--- a/dom/security/ReferrerInfo.cpp
+++ b/dom/security/ReferrerInfo.cpp
@@ -1,115 +1,824 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "ReferrerInfo.h"
+#include "nsIClassInfoImpl.h"
+#include "nsContentUtils.h"
+#include "nsICookieService.h"
+#include "nsIHttpChannel.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
-#include "nsIClassInfoImpl.h"
+#include "nsIURIFixup.h"
+#include "nsIURL.h"
+#include "nsIURIMutator.h"
+
+#include "nsAlgorithm.h"
+#include "ReferrerInfo.h"
+
+#include "mozilla/AntiTrackingCommon.h"
+#include "mozilla/net/HttpBaseChannel.h"
+
+static mozilla::LazyLogModule gReferrerInfoLog("ReferrerInfo");
+#define LOG(msg) MOZ_LOG(gReferrerInfoLog, mozilla::LogLevel::Debug, msg)
+#define LOG_ENABLED() MOZ_LOG_TEST(gReferrerInfoLog, mozilla::LogLevel::Debug)
+
+using namespace mozilla::net;
 
 namespace mozilla {
 namespace dom {
 
 // Implementation of ClassInfo is required to serialize/deserialize
 NS_IMPL_CLASSINFO(ReferrerInfo, nullptr, nsIClassInfo::MAIN_THREAD_ONLY,
                   REFERRERINFO_CID)
 
 NS_IMPL_ISUPPORTS_CI(ReferrerInfo, nsIReferrerInfo, nsISerializable)
 
+#define DEFAULT_RP 3
+#define DEFAULT_TRACKER_RP 3
+#define DEFAULT_PRIVATE_RP 2
+#define DEFAULT_TRACKER_PRIVATE_RP 2
+
+#define MAX_REFERRER_SENDING_POLICY 2
+#define MAX_CROSS_ORIGIN_SENDING_POLICY 2
+#define MAX_TRIMMING_POLICY 2
+
+#define MIN_REFERRER_SENDING_POLICY 0
+#define MIN_CROSS_ORIGIN_SENDING_POLICY 0
+#define MIN_TRIMMING_POLICY 0
+
+static uint32_t sDefaultRp = DEFAULT_RP;
+static uint32_t sDefaultTrackerRp = DEFAULT_TRACKER_RP;
+static uint32_t defaultPrivateRp = DEFAULT_PRIVATE_RP;
+static uint32_t defaultTrackerPrivateRp = DEFAULT_TRACKER_PRIVATE_RP;
+
+static bool sUserSpoofReferrerSource = false;
+static bool sUserHideOnionReferrerSource = false;
+static uint32_t sUserReferrerSendingPolicy = 0;
+static uint32_t sUserXOriginSendingPolicy = 0;
+static uint32_t sUserTrimmingPolicy = 0;
+static uint32_t sUserXOriginTrimmingPolicy = 0;
+
+static void CachePreferrenceValue() {
+  static bool sPrefCached = false;
+  if (sPrefCached) {
+    return;
+  }
+
+  Preferences::AddBoolVarCache(&sUserSpoofReferrerSource,
+                               "network.http.referer.spoofSource");
+  Preferences::AddBoolVarCache(&sUserHideOnionReferrerSource,
+                               "network.http.referer.hideOnionSource");
+  Preferences::AddUintVarCache(&sUserReferrerSendingPolicy,
+                               "network.http.sendRefererHeader");
+  sUserReferrerSendingPolicy =
+      clamped<uint32_t>(sUserReferrerSendingPolicy, MIN_REFERRER_SENDING_POLICY,
+                        MAX_REFERRER_SENDING_POLICY);
+
+  Preferences::AddUintVarCache(&sUserXOriginSendingPolicy,
+                               "network.http.referer.XOriginPolicy");
+  sUserXOriginSendingPolicy = clamped<uint32_t>(
+      sUserXOriginSendingPolicy, MIN_CROSS_ORIGIN_SENDING_POLICY,
+      MAX_CROSS_ORIGIN_SENDING_POLICY);
+
+  Preferences::AddUintVarCache(&sUserTrimmingPolicy,
+                               "network.http.referer.trimmingPolicy");
+  sUserTrimmingPolicy = clamped<uint32_t>(
+      sUserTrimmingPolicy, MIN_TRIMMING_POLICY, MAX_TRIMMING_POLICY);
+
+  Preferences::AddUintVarCache(&sUserXOriginTrimmingPolicy,
+                               "network.http.referer.XOriginTrimmingPolicy");
+  sUserXOriginTrimmingPolicy = clamped<uint32_t>(
+      sUserXOriginTrimmingPolicy, MIN_TRIMMING_POLICY, MAX_TRIMMING_POLICY);
+
+  Preferences::AddUintVarCache(
+      &sDefaultRp, "network.http.referer.defaultPolicy", DEFAULT_RP);
+  Preferences::AddUintVarCache(&sDefaultTrackerRp,
+                               "network.http.referer.defaultPolicy.trackers",
+                               DEFAULT_TRACKER_RP);
+  Preferences::AddUintVarCache(&defaultPrivateRp,
+                               "network.http.referer.defaultPolicy.pbmode",
+                               DEFAULT_PRIVATE_RP);
+  Preferences::AddUintVarCache(
+      &defaultTrackerPrivateRp,
+      "network.http.referer.defaultPolicy.trackers.pbmode",
+      DEFAULT_TRACKER_PRIVATE_RP);
+
+  sPrefCached = true;
+}
+
+/* static */
+bool ReferrerInfo::HideOnionReferrerSource() {
+  CachePreferrenceValue();
+  return sUserHideOnionReferrerSource;
+}
+
+/* static */
+uint32_t ReferrerInfo::GetDefaultReferrerPolicy(nsIHttpChannel* aChannel,
+                                                nsIURI* aURI,
+                                                bool privateBrowsing) {
+  CachePreferrenceValue();
+  bool thirdPartyTrackerIsolated = false;
+  if (StaticPrefs::network_cookie_cookieBehavior() ==
+          nsICookieService::BEHAVIOR_REJECT_TRACKER &&
+      aChannel && aURI) {
+    uint32_t rejectedReason = 0;
+    thirdPartyTrackerIsolated =
+        !AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(
+            aChannel, aURI, &rejectedReason);
+    // Here we intentionally do not notify about the rejection reason, if any
+    // in order to avoid this check to have any visible side-effects (e.g. a
+    // web console report.)
+  }
+
+  uint32_t defaultToUse;
+  if (thirdPartyTrackerIsolated) {
+    if (privateBrowsing) {
+      defaultToUse = defaultTrackerPrivateRp;
+    } else {
+      defaultToUse = sDefaultTrackerRp;
+    }
+  } else {
+    if (privateBrowsing) {
+      defaultToUse = defaultPrivateRp;
+    } else {
+      defaultToUse = sDefaultRp;
+    }
+  }
+
+  switch (defaultToUse) {
+    case DefaultReferrerPolicy::eDefaultPolicyNoReferrer:
+      return nsIHttpChannel::REFERRER_POLICY_NO_REFERRER;
+    case DefaultReferrerPolicy::eDefaultPolicySameOrgin:
+      return nsIHttpChannel::REFERRER_POLICY_SAME_ORIGIN;
+    case DefaultReferrerPolicy::eDefaultPolicyStrictWhenXorigin:
+      return nsIHttpChannel::REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN;
+  }
+
+  return nsIHttpChannel::REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE;
+}
+
+/* static */
+bool ReferrerInfo::IsReferrerSchemeAllowed(nsIURI* aReferrer) {
+  NS_ENSURE_TRUE(aReferrer, false);
+
+  nsAutoCString scheme;
+  nsresult rv = aReferrer->GetScheme(scheme);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  return scheme.EqualsIgnoreCase("https") || scheme.EqualsIgnoreCase("http") ||
+         scheme.EqualsIgnoreCase("ftp");
+}
+
+nsresult ReferrerInfo::HandleSecureToInsecureReferral(nsIURI* aURI,
+                                                      bool& aAllowed) const {
+  NS_ENSURE_ARG(aURI);
+
+  aAllowed = false;
+  bool referrerIsHttpsScheme;
+  nsresult rv = mOriginalReferrer->SchemeIs("https", &referrerIsHttpsScheme);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (!referrerIsHttpsScheme) {
+    aAllowed = true;
+    return NS_OK;
+  }
+
+  bool uriIsHttpsScheme;
+  rv = aURI->SchemeIs("https", &uriIsHttpsScheme);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // It's ok to send referrer for https-to-http scenarios if the referrer
+  // policy is "unsafe-url", "origin", or "origin-when-cross-origin".
+  // in other referrer policies, https->http is not allowed...
+
+  if (mPolicy != nsIHttpChannel::REFERRER_POLICY_UNSAFE_URL &&
+      mPolicy != nsIHttpChannel::REFERRER_POLICY_ORIGIN_WHEN_XORIGIN &&
+      mPolicy != nsIHttpChannel::REFERRER_POLICY_ORIGIN && !uriIsHttpsScheme) {
+    return NS_OK;
+  }
+
+  aAllowed = true;
+  return NS_OK;
+}
+
+nsresult ReferrerInfo::HandleUserXOriginSendingPolicy(nsIURI* aURI,
+                                                      nsIURI* aReferrer,
+                                                      bool& aAllowed) const {
+  NS_ENSURE_ARG(aURI);
+  aAllowed = false;
+
+  nsAutoCString uriHost;
+  nsAutoCString referrerHost;
+
+  nsresult rv = aURI->GetAsciiHost(uriHost);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aReferrer->GetAsciiHost(referrerHost);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Send an empty referrer if xorigin and leaving a .onion domain.
+  if (sUserHideOnionReferrerSource && !uriHost.Equals(referrerHost) &&
+      StringEndsWith(referrerHost, NS_LITERAL_CSTRING(".onion"))) {
+    return NS_OK;
+  }
+
+  switch (sUserXOriginSendingPolicy) {
+    // Check policy for sending referrer only when hosts match
+    case XOriginSendingPolicy::ePolicySendWhenSameHost: {
+      if (!uriHost.Equals(referrerHost)) {
+        return NS_OK;
+      }
+      break;
+    }
+
+    case XOriginSendingPolicy::ePolicySendWhenSameDomain: {
+      nsCOMPtr<nsIEffectiveTLDService> eTLDService =
+          do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
+      if (!eTLDService) {
+        // check policy for sending only when effective top level domain
+        // matches. this falls back on using host if eTLDService does not work
+        if (!uriHost.Equals(referrerHost)) {
+          return NS_OK;
+        }
+        break;
+      }
+
+      nsAutoCString uriDomain;
+      nsAutoCString referrerDomain;
+      uint32_t extraDomains = 0;
+
+      rv = eTLDService->GetBaseDomain(aURI, extraDomains, uriDomain);
+      if (rv == NS_ERROR_HOST_IS_IP_ADDRESS ||
+          rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
+        // uri is either an IP address, an alias such as 'localhost', an eTLD
+        // such as 'co.uk', or the empty string. Uses the normalized host in
+        // such cases.
+        rv = aURI->GetAsciiHost(uriDomain);
+      }
+
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+
+      rv = eTLDService->GetBaseDomain(aReferrer, extraDomains, referrerDomain);
+      if (rv == NS_ERROR_HOST_IS_IP_ADDRESS ||
+          rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
+        // referrer is either an IP address, an alias such as 'localhost', an
+        // eTLD such as 'co.uk', or the empty string. Uses the normalized host
+        // in such cases.
+        rv = aReferrer->GetAsciiHost(referrerDomain);
+      }
+
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+
+      if (!uriDomain.Equals(referrerDomain)) {
+        return NS_OK;
+      }
+      break;
+    }
+
+    default:
+      break;
+  }
+
+  aAllowed = true;
+  return NS_OK;
+}
+
+nsresult ReferrerInfo::HandleUserReferrerSendingPolicy(nsIHttpChannel* aChannel,
+                                                       bool& aAllowed) const {
+  aAllowed = false;
+  uint32_t referrerSendingPolicy;
+  uint32_t loadFlags;
+  nsresult rv = aChannel->GetLoadFlags(&loadFlags);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (loadFlags & nsIHttpChannel::LOAD_INITIAL_DOCUMENT_URI) {
+    referrerSendingPolicy = ReferrerSendingPolicy::ePolicySendWhenUserTrigger;
+  } else {
+    referrerSendingPolicy = ReferrerSendingPolicy::ePolicySendInlineContent;
+  }
+  if (sUserReferrerSendingPolicy < referrerSendingPolicy) {
+    return NS_OK;
+  }
+
+  aAllowed = true;
+  return NS_OK;
+}
+
+bool ReferrerInfo::IsCrossOriginRequest(nsIHttpChannel* aChannel) const {
+  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
+
+  nsCOMPtr<nsIURI> triggeringURI;
+  loadInfo->TriggeringPrincipal()->GetURI(getter_AddRefs(triggeringURI));
+
+  if (!triggeringURI) {
+    LOG(("no triggering URI via loadInfo, assuming load is cross-origin"));
+    return true;
+  }
+
+  if (LOG_ENABLED()) {
+    nsAutoCString triggeringURISpec;
+    triggeringURI->GetAsciiSpec(triggeringURISpec);
+    LOG(("triggeringURI=%s\n", triggeringURISpec.get()));
+  }
+
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return true;
+  }
+
+  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+  bool isPrivateWin = loadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
+
+  rv = ssm->CheckSameOriginURI(triggeringURI, uri, false, isPrivateWin);
+  return (NS_FAILED(rv));
+}
+
+ReferrerInfo::TrimmingPolicy ReferrerInfo::ComputeTrimmingPolicy(
+    nsIHttpChannel* aChannel) const {
+  uint32_t trimmingPolicy = sUserTrimmingPolicy;
+
+  switch (mPolicy) {
+    case nsIHttpChannel::REFERRER_POLICY_ORIGIN:
+    case nsIHttpChannel::REFERRER_POLICY_STRICT_ORIGIN:
+      trimmingPolicy = TrimmingPolicy::ePolicySchemeHostPort;
+      break;
+
+    case nsIHttpChannel::REFERRER_POLICY_ORIGIN_WHEN_XORIGIN:
+    case nsIHttpChannel::REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN:
+      if (trimmingPolicy != TrimmingPolicy::ePolicySchemeHostPort &&
+          IsCrossOriginRequest(aChannel)) {
+        // Ignore set trimmingPolicy if it is already the strictest
+        // policy.
+        trimmingPolicy = TrimmingPolicy::ePolicySchemeHostPort;
+      }
+      break;
+
+    // This function is called when a nonempty referrer value is allowed to
+    // send. For the next 3 policies: same-origin, no-referrer-when-downgrade,
+    // unsafe-url, without trimming we should have a full uri. And the trimming
+    // policy only depends on user prefs.
+    case nsIHttpChannel::REFERRER_POLICY_SAME_ORIGIN:
+    case nsIHttpChannel::REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE:
+    case nsIHttpChannel::REFERRER_POLICY_UNSAFE_URL:
+      if (trimmingPolicy != TrimmingPolicy::ePolicySchemeHostPort) {
+        // Ignore set trimmingPolicy if it is already the strictest
+        // policy. Apply the user cross-origin trimming policy if it's more
+        // restrictive than the general one.
+        if (sUserXOriginTrimmingPolicy != TrimmingPolicy::ePolicyFullURI &&
+            IsCrossOriginRequest(aChannel)) {
+          trimmingPolicy = std::max(trimmingPolicy, sUserXOriginTrimmingPolicy);
+        }
+      }
+      break;
+
+    case nsIHttpChannel::REFERRER_POLICY_NO_REFERRER:
+    case nsIHttpChannel::REFERRER_POLICY_UNSET:
+    default:
+      MOZ_ASSERT_UNREACHABLE("Unexpected value");
+      break;
+  }
+
+  return static_cast<TrimmingPolicy>(trimmingPolicy);
+}
+
+nsresult ReferrerInfo::TrimReferrerWithPolicy(nsCOMPtr<nsIURI>& aReferrer,
+                                              TrimmingPolicy aTrimmingPolicy,
+                                              nsACString& aResult) const {
+  if (aTrimmingPolicy == TrimmingPolicy::ePolicyFullURI) {
+    // use the full URI
+    return aReferrer->GetAsciiSpec(aResult);
+  }
+
+  // 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;
+  nsresult rv = aReferrer->GetScheme(scheme);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  aResult = scheme;
+  aResult.AppendLiteral("://");
+  // Note we explicitly cleared UserPass above, so do not need to build it.
+  rv = aReferrer->GetAsciiHostPort(asciiHostPort);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  aResult.Append(asciiHostPort);
+
+  switch (aTrimmingPolicy) {
+    case TrimmingPolicy::ePolicySchemeHostPortPath: {
+      nsCOMPtr<nsIURL> url(do_QueryInterface(aReferrer));
+      if (url) {
+        nsAutoCString path;
+        rv = url->GetFilePath(path);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
+        aResult.Append(path);
+        rv = NS_MutateURI(url)
+                 .SetQuery(EmptyCString())
+                 .SetRef(EmptyCString())
+                 .Finalize(aReferrer);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
+        break;
+      }
+      // No URL, so fall through to truncating the path and any query/ref off
+      // as well.
+    }
+      MOZ_FALLTHROUGH;
+    default:  // (User Pref limited to [0,2])
+    case TrimmingPolicy::ePolicySchemeHostPort:
+      aResult.AppendLiteral("/");
+      // This nukes any query/ref present as well in the case of nsStandardURL
+      rv = NS_MutateURI(aReferrer)
+               .SetPathQueryRef(EmptyCString())
+               .Finalize(aReferrer);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+      break;
+  }
+
+  return NS_OK;
+}
+ReferrerInfo::ReferrerInfo()
+    : mOriginalReferrer(nullptr),
+      mPolicy(mozilla::net::RP_Unset),
+      mSendReferrer(true),
+      mInitialized(false),
+      mOverridePolicyByDefault(false),
+      mComputedReferrer(Maybe<nsCString>()) {}
+
 ReferrerInfo::ReferrerInfo(nsIURI* aOriginalReferrer, uint32_t aPolicy,
-                           bool aSendReferrer)
+                           bool aSendReferrer,
+                           const Maybe<nsCString>& aComputedReferrer)
     : mOriginalReferrer(aOriginalReferrer),
       mPolicy(aPolicy),
-      mSendReferrer(aSendReferrer) {}
+      mSendReferrer(aSendReferrer),
+      mInitialized(true),
+      mOverridePolicyByDefault(false),
+      mComputedReferrer(aComputedReferrer) {}
+
+ReferrerInfo::ReferrerInfo(const ReferrerInfo& rhs)
+    : mOriginalReferrer(rhs.mOriginalReferrer),
+      mPolicy(rhs.mPolicy),
+      mSendReferrer(rhs.mSendReferrer),
+      mInitialized(rhs.mInitialized),
+      mOverridePolicyByDefault(rhs.mOverridePolicyByDefault),
+      mComputedReferrer(rhs.mComputedReferrer) {}
+
+already_AddRefed<nsIReferrerInfo> ReferrerInfo::Clone() const {
+  RefPtr<ReferrerInfo> copy(new ReferrerInfo(*this));
+  return copy.forget();
+}
+
+already_AddRefed<nsIReferrerInfo> ReferrerInfo::CloneWithNewPolicy(
+    uint32_t aPolicy) const {
+  RefPtr<ReferrerInfo> copy(new ReferrerInfo(*this));
+  copy->mPolicy = aPolicy;
+  return copy.forget();
+}
 
 NS_IMETHODIMP
 ReferrerInfo::GetOriginalReferrer(nsIURI** aOriginalReferrer) {
   *aOriginalReferrer = mOriginalReferrer;
   NS_IF_ADDREF(*aOriginalReferrer);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 ReferrerInfo::GetReferrerPolicy(uint32_t* aReferrerPolicy) {
   *aReferrerPolicy = mPolicy;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-ReferrerInfo::SetReferrerPolicy(uint32_t aReferrerPolicy) {
-  mPolicy = aReferrerPolicy;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 ReferrerInfo::GetSendReferrer(bool* aSendReferrer) {
   *aSendReferrer = mSendReferrer;
   return NS_OK;
 }
 
-NS_IMETHODIMP
-ReferrerInfo::SetSendReferrer(bool aSendReferrer) {
-  mSendReferrer = aSendReferrer;
-  return NS_OK;
+already_AddRefed<nsIURI> ReferrerInfo::GetComputedReferrer() {
+  if (!mComputedReferrer.isSome() || mComputedReferrer.value().IsEmpty()) {
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIURI> result;
+  nsresult rv = NS_NewURI(getter_AddRefs(result), mComputedReferrer.value());
+  if (NS_FAILED(rv)) {
+    return nullptr;
+  }
+
+  return result.forget();
 }
 
 NS_IMETHODIMP
 ReferrerInfo::Init(uint32_t aReferrerPolicy, bool aSendReferrer,
                    nsIURI* aOriginalReferrer) {
+  MOZ_ASSERT(!mInitialized);
+  if (mInitialized) {
+    return NS_ERROR_ALREADY_INITIALIZED;
+  };
+
+  mInitialized = true;
   mPolicy = aReferrerPolicy;
   mSendReferrer = aSendReferrer;
   mOriginalReferrer = aOriginalReferrer;
   return NS_OK;
 }
 
+nsresult ReferrerInfo::ComputeReferrer(nsIHttpChannel* aChannel) {
+  CachePreferrenceValue();
+  NS_ENSURE_ARG(aChannel);
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // If the referrerInfo is passed around when redirect, just use the last
+  // computedReferrer to recompute
+  nsCOMPtr<nsIURI> referrer;
+  nsresult rv = NS_OK;
+  mOverridePolicyByDefault = false;
+
+  if (mComputedReferrer.isSome()) {
+    if (mComputedReferrer.value().IsEmpty()) {
+      return NS_OK;
+    }
+
+    rv = NS_NewURI(getter_AddRefs(referrer), mComputedReferrer.value());
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  mComputedReferrer.reset();
+  // Emplace mComputedReferrer with an empty string, which means we have
+  // computed the referrer and the result referrer value is empty (not send
+  // referrer). So any early return later than this line will use that empty
+  // referrer.
+  mComputedReferrer.emplace(EmptyCString());
+
+  if (!mSendReferrer || !mOriginalReferrer ||
+      mPolicy == nsIHttpChannel::REFERRER_POLICY_NO_REFERRER) {
+    return NS_OK;
+  }
+
+  if (mPolicy == nsIHttpChannel::REFERRER_POLICY_UNSET) {
+    nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
+    OriginAttributes attrs = loadInfo->GetOriginAttributes();
+    bool isPrivate = attrs.mPrivateBrowsingId > 0;
+
+    nsCOMPtr<nsIURI> uri;
+    rv = aChannel->GetURI(getter_AddRefs(uri));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    mPolicy = GetDefaultReferrerPolicy(aChannel, uri, isPrivate);
+    mOverridePolicyByDefault = true;
+  }
+
+  if (mPolicy == nsIHttpChannel::REFERRER_POLICY_NO_REFERRER) {
+    return NS_OK;
+  }
+
+  bool isUserReferrerSendingAllowed = false;
+  rv = HandleUserReferrerSendingPolicy(aChannel, isUserReferrerSendingAllowed);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (!isUserReferrerSendingAllowed) {
+    return NS_OK;
+  }
+
+  // Enforce Referrer whitelist, only http, https, ftp scheme are allowed
+  if (!IsReferrerSchemeAllowed(mOriginalReferrer)) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIURI> uri;
+  rv = aChannel->GetURI(getter_AddRefs(uri));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  bool isSecureToInsecureAllowed = false;
+  rv = HandleSecureToInsecureReferral(uri, isSecureToInsecureAllowed);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (!isSecureToInsecureAllowed) {
+    return NS_OK;
+  }
+
+  // Don't send referrer when the request is cross-origin and policy is
+  // "same-origin".
+  if (mPolicy == nsIHttpChannel::REFERRER_POLICY_SAME_ORIGIN &&
+      IsCrossOriginRequest(aChannel)) {
+    return NS_OK;
+  }
+
+  // Strip away any fragment per RFC 2616 section 14.36
+  // and Referrer Policy section 6.3.5.
+  if (!referrer) {
+    rv = NS_GetURIWithoutRef(mOriginalReferrer, getter_AddRefs(referrer));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  bool isUserXOriginAllowed = false;
+  rv = HandleUserXOriginSendingPolicy(uri, referrer, isUserXOriginAllowed);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (!isUserXOriginAllowed) {
+    return NS_OK;
+  }
+
+  // Handle user pref network.http.referer.spoofSource, send spoofed referrer if
+  // desired
+  if (sUserSpoofReferrerSource) {
+    nsCOMPtr<nsIURI> userSpoofReferrer;
+    rv = NS_GetURIWithoutRef(uri, getter_AddRefs(userSpoofReferrer));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+    referrer = userSpoofReferrer;
+  }
+
+  // strip away any userpass; we don't want to be giving out passwords ;-)
+  // This is required by Referrer Policy stripping algorithm.
+  nsCOMPtr<nsIURIFixup> urifixup = services::GetURIFixup();
+  if (NS_WARN_IF(!urifixup)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIURI> exposableURI;
+  rv = urifixup->CreateExposableURI(referrer, getter_AddRefs(exposableURI));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  referrer = exposableURI;
+
+  TrimmingPolicy trimmingPolicy = ComputeTrimmingPolicy(aChannel);
+
+  nsAutoCString referrerSpec;
+  rv = TrimReferrerWithPolicy(referrer, trimmingPolicy, referrerSpec);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // finally, remember the referrer URI.
+  mComputedReferrer.reset();
+  mComputedReferrer.emplace(referrerSpec);
+
+  return NS_OK;
+}
+
 /* ===== nsISerializable implementation ====== */
 
 NS_IMETHODIMP
 ReferrerInfo::Read(nsIObjectInputStream* aStream) {
-  nsresult rv;
-  nsCOMPtr<nsISupports> supports;
-
-  rv = NS_ReadOptionalObject(aStream, true, getter_AddRefs(supports));
-  if (NS_FAILED(rv)) {
+  bool nonNull;
+  nsresult rv = aStream->ReadBoolean(&nonNull);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  mOriginalReferrer = do_QueryInterface(supports);
+  if (nonNull) {
+    nsAutoCString spec;
+    nsresult rv = aStream->ReadCString(spec);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = NS_NewURI(getter_AddRefs(mOriginalReferrer), spec);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  } else {
+    mOriginalReferrer = nullptr;
+  }
 
   rv = aStream->Read32(&mPolicy);
-  if (NS_FAILED(rv)) {
+  if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   rv = aStream->ReadBoolean(&mSendReferrer);
-  if (NS_FAILED(rv)) {
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  bool isComputed;
+  rv = aStream->ReadBoolean(&isComputed);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (isComputed) {
+    nsAutoCString computedReferrer;
+    rv = aStream->ReadCString(computedReferrer);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+    mComputedReferrer.emplace(computedReferrer);
+  }
+
+  rv = aStream->ReadBoolean(&mInitialized);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aStream->ReadBoolean(&mOverridePolicyByDefault);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 ReferrerInfo::Write(nsIObjectOutputStream* aStream) {
-  nsresult rv = NS_WriteOptionalCompoundObject(aStream, mOriginalReferrer,
-                                               NS_GET_IID(nsIURI), true);
-  if (NS_FAILED(rv)) {
+  bool nonNull = (mOriginalReferrer != nullptr);
+  nsresult rv = aStream->WriteBoolean(nonNull);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+  if (nonNull) {
+    nsAutoCString spec;
+    nsresult rv = mOriginalReferrer->GetSpec(spec);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = aStream->WriteStringZ(spec.get());
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
   rv = aStream->Write32(mPolicy);
-  if (NS_FAILED(rv)) {
+  if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   rv = aStream->WriteBoolean(mSendReferrer);
-  if (NS_FAILED(rv)) {
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  bool isComputed = mComputedReferrer.isSome();
+  rv = aStream->WriteBoolean(isComputed);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+  if (isComputed) {
+    rv = aStream->WriteStringZ(mComputedReferrer.value().get());
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  rv = aStream->WriteBoolean(mInitialized);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aStream->WriteBoolean(mOverridePolicyByDefault);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
   return NS_OK;
 }
+
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/security/ReferrerInfo.h
+++ b/dom/security/ReferrerInfo.h
@@ -6,55 +6,234 @@
 
 #ifndef mozilla_dom_ReferrerInfo_h
 #define mozilla_dom_ReferrerInfo_h
 
 #include "nsCOMPtr.h"
 #include "nsIReferrerInfo.h"
 #include "nsISerializable.h"
 #include "mozilla/net/ReferrerPolicy.h"
+#include "nsReadableUtils.h"
+#include "mozilla/Maybe.h"
 
 #define REFERRERINFOF_CONTRACTID "@mozilla.org/referrer-info;1"
 // 041a129f-10ce-4bda-a60d-e027a26d5ed0
 #define REFERRERINFO_CID                             \
   {                                                  \
     0x041a129f, 0x10ce, 0x4bda, {                    \
       0xa6, 0x0d, 0xe0, 0x27, 0xa2, 0x6d, 0x5e, 0xd0 \
     }                                                \
   }
 
 class nsIURI;
 class nsIChannel;
+class nsILoadInfo;
+
+namespace mozilla {
+namespace net {
+class HttpBaseChannel;
+class nsHttpChannel;
+}  // namespace net
+}  // namespace mozilla
+
+using mozilla::Maybe;
 
 namespace mozilla {
 namespace dom {
 
 /**
- * ReferrerInfo class holds a original referrer URL, as well as the referrer
- * policy to be applied to this referrer.
+ * The ReferrerInfo class holds the raw referrer and potentially a referrer
+ * policy which allows to query the computed referrer which should be applied to
+ * a channel as the actual referrer value.
  *
- **/
+ * The ReferrerInfo class solely contains readonly fields and represents a 1:1
+ * sync to the referrer header of the corresponding channel. In turn that means
+ * the class is immutable - so any modifications require to clone the current
+ * ReferrerInfo.
+ *
+ * For example if a request undergoes a redirect, the new channel
+ * will need a new ReferrerInfo clone with members being updated accordingly.
+ */
+
 class ReferrerInfo : public nsIReferrerInfo {
  public:
-  ReferrerInfo() = default;
-  explicit ReferrerInfo(nsIURI* aOriginalReferrer,
-                        uint32_t aPolicy = mozilla::net::RP_Unset,
-                        bool aSendReferrer = true);
+  ReferrerInfo();
+
+  explicit ReferrerInfo(
+      nsIURI* aOriginalReferrer, uint32_t aPolicy = mozilla::net::RP_Unset,
+      bool aSendReferrer = true,
+      const Maybe<nsCString>& aComputedReferrer = Maybe<nsCString>());
+
+  // create an exact copy of the ReferrerInfo
+  already_AddRefed<nsIReferrerInfo> Clone() const;
+
+  // create an copy of the ReferrerInfo with new referrer policy
+  already_AddRefed<nsIReferrerInfo> CloneWithNewPolicy(uint32_t aPolicy) const;
+
+  /**
+   * Check whether the given referrer's scheme is allowed to be computed and
+   * sent. The whitelist schemes are: http, https, ftp.
+   */
+  static bool IsReferrerSchemeAllowed(nsIURI* aReferrer);
+
+  /*
+   * Check whether we need to hide referrer when leaving a .onion domain.
+   * Controlled by user pref: network.http.referer.hideOnionSource
+   */
+  static bool HideOnionReferrerSource();
+
+  /**
+   * Return default referrer policy which is controlled by user
+   * prefs:
+   * network.http.referer.defaultPolicy for regular mode
+   * network.http.referer.defaultPolicy.trackers for third-party trackers
+   * in regular mode
+   * network.http.referer.defaultPolicy.pbmode for private mode
+   * network.http.referer.defaultPolicy.trackers.pbmode for third-party trackers
+   * in private mode
+   */
+  static uint32_t GetDefaultReferrerPolicy(nsIHttpChannel* aChannel = nullptr,
+                                           nsIURI* aURI = nullptr,
+                                           bool privateBrowsing = false);
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIREFERRERINFO
   NS_DECL_NSISERIALIZABLE
 
  private:
   virtual ~ReferrerInfo() {}
 
+  ReferrerInfo(const ReferrerInfo& rhs);
+
+  /*
+   * Default referrer policy to use
+   */
+  enum DefaultReferrerPolicy : uint32_t {
+    eDefaultPolicyNoReferrer = 0,
+    eDefaultPolicySameOrgin = 1,
+    eDefaultPolicyStrictWhenXorigin = 2,
+    eDefaultPolicyNoReferrerWhenDownGrade = 3,
+  };
+
+  /*
+   * Trimming policy when compute referrer, indicate how much information in the
+   * referrer will be sent. Order matters here.
+   */
+  enum TrimmingPolicy : uint32_t {
+    ePolicyFullURI = 0,
+    ePolicySchemeHostPortPath = 1,
+    ePolicySchemeHostPort = 2,
+  };
+
+  /*
+   * Referrer sending policy, indicates type of action could trigger to send
+   * referrer header, not send at all, send only with user's action (click on a
+   * link) or send even with inline content request (image request).
+   * Order matters here.
+   */
+  enum ReferrerSendingPolicy : uint32_t {
+    ePolicyNotSend = 0,
+    ePolicySendWhenUserTrigger = 1,
+    ePolicySendInlineContent = 2,
+  };
+
+  /*
+   * Sending referrer when cross origin policy, indicates when referrer should
+   * be send when compare 2 origins. Order matters here.
+   */
+  enum XOriginSendingPolicy : uint32_t {
+    ePolicyAlwaysSend = 0,
+    ePolicySendWhenSameDomain = 1,
+    ePolicySendWhenSameHost = 2,
+  };
+
+  /**
+   * Returns true if the given channel is cross-origin request
+   *
+   * Computing whether the request is cross-origin may be expensive, so please
+   * do that in cases where we're going to use this information later on.
+   */
+  bool IsCrossOriginRequest(nsIHttpChannel* aChannel) const;
+
+  /*
+   * Check whether referrer is allowed to send in secure to insecure scenario.
+   */
+  nsresult HandleSecureToInsecureReferral(nsIURI* aURI, bool& aAllowed) const;
+
+  /*
+   * Handle user controlled pref network.http.referer.XOriginPolicy
+   */
+  nsresult HandleUserXOriginSendingPolicy(nsIURI* aURI, nsIURI* aReferrer,
+                                          bool& aAllowed) const;
+
+  /*
+   * Handle user controlled pref network.http.sendRefererHeader
+   */
+  nsresult HandleUserReferrerSendingPolicy(nsIHttpChannel* aChannel,
+                                           bool& aAllowed) const;
+
+  /*
+   * Compute trimming policy from user controlled prefs.
+   * This function is called when we already made sure a nonempty referrer is
+   * allowed to send.
+   */
+  TrimmingPolicy ComputeTrimmingPolicy(nsIHttpChannel* aChannel) const;
+
+  // HttpBaseChannel could access IsInitialized() and ComputeReferrer();
+  friend class mozilla::net::HttpBaseChannel;
+
+  /*
+   * Compute referrer for a given channel. The computation result then will be
+   * stored in this class and then used to set the actual referrer header of
+   * the channel. The computation could be controlled by sereral user prefs
+   * which is defined in all.js (see all.js for more details):
+   *  network.http.sendRefererHeader
+   *  network.http.referer.spoofSource
+   *  network.http.referer.hideOnionSource
+   *  network.http.referer.XOriginPolicy
+   *  network.http.referer.trimmingPolicy
+   *  network.http.referer.XOriginTrimmingPolicy
+   */
+  nsresult ComputeReferrer(nsIHttpChannel* aChannel);
+
+  /*
+   * Check whether the ReferrerInfo has been initialized or not.
+   */
+  bool IsInitialized() { return mInitialized; }
+
+  // nsHttpChannel could access IsPolicyOverrided();
+  friend class mozilla::net::nsHttpChannel;
+  /*
+   * Check whether if unset referrer policy is overrided by default or not
+   */
+  bool IsPolicyOverrided() { return mOverridePolicyByDefault; }
+
+  /*
+   * Trim a given referrer with a given a trimming policy,
+   */
+  nsresult TrimReferrerWithPolicy(nsCOMPtr<nsIURI>& aReferrer,
+                                  TrimmingPolicy aTrimmingPolicy,
+                                  nsACString& aResult) const;
+
   nsCOMPtr<nsIURI> mOriginalReferrer;
+
   uint32_t mPolicy;
 
   // Indicates if the referrer should be sent or not even when it's available
   // (default is true).
   bool mSendReferrer;
+
+  // Since the ReferrerInfo is immutable, we use this member as a helper to
+  // ensure no one can call e.g. init() twice to modify state of the
+  // ReferrerInfo.
+  bool mInitialized;
+
+  // Indicates if unset referrer policy is overrided by default
+  bool mOverridePolicyByDefault;
+
+  // Store a computed referrer for a given channel
+  Maybe<nsCString> mComputedReferrer;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // mozilla_dom_ReferrerInfo_h
--- a/dom/security/moz.build
+++ b/dom/security/moz.build
@@ -50,16 +50,17 @@ UNIFIED_SOURCES += [
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '/caps',
     '/docshell/base',  # for nsDocShell.h
     '/netwerk/base',
     '/netwerk/protocol/data', # for nsDataHandler.h
+    '/netwerk/protocol/http', # for HttpBaseChannel.h
 ]
 
 include('/tools/fuzzing/libfuzzer-config.mozbuild')
 
 if CONFIG['FUZZING_INTERFACES']:
     TEST_DIRS += [
         'fuzztest'
     ]
--- a/dom/serviceworkers/ServiceWorkerPrivate.cpp
+++ b/dom/serviceworkers/ServiceWorkerPrivate.cpp
@@ -39,16 +39,17 @@
 #include "mozilla/dom/WorkerRef.h"
 #include "mozilla/dom/WorkerRunnable.h"
 #include "mozilla/dom/WorkerScope.h"
 #include "mozilla/dom/ipc/StructuredCloneData.h"
 #include "mozilla/net/CookieSettings.h"
 #include "mozilla/net/NeckoChannelParams.h"
 #include "mozilla/StaticPrefs.h"
 #include "mozilla/Unused.h"
+#include "nsIReferrerInfo.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 namespace mozilla {
 namespace dom {
 
 using mozilla::ipc::PrincipalInfo;
@@ -1249,31 +1250,27 @@ class FetchEventRunnable : public Extend
     rv = channel->GetLoadFlags(&loadFlags);
     NS_ENSURE_SUCCESS(rv, rv);
     nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
     mContentPolicyType = loadInfo->InternalContentPolicyType();
 
     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.
-    Unused << 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();
+    mReferrer = EmptyCString();
+    uint32_t referrerPolicy = 0;
+    nsCOMPtr<nsIReferrerInfo> referrerInfo = httpChannel->GetReferrerInfo();
+    if (referrerInfo) {
+      referrerPolicy = referrerInfo->GetReferrerPolicy();
+      nsCOMPtr<nsIURI> computedReferrer = referrerInfo->GetComputedReferrer();
+      if (computedReferrer) {
+        rv = computedReferrer->GetSpec(mReferrer);
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
     }
-
-    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:
--- a/dom/webbrowserpersist/nsWebBrowserPersist.cpp
+++ b/dom/webbrowserpersist/nsWebBrowserPersist.cpp
@@ -59,16 +59,17 @@
 #include "WebBrowserPersistLocalDocument.h"
 
 #include "nsIContent.h"
 #include "nsIMIMEInfo.h"
 #include "mozilla/dom/HTMLInputElement.h"
 #include "mozilla/dom/HTMLSharedElement.h"
 #include "mozilla/net/CookieSettings.h"
 #include "mozilla/Printf.h"
+#include "ReferrerInfo.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 // Buffer file writes in 32kb chunks
 #define BUFFERED_OUTPUT_SIZE (1024 * 32)
 
 struct nsWebBrowserPersist::WalkData {
@@ -1268,17 +1269,19 @@ nsresult nsWebBrowserPersist::SaveURIInt
     }
   }
 
   // Set the referrer, post data and headers if any
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(inputChannel));
   if (httpChannel) {
     // Referrer
     if (aReferrer) {
-      rv = httpChannel->SetReferrerWithPolicy(aReferrer, aReferrerPolicy);
+      nsCOMPtr<nsIReferrerInfo> referrerInfo =
+          new ReferrerInfo(aReferrer, aReferrerPolicy);
+      rv = httpChannel->SetReferrerInfoWithoutClone(referrerInfo);
       MOZ_ASSERT(NS_SUCCEEDED(rv));
     }
 
     // Post data
     if (aPostData) {
       nsCOMPtr<nsISeekableStream> stream(do_QueryInterface(aPostData));
       if (stream) {
         // Rewind the postdata stream
--- a/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp
+++ b/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp
@@ -33,16 +33,17 @@
 #include "nsIScriptError.h"
 #include "nsIURL.h"
 #include "nsError.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/Text.h"
 #include "mozilla/Encoding.h"
 #include "mozilla/UniquePtr.h"
+#include "ReferrerInfo.h"
 
 using namespace mozilla;
 using mozilla::net::ReferrerPolicy;
 
 static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
 
 static void getSpec(nsIChannel* aChannel, nsAString& aSpec) {
   if (!aChannel) {
@@ -410,17 +411,19 @@ nsresult txCompileObserver::startLoad(ns
   channel->SetContentType(NS_LITERAL_CSTRING("text/xml"));
 
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
   if (httpChannel) {
     nsCOMPtr<nsIURI> referrerURI;
     aReferrerPrincipal->GetURI(getter_AddRefs(referrerURI));
     if (referrerURI) {
       DebugOnly<nsresult> rv;
-      rv = httpChannel->SetReferrerWithPolicy(referrerURI, aReferrerPolicy);
+      nsCOMPtr<nsIReferrerInfo> referrerInfo =
+          new dom::ReferrerInfo(referrerURI, aReferrerPolicy);
+      rv = httpChannel->SetReferrerInfoWithoutClone(referrerInfo);
       MOZ_ASSERT(NS_SUCCEEDED(rv));
     }
   }
 
   nsCOMPtr<nsIParser> parser = do_CreateInstance(kCParserCID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   RefPtr<txStylesheetSink> sink = new txStylesheetSink(aCompiler, parser);
--- a/image/imgLoader.cpp
+++ b/image/imgLoader.cpp
@@ -52,16 +52,17 @@
 #include "nsIApplicationCache.h"
 #include "nsIApplicationCacheContainer.h"
 
 #include "nsIMemoryReporter.h"
 #include "DecoderFactory.h"
 #include "Image.h"
 #include "gfxPrefs.h"
 #include "prtime.h"
+#include "ReferrerInfo.h"
 
 // we want to explore making the document own the load group
 // so we can associate the document URI with the load group.
 // until this point, we have an evil hack:
 #include "nsIHttpChannelInternal.h"
 #include "nsILoadContext.h"
 #include "nsILoadGroupChild.h"
 #include "nsIDocShell.h"
@@ -879,17 +880,19 @@ static nsresult NewImageChannel(
                                           aAcceptHeader, false);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
 
     nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
         do_QueryInterface(newHttpChannel);
     NS_ENSURE_TRUE(httpChannelInternal, NS_ERROR_UNEXPECTED);
     rv = httpChannelInternal->SetDocumentURI(aInitialDocumentURI);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
-    rv = newHttpChannel->SetReferrerWithPolicy(aReferringURI, aReferrerPolicy);
+    nsCOMPtr<nsIReferrerInfo> referrerInfo =
+        new ReferrerInfo(aReferringURI, aReferrerPolicy);
+    rv = newHttpChannel->SetReferrerInfoWithoutClone(referrerInfo);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
   }
 
   // Image channels are loaded by default with reduced priority.
   nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(*aResult);
   if (p) {
     uint32_t priority = nsISupportsPriority::PRIORITY_LOW;
 
--- a/layout/style/FontFaceSet.cpp
+++ b/layout/style/FontFaceSet.cpp
@@ -50,16 +50,17 @@
 #include "nsNetUtil.h"
 #include "nsIProtocolHandler.h"
 #include "nsIInputStream.h"
 #include "nsLayoutUtils.h"
 #include "nsPresContext.h"
 #include "nsPrintfCString.h"
 #include "nsUTF8Utils.h"
 #include "nsDOMNavigationTiming.h"
+#include "ReferrerInfo.h"
 
 using namespace mozilla;
 using namespace mozilla::css;
 using namespace mozilla::dom;
 
 #define LOG(args) \
   MOZ_LOG(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug, args)
 #define LOG_ENABLED() \
@@ -613,18 +614,19 @@ nsresult FontFaceSet::StartLoad(gfxUserF
          fontLoader.get(), aFontFaceSrc->mURI->GetSpecOrDefault().get(),
          aFontFaceSrc->mReferrer
              ? aFontFaceSrc->mReferrer->GetSpecOrDefault().get()
              : ""));
   }
 
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
   if (httpChannel) {
-    rv = httpChannel->SetReferrerWithPolicy(aFontFaceSrc->mReferrer,
-                                            aFontFaceSrc->mReferrerPolicy);
+    nsCOMPtr<nsIReferrerInfo> referrerInfo = new mozilla::dom::ReferrerInfo(
+        aFontFaceSrc->mReferrer, aFontFaceSrc->mReferrerPolicy);
+    rv = httpChannel->SetReferrerInfoWithoutClone(referrerInfo);
     Unused << NS_WARN_IF(NS_FAILED(rv));
 
     nsAutoCString accept("application/font-woff;q=0.9,*/*;q=0.8");
     if (Preferences::GetBool(GFX_PREF_WOFF2_ENABLED)) {
       accept.InsertLiteral("application/font-woff2;q=1.0,", 0);
     }
     rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"), accept,
                                        false);
--- a/layout/style/Loader.cpp
+++ b/layout/style/Loader.cpp
@@ -49,16 +49,17 @@
 #include "mozilla/net/UrlClassifierFeatureFactory.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/ServoBindings.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/ConsoleReportCollector.h"
 #include "mozilla/ServoUtils.h"
 #include "mozilla/css/StreamLoader.h"
+#include "ReferrerInfo.h"
 
 #ifdef MOZ_XUL
 #  include "nsXULPrototypeCache.h"
 #endif
 
 #include "nsError.h"
 
 #include "nsIContentSecurityPolicy.h"
@@ -1449,18 +1450,19 @@ nsresult Loader::LoadSheet(SheetLoadData
       cos->AddClassFlags(nsIClassOfService::Leader);
     }
   }
 
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
   if (httpChannel) {
     nsCOMPtr<nsIURI> referrerURI = aLoadData->GetReferrerURI();
     if (referrerURI) {
-      rv = httpChannel->SetReferrerWithPolicy(
+      nsCOMPtr<nsIReferrerInfo> referrerInfo = new mozilla::dom::ReferrerInfo(
           referrerURI, aLoadData->mSheet->GetReferrerPolicy());
+      rv = httpChannel->SetReferrerInfoWithoutClone(referrerInfo);
       Unused << NS_WARN_IF(NS_FAILED(rv));
     }
 
     nsCOMPtr<nsIHttpChannelInternal> internalChannel =
         do_QueryInterface(httpChannel);
     if (internalChannel) {
       rv = internalChannel->SetIntegrityMetadata(
           sriMetadata.GetIntegrityString());
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -2247,28 +2247,31 @@ void PeerConnectionImpl::ShutdownMedia()
   // Forget the reference so that we can transfer it to
   // SelfDestruct().
   mMedia.forget().take()->SelfDestruct();
 }
 
 void PeerConnectionImpl::SetSignalingState_m(
     PCImplSignalingState aSignalingState, bool rollback) {
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
-  if (mSignalingState == aSignalingState ||
-      mSignalingState == PCImplSignalingState::SignalingClosed) {
+  if (mSignalingState == PCImplSignalingState::SignalingClosed) {
     return;
   }
 
   if (aSignalingState == PCImplSignalingState::SignalingHaveLocalOffer ||
       (aSignalingState == PCImplSignalingState::SignalingStable &&
        mSignalingState == PCImplSignalingState::SignalingHaveRemoteOffer &&
        !rollback)) {
     mMedia->EnsureTransports(*mJsepSession);
   }
 
+  if (mSignalingState == aSignalingState) {
+    return;
+  }
+
   mSignalingState = aSignalingState;
 
   if (mSignalingState == PCImplSignalingState::SignalingStable) {
     // If we're rolling back a local offer, we might need to remove some
     // transports, and stomp some MediaPipeline setup, but nothing further
     // needs to be done.
     mMedia->UpdateTransports(*mJsepSession, mForceIceTcp);
     if (NS_FAILED(mMedia->UpdateMediaPipelines())) {
--- a/mfbt/CheckedInt.h
+++ b/mfbt/CheckedInt.h
@@ -527,17 +527,17 @@ class CheckedInt {
   /** Constructs a valid checked integer with initial value 0 */
   constexpr CheckedInt() : mValue(0), mIsValid(true) {
     static_assert(detail::IsSupported<T>::value,
                   "This type is not supported by CheckedInt");
   }
 
   /** @returns the actual value */
   T value() const {
-    MOZ_RELEASE_ASSERT(
+    MOZ_DIAGNOSTIC_ASSERT(
         mIsValid,
         "Invalid checked integer (division by zero or integer overflow)");
     return mValue;
   }
 
   /**
    * @returns true if the checked integer is valid, i.e. is not the result
    * of an invalid operation or of an operation involving an invalid checked
--- a/netwerk/base/Predictor.cpp
+++ b/netwerk/base/Predictor.cpp
@@ -48,16 +48,17 @@
 #include "mozilla/ipc/URIUtils.h"
 #include "SerializedLoadContext.h"
 #include "mozilla/net/NeckoChild.h"
 
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/ClearOnShutdown.h"
 
 #include "CacheControlParser.h"
+#include "ReferrerInfo.h"
 
 using namespace mozilla;
 
 namespace mozilla {
 namespace net {
 
 Predictor* Predictor::sSelf = nullptr;
 
@@ -1270,17 +1271,18 @@ nsresult Predictor::Prefetch(nsIURI* uri
 
   nsCOMPtr<nsIHttpChannel> httpChannel;
   httpChannel = do_QueryInterface(channel);
   if (!httpChannel) {
     PREDICTOR_LOG(("    Could not get HTTP Channel from new channel!"));
     return NS_ERROR_UNEXPECTED;
   }
 
-  rv = httpChannel->SetReferrer(referrer);
+  nsCOMPtr<nsIReferrerInfo> referrerInfo = new ReferrerInfo(referrer);
+  rv = httpChannel->SetReferrerInfoWithoutClone(referrerInfo);
   NS_ENSURE_SUCCESS(rv, rv);
   // XXX - set a header here to indicate this is a prefetch?
 
   nsCOMPtr<nsIStreamListener> listener =
       new PrefetchListener(verifier, uri, this);
   PREDICTOR_LOG(("    calling AsyncOpen listener=%p channel=%p", listener.get(),
                  channel.get()));
   rv = channel->AsyncOpen(listener);
--- a/netwerk/base/nsNetUtil.cpp
+++ b/netwerk/base/nsNetUtil.cpp
@@ -96,26 +96,16 @@
 
 using namespace mozilla;
 using namespace mozilla::net;
 using mozilla::dom::BlobURLProtocolHandler;
 using mozilla::dom::ClientInfo;
 using mozilla::dom::PerformanceStorage;
 using mozilla::dom::ServiceWorkerDescriptor;
 
-#define DEFAULT_RP 3
-#define DEFAULT_TRACKER_RP 3
-#define DEFAULT_PRIVATE_RP 2
-#define DEFAULT_TRACKER_PRIVATE_RP 2
-
-static uint32_t sDefaultRp = DEFAULT_RP;
-static uint32_t sDefaultTrackerRp = DEFAULT_TRACKER_RP;
-static uint32_t defaultPrivateRp = DEFAULT_PRIVATE_RP;
-static uint32_t defaultTrackerPrivateRp = DEFAULT_TRACKER_PRIVATE_RP;
-
 already_AddRefed<nsIIOService> do_GetIOService(nsresult* error /* = 0 */) {
   nsCOMPtr<nsIIOService> io = mozilla::services::GetIOService();
   if (error) *error = io ? NS_OK : NS_ERROR_FAILURE;
   return io.forget();
 }
 
 nsresult NS_NewLocalFileInputStream(nsIInputStream** result, nsIFile* file,
                                     int32_t ioFlags /* = -1 */,
@@ -917,68 +907,59 @@ nsresult NS_NewStreamLoader(
 
 nsresult NS_NewStreamLoaderInternal(
     nsIStreamLoader** outStream, nsIURI* aUri,
     nsIStreamLoaderObserver* aObserver, nsINode* aLoadingNode,
     nsIPrincipal* aLoadingPrincipal, nsSecurityFlags aSecurityFlags,
     nsContentPolicyType aContentPolicyType,
     nsILoadGroup* aLoadGroup /* = nullptr */,
     nsIInterfaceRequestor* aCallbacks /* = nullptr */,
-    nsLoadFlags aLoadFlags /* = nsIRequest::LOAD_NORMAL */,
-    nsIURI* aReferrer /* = nullptr */) {
+    nsLoadFlags aLoadFlags /* = nsIRequest::LOAD_NORMAL */) {
   nsCOMPtr<nsIChannel> channel;
   nsresult rv = NS_NewChannelInternal(
       getter_AddRefs(channel), aUri, aLoadingNode, aLoadingPrincipal,
       nullptr,  // aTriggeringPrincipal
       Maybe<ClientInfo>(), Maybe<ServiceWorkerDescriptor>(), aSecurityFlags,
       aContentPolicyType,
       nullptr,  // nsICookieSettings
       nullptr,  // PerformanceStorage
       aLoadGroup, aCallbacks, aLoadFlags);
 
   NS_ENSURE_SUCCESS(rv, rv);
-  nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
-  if (httpChannel) {
-    rv = httpChannel->SetReferrer(aReferrer);
-    MOZ_ASSERT(NS_SUCCEEDED(rv));
-  }
   rv = NS_NewStreamLoader(outStream, aObserver);
   NS_ENSURE_SUCCESS(rv, rv);
   return channel->AsyncOpen(*outStream);
 }
 
 nsresult NS_NewStreamLoader(
     nsIStreamLoader** outStream, nsIURI* aUri,
     nsIStreamLoaderObserver* aObserver, nsINode* aLoadingNode,
     nsSecurityFlags aSecurityFlags, nsContentPolicyType aContentPolicyType,
     nsILoadGroup* aLoadGroup /* = nullptr */,
     nsIInterfaceRequestor* aCallbacks /* = nullptr */,
-    nsLoadFlags aLoadFlags /* = nsIRequest::LOAD_NORMAL */,
-    nsIURI* aReferrer /* = nullptr */) {
+    nsLoadFlags aLoadFlags /* = nsIRequest::LOAD_NORMAL */) {
   NS_ASSERTION(aLoadingNode,
                "Can not create stream loader without a loading Node!");
   return NS_NewStreamLoaderInternal(
       outStream, aUri, aObserver, aLoadingNode, aLoadingNode->NodePrincipal(),
-      aSecurityFlags, aContentPolicyType, aLoadGroup, aCallbacks, aLoadFlags,
-      aReferrer);
+      aSecurityFlags, aContentPolicyType, aLoadGroup, aCallbacks, aLoadFlags);
 }
 
 nsresult NS_NewStreamLoader(
     nsIStreamLoader** outStream, nsIURI* aUri,
     nsIStreamLoaderObserver* aObserver, nsIPrincipal* aLoadingPrincipal,
     nsSecurityFlags aSecurityFlags, nsContentPolicyType aContentPolicyType,
     nsILoadGroup* aLoadGroup /* = nullptr */,
     nsIInterfaceRequestor* aCallbacks /* = nullptr */,
-    nsLoadFlags aLoadFlags /* = nsIRequest::LOAD_NORMAL */,
-    nsIURI* aReferrer /* = nullptr */) {
+    nsLoadFlags aLoadFlags /* = nsIRequest::LOAD_NORMAL */) {
   return NS_NewStreamLoaderInternal(outStream, aUri, aObserver,
                                     nullptr,  // aLoadingNode
                                     aLoadingPrincipal, aSecurityFlags,
                                     aContentPolicyType, aLoadGroup, aCallbacks,
-                                    aLoadFlags, aReferrer);
+                                    aLoadFlags);
 }
 
 nsresult NS_NewSyncStreamListener(nsIStreamListener** result,
                                   nsIInputStream** stream) {
   nsCOMPtr<nsISyncStreamListener> listener = nsSyncStreamListener::Create();
   if (listener) {
     nsresult rv = listener->GetInputStream(stream);
     if (NS_SUCCEEDED(rv)) {
@@ -1117,41 +1098,47 @@ nsresult NS_GetURLSpecFromDir(nsIFile* f
                               nsIIOService* ioService /* = nullptr */) {
   nsresult rv;
   nsCOMPtr<nsIFileProtocolHandler> fileHandler;
   rv = NS_GetFileProtocolHandler(getter_AddRefs(fileHandler), ioService);
   if (NS_SUCCEEDED(rv)) rv = fileHandler->GetURLSpecFromDir(file, url);
   return rv;
 }
 
-nsresult NS_GetReferrerFromChannel(nsIChannel* channel, nsIURI** referrer) {
-  nsresult rv = NS_ERROR_NOT_AVAILABLE;
+void NS_GetReferrerFromChannel(nsIChannel* channel, nsIURI** referrer) {
   *referrer = nullptr;
 
   nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(channel));
   if (props) {
     // We have to check for a property on a property bag because the
     // referrer may be empty for security reasons (for example, when loading
     // an http page with an https referrer).
-    rv = props->GetPropertyAsInterface(
+    nsresult rv = props->GetPropertyAsInterface(
         NS_LITERAL_STRING("docshell.internalReferrer"), NS_GET_IID(nsIURI),
         reinterpret_cast<void**>(referrer));
     if (NS_FAILED(rv)) *referrer = nullptr;
   }
 
+  if (*referrer) {
+    return;
+  }
+
   // if that didn't work, we can still try to get the referrer from the
   // nsIHttpChannel (if we can QI to it)
-  if (!(*referrer)) {
-    nsCOMPtr<nsIHttpChannel> chan(do_QueryInterface(channel));
-    if (chan) {
-      rv = chan->GetReferrer(referrer);
-      if (NS_FAILED(rv)) *referrer = nullptr;
-    }
+  nsCOMPtr<nsIHttpChannel> chan(do_QueryInterface(channel));
+  if (!chan) {
+    return;
   }
-  return rv;
+
+  nsCOMPtr<nsIReferrerInfo> referrerInfo = chan->GetReferrerInfo();
+  if (!referrerInfo) {
+    return;
+  }
+
+  referrerInfo->GetOriginalReferrer(referrer);
 }
 
 already_AddRefed<nsINetUtil> do_GetNetUtil(nsresult* error /* = 0 */) {
   nsCOMPtr<nsIIOService> io = mozilla::services::GetIOService();
   nsCOMPtr<nsINetUtil> util;
   if (io) util = do_QueryInterface(io);
 
   if (error) *error = !!util ? NS_OK : NS_ERROR_FAILURE;
@@ -2940,77 +2927,16 @@ nsresult NS_CompareLoadInfoAndLoadContex
   MOZ_ASSERT(originAttrsLoadInfo.mPrivateBrowsingId ==
                  originAttrsLoadContext.mPrivateBrowsingId,
              "The value of mPrivateBrowsingId in the loadContext and in the "
              "loadInfo are not the same!");
 
   return NS_OK;
 }
 
-uint32_t NS_GetDefaultReferrerPolicy(nsIHttpChannel* aChannel, nsIURI* aURI,
-                                     bool privateBrowsing) {
-  static bool preferencesInitialized = false;
-
-  if (!preferencesInitialized) {
-    mozilla::Preferences::AddUintVarCache(
-        &sDefaultRp, "network.http.referer.defaultPolicy", DEFAULT_RP);
-    mozilla::Preferences::AddUintVarCache(
-        &sDefaultTrackerRp, "network.http.referer.defaultPolicy.trackers",
-        DEFAULT_TRACKER_RP);
-    mozilla::Preferences::AddUintVarCache(
-        &defaultPrivateRp, "network.http.referer.defaultPolicy.pbmode",
-        DEFAULT_PRIVATE_RP);
-    mozilla::Preferences::AddUintVarCache(
-        &defaultTrackerPrivateRp,
-        "network.http.referer.defaultPolicy.trackers.pbmode",
-        DEFAULT_TRACKER_PRIVATE_RP);
-    preferencesInitialized = true;
-  }
-
-  bool thirdPartyTrackerIsolated = false;
-  if (StaticPrefs::network_cookie_cookieBehavior() ==
-      nsICookieService::BEHAVIOR_REJECT_TRACKER) {
-    if (aChannel && aURI) {
-      uint32_t rejectedReason = 0;
-      thirdPartyTrackerIsolated =
-          !AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(
-              aChannel, aURI, &rejectedReason);
-      // Here we intentionally do not notify about the rejection reason, if any
-      // in order to avoid this check to have any visible side-effects (e.g. a
-      // web console report.)
-    }
-  }
-
-  uint32_t defaultToUse;
-  if (thirdPartyTrackerIsolated) {
-    if (privateBrowsing) {
-      defaultToUse = defaultTrackerPrivateRp;
-    } else {
-      defaultToUse = sDefaultTrackerRp;
-    }
-  } else {
-    if (privateBrowsing) {
-      defaultToUse = defaultPrivateRp;
-    } else {
-      defaultToUse = sDefaultRp;
-    }
-  }
-
-  switch (defaultToUse) {
-    case 0:
-      return nsIHttpChannel::REFERRER_POLICY_NO_REFERRER;
-    case 1:
-      return nsIHttpChannel::REFERRER_POLICY_SAME_ORIGIN;
-    case 2:
-      return nsIHttpChannel::REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN;
-  }
-
-  return nsIHttpChannel::REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE;
-}
-
 nsresult NS_SetRequestBlockingReason(nsIChannel *channel, uint32_t reason) {
   NS_ENSURE_ARG(channel);
 
   nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
   return NS_SetRequestBlockingReason(loadInfo, reason);
 }
 
 nsresult NS_SetRequestBlockingReason(nsILoadInfo *loadInfo, uint32_t reason) {
--- a/netwerk/base/nsNetUtil.h
+++ b/netwerk/base/nsNetUtil.h
@@ -371,38 +371,35 @@ nsresult NS_NewIncrementalStreamLoader(
     nsIIncrementalStreamLoaderObserver* observer);
 
 nsresult NS_NewStreamLoaderInternal(
     nsIStreamLoader** outStream, nsIURI* aUri,
     nsIStreamLoaderObserver* aObserver, nsINode* aLoadingNode,
     nsIPrincipal* aLoadingPrincipal, nsSecurityFlags aSecurityFlags,
     nsContentPolicyType aContentPolicyType, nsILoadGroup* aLoadGroup = nullptr,
     nsIInterfaceRequestor* aCallbacks = nullptr,
-    nsLoadFlags aLoadFlags = nsIRequest::LOAD_NORMAL,
-    nsIURI* aReferrer = nullptr);
+    nsLoadFlags aLoadFlags = nsIRequest::LOAD_NORMAL);
 
 nsresult NS_NewStreamLoader(nsIStreamLoader** outStream, nsIURI* aUri,
                             nsIStreamLoaderObserver* aObserver,
                             nsINode* aLoadingNode,
                             nsSecurityFlags aSecurityFlags,
                             nsContentPolicyType aContentPolicyType,
                             nsILoadGroup* aLoadGroup = nullptr,
                             nsIInterfaceRequestor* aCallbacks = nullptr,
-                            nsLoadFlags aLoadFlags = nsIRequest::LOAD_NORMAL,
-                            nsIURI* aReferrer = nullptr);
+                            nsLoadFlags aLoadFlags = nsIRequest::LOAD_NORMAL);
 
 nsresult NS_NewStreamLoader(nsIStreamLoader** outStream, nsIURI* aUri,
                             nsIStreamLoaderObserver* aObserver,
                             nsIPrincipal* aLoadingPrincipal,
                             nsSecurityFlags aSecurityFlags,
                             nsContentPolicyType aContentPolicyType,
                             nsILoadGroup* aLoadGroup = nullptr,
                             nsIInterfaceRequestor* aCallbacks = nullptr,
-                            nsLoadFlags aLoadFlags = nsIRequest::LOAD_NORMAL,
-                            nsIURI* aReferrer = nullptr);
+                            nsLoadFlags aLoadFlags = nsIRequest::LOAD_NORMAL);
 
 nsresult NS_NewSyncStreamListener(nsIStreamListener** result,
                                   nsIInputStream** stream);
 
 /**
  * Implement the nsIChannel::Open(nsIInputStream**) method using the channel's
  * AsyncOpen method.
  *
@@ -457,19 +454,18 @@ nsresult NS_GetURLSpecFromActualFile(nsI
 nsresult NS_GetURLSpecFromDir(nsIFile* file, nsACString& url,
                               nsIIOService* ioService = nullptr);
 
 /**
  * Obtains the referrer for a given channel.  This first tries to obtain the
  * referrer from the property docshell.internalReferrer, and if that doesn't
  * work and the channel is an nsIHTTPChannel, we check it's referrer property.
  *
- * @returns NS_ERROR_NOT_AVAILABLE if no referrer is available.
  */
-nsresult NS_GetReferrerFromChannel(nsIChannel* channel, nsIURI** referrer);
+void NS_GetReferrerFromChannel(nsIChannel* channel, nsIURI** referrer);
 
 nsresult NS_ParseRequestContentType(const nsACString& rawContentType,
                                     nsCString& contentType,
                                     nsCString& contentCharset);
 
 nsresult NS_ParseResponseContentType(const nsACString& rawContentType,
                                      nsCString& contentType,
                                      nsCString& contentCharset);
@@ -921,30 +917,16 @@ nsresult NS_ShouldSecureUpgrade(
 /**
  * Returns an https URI for channels that need to go through secure upgrades.
  */
 nsresult NS_GetSecureUpgradedURI(nsIURI* aURI, nsIURI** aUpgradedURI);
 
 nsresult NS_CompareLoadInfoAndLoadContext(nsIChannel* aChannel);
 
 /**
- * Return default referrer policy which is controlled by user
- * prefs:
- * network.http.referer.defaultPolicy for regular mode
- * network.http.referer.defaultPolicy.trackers for third-party trackers
- * in regular mode
- * network.http.referer.defaultPolicy.pbmode for private mode
- * network.http.referer.defaultPolicy.trackers.pbmode for third-party trackers
- * in private mode
- */
-uint32_t NS_GetDefaultReferrerPolicy(nsIHttpChannel* aChannel = nullptr,
-                                     nsIURI* aURI = nullptr,
-                                     bool privateBrowsing = false);
-
-/**
  * Return true if this channel should be classified by the URL classifier.
  */
 bool NS_ShouldClassifyChannel(nsIChannel* aChannel);
 
 /**
  * Helper to set the blocking reason on loadinfo of the channel.
  */
 nsresult NS_SetRequestBlockingReason(nsIChannel *channel, uint32_t reason);
--- a/netwerk/ipc/NeckoChannelParams.ipdlh
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -211,16 +211,17 @@ struct CorsPreflightArgs
 struct HttpChannelOpenArgs
 {
   URIParams                   uri;
   // - TODO: bug 571161: unclear if any HTTP channel clients ever
   // set originalURI != uri (about:credits?); also not clear if
   // chrome channel would ever need to know.  Get rid of next arg?
   URIParams?                  original;
   URIParams?                  doc;
+  nsIReferrerInfo             referrerInfo;
   URIParams?                  originalReferrer;
   uint32_t                    originalReferrerPolicy;
   uint32_t                    referrerPolicy;
   URIParams?                  apiRedirectTo;
   URIParams?                  topWindowURI;
   uint32_t                    loadFlags;
   RequestHeaderTuples         requestHeaders;
   nsCString                   requestMethod;
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -72,16 +72,18 @@
 #include "nsIMIMEInputStream.h"
 #include "nsIXULRuntime.h"
 #include "nsICacheInfoChannel.h"
 #include "nsIDOMWindowUtils.h"
 #include "nsIURIFixup.h"
 #include "nsHttpChannel.h"
 #include "nsRedirectHistoryEntry.h"
 #include "nsServerTiming.h"
+#include "nsIURIMutator.h"
+#include "mozilla/Tokenizer.h"
 
 #include <algorithm>
 #include "HttpBaseChannel.h"
 
 namespace mozilla {
 namespace net {
 
 static bool IsHeaderBlacklistedForRedirectCopy(nsHttpAtom const& aHeader) {
@@ -206,18 +208,16 @@ HttpBaseChannel::HttpBaseChannel()
       mAddedAsNonTailRequest(false),
       mAsyncOpenWaitingForStreamLength(false),
       mUpgradableToSecure(true),
       mTlsFlags(0),
       mSuspendCount(0),
       mInitialRwin(0),
       mProxyResolveFlags(0),
       mContentDispositionHint(UINT32_MAX),
-      mOriginalReferrerPolicy(NS_GetDefaultReferrerPolicy()),
-      mReferrerPolicy(mOriginalReferrerPolicy),
       mCorsMode(nsIHttpChannelInternal::CORS_MODE_NO_CORS),
       mRedirectMode(nsIHttpChannelInternal::REDIRECT_MODE_FOLLOW),
       mLastRedirectFlags(0),
       mPriority(PRIORITY_NORMAL),
       mRedirectionLimit(gHttpHandler->RedirectionLimit()),
       mRedirectCount(0),
       mInternalRedirectCount(0),
       mAsyncOpenTimeOverriden(false),
@@ -286,17 +286,16 @@ void HttpBaseChannel::ReleaseMainThreadO
   nsTArray<nsCOMPtr<nsISupports>> arrayToRelease;
   arrayToRelease.AppendElement(mURI.forget());
   arrayToRelease.AppendElement(mOriginalURI.forget());
   arrayToRelease.AppendElement(mDocumentURI.forget());
   arrayToRelease.AppendElement(mLoadGroup.forget());
   arrayToRelease.AppendElement(mLoadInfo.forget());
   arrayToRelease.AppendElement(mCallbacks.forget());
   arrayToRelease.AppendElement(mProgressSink.forget());
-  arrayToRelease.AppendElement(mReferrer.forget());
   arrayToRelease.AppendElement(mApplicationCache.forget());
   arrayToRelease.AppendElement(mAPIRedirectToURI.forget());
   arrayToRelease.AppendElement(mProxyURI.forget());
   arrayToRelease.AppendElement(mPrincipal.forget());
   arrayToRelease.AppendElement(mTopWindowURI.forget());
   arrayToRelease.AppendElement(mListener.forget());
   arrayToRelease.AppendElement(mCompressListener.forget());
 
@@ -1565,361 +1564,80 @@ HttpBaseChannel::SetRequestMethod(const 
   // Method names are restricted to valid HTTP tokens.
   if (!nsHttp::IsValidToken(flatMethod)) return NS_ERROR_INVALID_ARG;
 
   mRequestHead.SetMethod(flatMethod);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-HttpBaseChannel::GetReferrer(nsIURI** referrer) {
-  NS_ENSURE_ARG_POINTER(referrer);
-  *referrer = mReferrer;
-  NS_IF_ADDREF(*referrer);
+HttpBaseChannel::GetReferrerInfo(nsIReferrerInfo** aReferrerInfo) {
+  NS_ENSURE_ARG_POINTER(aReferrerInfo);
+  *aReferrerInfo = do_AddRef(mReferrerInfo).take();
   return NS_OK;
 }
 
-NS_IMETHODIMP
-HttpBaseChannel::SetReferrer(nsIURI* referrer) {
-  bool isPrivate = mLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
-  return SetReferrerWithPolicy(
-      referrer, NS_GetDefaultReferrerPolicy(this, mURI, isPrivate));
-}
-
-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));
+nsresult HttpBaseChannel::SetReferrerInfo(nsIReferrerInfo* aReferrerInfo,
+                                          bool aClone, bool aCompute) {
+  ENSURE_CALLED_BEFORE_CONNECT();
+
+  mReferrerInfo = aReferrerInfo;
+
+  // clear existing referrer, if any
+  nsresult rv = ClearReferrerHeader();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (!mReferrerInfo) {
+    return NS_OK;
+  }
+
+  if (aClone) {
+    mReferrerInfo = static_cast<dom::ReferrerInfo*>(aReferrerInfo)->Clone();
+  }
+
+  dom::ReferrerInfo* referrerInfo =
+      static_cast<dom::ReferrerInfo*>(mReferrerInfo.get());
+
+  // Don't set referrerInfo if it has not been initialized.
+  if (!referrerInfo->IsInitialized()) {
+    mReferrerInfo = nullptr;
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+
+  if (aCompute) {
+    rv = referrerInfo->ComputeReferrer(this);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
     }
   }
-  if (triggeringURI) {
-    if (LOG_ENABLED()) {
-      nsAutoCString triggeringURISpec;
-      triggeringURI->GetAsciiSpec(triggeringURISpec);
-      LOG(("triggeringURI=%s\n", triggeringURISpec.get()));
-    }
-    nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
-    bool isPrivateWin = mLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
-    rv = ssm->CheckSameOriginURI(triggeringURI, mURI, false, isPrivateWin);
-    return (NS_FAILED(rv));
+
+  nsCOMPtr<nsIURI> computedReferrer = mReferrerInfo->GetComputedReferrer();
+  if (!computedReferrer) {
+    return NS_OK;
   }
 
-  LOG(
-      ("no triggering principal available via loadInfo, assuming load is "
-       "cross-origin"));
-  return true;
+  nsAutoCString spec;
+  rv = computedReferrer->GetSpec(spec);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return SetReferrerHeader(spec);
 }
 
 NS_IMETHODIMP
-HttpBaseChannel::SetReferrerWithPolicy(nsIURI* referrer,
-                                       uint32_t referrerPolicy) {
-  ENSURE_CALLED_BEFORE_CONNECT();
-
-  nsIURI* originalReferrer = referrer;
-  uint32_t originalReferrerPolicy = referrerPolicy;
-
-  mReferrerPolicy = referrerPolicy;
-
-  // clear existing referrer, if any
-  mReferrer = nullptr;
-  nsresult rv = mRequestHead.ClearHeader(nsHttp::Referer);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  if (mReferrerPolicy == REFERRER_POLICY_UNSET) {
-    bool isPrivate = mLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
-    mReferrerPolicy = NS_GetDefaultReferrerPolicy(this, mURI, isPrivate);
-  }
-
-  if (!referrer) {
-    return NS_OK;
-  }
-
-  // Don't send referrer at all when the meta referrer setting is "no-referrer"
-  if (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();
-
-  // 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: 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) {
-    referrerLevel = 1;  // user action
-  } else {
-    referrerLevel = 2;  // inline content
-  }
-  if (userReferrerLevel < referrerLevel) {
-    return NS_OK;
-  }
-
-  nsCOMPtr<nsIURI> referrerGrip;
-  bool match;
-
-  // Enforce Referrer whitelist
-  if (!IsReferrerSchemeAllowed(referrer)) {
-    return NS_OK;  // kick out....
-  }
-
-  //
-  // Handle secure referrals.
-  //
-  // Support referrals from a secure server if this is a secure site
-  // and (optionally) if the host names are the same.
-  //
-  rv = referrer->SchemeIs("https", &match);
-  if (NS_FAILED(rv)) return rv;
-
-  if (match) {
-    rv = mURI->SchemeIs("https", &match);
-    if (NS_FAILED(rv)) return rv;
-
-    // It's ok to send referrer for https-to-http scenarios if the referrer
-    // policy is "unsafe-url", "origin", or "origin-when-cross-origin".
-    if (mReferrerPolicy != REFERRER_POLICY_UNSAFE_URL &&
-        mReferrerPolicy != REFERRER_POLICY_ORIGIN_WHEN_XORIGIN &&
-        mReferrerPolicy != REFERRER_POLICY_ORIGIN) {
-      // in other referrer policies, https->http is not allowed...
-      if (!match) return NS_OK;
-    }
-  }
-
-  nsCOMPtr<nsIURI> clone;
-  //
-  // we need to clone the referrer, so we can:
-  //  (1) modify it
-  //  (2) keep a reference to it after returning from this function
-  //
-  // Strip away any fragment per RFC 2616 section 14.36
-  // and Referrer Policy section 6.3.5.
-  rv = NS_GetURIWithoutRef(referrer, getter_AddRefs(clone));
-  if (NS_FAILED(rv)) return rv;
-
-  nsAutoCString currentHost;
-  nsAutoCString referrerHost;
-
-  rv = mURI->GetAsciiHost(currentHost);
-  if (NS_FAILED(rv)) return rv;
-
-  rv = clone->GetAsciiHost(referrerHost);
-  if (NS_FAILED(rv)) return rv;
-
-  // Send an empty referrer if leaving a .onion domain.
-  if (userHideOnionReferrerSource && !currentHost.Equals(referrerHost) &&
-      StringEndsWith(referrerHost, NS_LITERAL_CSTRING(".onion"))) {
-    return NS_OK;
-  }
-
-  // check policy for sending ref only when hosts match
-  if (userReferrerXOriginPolicy == 2 && !currentHost.Equals(referrerHost))
-    return NS_OK;
-
-  if (userReferrerXOriginPolicy == 1) {
-    nsAutoCString currentDomain = currentHost;
-    nsAutoCString referrerDomain = referrerHost;
-    uint32_t extraDomains = 0;
-    nsCOMPtr<nsIEffectiveTLDService> eTLDService =
-        do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
-    if (eTLDService) {
-      rv = eTLDService->GetBaseDomain(mURI, extraDomains, currentDomain);
-      if (rv == NS_ERROR_HOST_IS_IP_ADDRESS ||
-          rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
-        // mURI is either an IP address, an alias such as 'localhost', an eTLD
-        // such as 'co.uk', or the empty string. Uses the normalized host in
-        // such cases.
-        rv = mURI->GetAsciiHost(currentDomain);
-      }
-      if (NS_FAILED(rv)) {
-        return rv;
-      }
-      rv = eTLDService->GetBaseDomain(clone, extraDomains, referrerDomain);
-      if (rv == NS_ERROR_HOST_IS_IP_ADDRESS ||
-          rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
-        // clone is either an IP address, an alias such as 'localhost', an eTLD
-        // such as 'co.uk', or the empty string. Uses the normalized host in
-        // such cases.
-        rv = clone->GetAsciiHost(referrerDomain);
-      }
-      if (NS_FAILED(rv)) {
-        return rv;
-      }
-    }
-
-    // check policy for sending only when effective top level domain matches.
-    // this falls back on using host if eTLDService does not work
-    if (!currentDomain.Equals(referrerDomain)) return NS_OK;
-  }
-
-  // send spoofed referrer if desired
-  if (userSpoofReferrerSource) {
-    nsCOMPtr<nsIURI> mURIclone;
-    rv = NS_GetURIWithoutRef(mURI, getter_AddRefs(mURIclone));
-    if (NS_FAILED(rv)) return rv;
-    clone = mURIclone;
-    currentHost = referrerHost;
-  }
-
-  // strip away any userpass; we don't want to be giving out passwords ;-)
-  // This is required by Referrer Policy stripping algorithm.
-  nsCOMPtr<nsIURIFixup> urifixup = services::GetURIFixup();
-  if (NS_WARN_IF(!urifixup)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsCOMPtr<nsIURI> exposableURI;
-  rv = urifixup->CreateExposableURI(clone, getter_AddRefs(exposableURI));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  clone = exposableURI;
-
-  // 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;
-      }
-      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);
-        }
-      }
-
-      break;
-
-    case REFERRER_POLICY_NO_REFERRER:
-    case REFERRER_POLICY_UNSET:
-    default:
-      MOZ_ASSERT_UNREACHABLE("Unexpected value");
-      break;
-  }
-
-  nsAutoCString spec;
-  // 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);
-    if (NS_FAILED(rv)) return rv;
-    spec = scheme;
-    spec.AppendLiteral("://");
-    // Note we explicitly cleared UserPass above, so do not need to build it.
-    rv = clone->GetAsciiHostPort(asciiHostPort);
-    if (NS_FAILED(rv)) return rv;
-    spec.Append(asciiHostPort);
-
-    switch (userReferrerTrimmingPolicy) {
-      case 1: {  // scheme+host+port+path
-        nsCOMPtr<nsIURL> url(do_QueryInterface(clone));
-        if (url) {
-          nsAutoCString path;
-          rv = url->GetFilePath(path);
-          if (NS_FAILED(rv)) return rv;
-          spec.Append(path);
-          rv = NS_MutateURI(url)
-                   .SetQuery(EmptyCString())
-                   .SetRef(EmptyCString())
-                   .Finalize(clone);
-          if (NS_FAILED(rv)) return rv;
-          break;
-        }
-        // No URL, so fall through to truncating the path and any query/ref off
-        // as well.
-      }
-        MOZ_FALLTHROUGH;
-      default
-          :    // (Pref limited to [0,2] enforced by clamp, MOZ_CRASH overkill.)
-      case 2:  // scheme+host+port+/
-        spec.AppendLiteral("/");
-        // This nukes any query/ref present as well in the case of nsStandardURL
-        rv =
-            NS_MutateURI(clone).SetPathQueryRef(EmptyCString()).Finalize(clone);
-        if (NS_FAILED(rv)) return rv;
-        break;
-    }
-  } else {
-    // use the full URI
-    rv = clone->GetAsciiSpec(spec);
-    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;
-
-  mOriginalReferrerPolicy = originalReferrerPolicy;
-  mOriginalReferrer = originalReferrer;
-  mReferrer = clone;
-  return NS_OK;
+HttpBaseChannel::SetReferrerInfo(nsIReferrerInfo* aReferrerInfo) {
+  return SetReferrerInfo(aReferrerInfo, true, true);
+}
+
+NS_IMETHODIMP
+HttpBaseChannel::SetReferrerInfoWithoutClone(nsIReferrerInfo* aReferrerInfo) {
+  return SetReferrerInfo(aReferrerInfo, false, true);
 }
 
 // Return the channel's proxy URI, or if it doesn't exist, the
 // channel's main URI.
 NS_IMETHODIMP
 HttpBaseChannel::GetProxyURI(nsIURI** aOut) {
   NS_ENSURE_ARG_POINTER(aOut);
   nsCOMPtr<nsIURI> result(mProxyURI);
@@ -3198,18 +2916,23 @@ already_AddRefed<nsILoadInfo> HttpBaseCh
   newLoadInfo->SetResultPrincipalURI(nullptr);
 
   bool isInternalRedirect =
       (redirectFlags & (nsIChannelEventSink::REDIRECT_INTERNAL |
                         nsIChannelEventSink::REDIRECT_STS_UPGRADE));
 
   nsCString remoteAddress;
   Unused << GetRemoteAddress(remoteAddress);
+  nsCOMPtr<nsIURI> referrer;
+  if (mReferrerInfo) {
+    referrer = mReferrerInfo->GetComputedReferrer();
+  }
+
   nsCOMPtr<nsIRedirectHistoryEntry> entry =
-      new nsRedirectHistoryEntry(GetURIPrincipal(), mReferrer, remoteAddress);
+      new nsRedirectHistoryEntry(GetURIPrincipal(), referrer, remoteAddress);
 
   newLoadInfo->AppendRedirectHistoryEntry(entry, isInternalRedirect);
 
   return newLoadInfo.forget();
 }
 
 //-----------------------------------------------------------------------------
 // nsHttpChannel::nsITraceableChannel
@@ -3329,31 +3052,16 @@ void HttpBaseChannel::AddCookiesToReques
   }
 
   // If we are in the child process, we want the parent seeing any
   // cookie headers that might have been set by SetRequestHeader()
   SetRequestHeader(nsDependentCString(nsHttp::Cookie), cookie, false);
 }
 
 /* static */
-bool HttpBaseChannel::IsReferrerSchemeAllowed(nsIURI* aReferrer) {
-  NS_ENSURE_TRUE(aReferrer, false);
-
-  nsAutoCString scheme;
-  nsresult rv = aReferrer->GetScheme(scheme);
-  NS_ENSURE_SUCCESS(rv, false);
-
-  if (scheme.EqualsIgnoreCase("https") || scheme.EqualsIgnoreCase("http") ||
-      scheme.EqualsIgnoreCase("ftp")) {
-    return true;
-  }
-  return false;
-}
-
-/* static */
 void HttpBaseChannel::PropagateReferenceIfNeeded(
     nsIURI* aURI, nsCOMPtr<nsIURI>& aRedirectURI) {
   bool hasRef = false;
   nsresult rv = aRedirectURI->GetHasRef(&hasRef);
   if (NS_SUCCEEDED(rv) && !hasRef) {
     nsAutoCString ref;
     aURI->GetRef(ref);
     if (!ref.IsEmpty()) {
@@ -3502,21 +3210,22 @@ nsresult HttpBaseChannel::SetupReplaceme
     // we set the upload stream above. This means SetRequestMethod() will
     // be called twice if ExplicitSetUploadStream() gets called above.
 
     nsAutoCString method;
     mRequestHead.Method(method);
     rv = httpChannel->SetRequestMethod(method);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
   }
-  // convey the referrer if one was used for this channel to the next one
-  if (mReferrer) {
-    rv = httpChannel->SetReferrerWithPolicy(mReferrer, mReferrerPolicy);
+
+  if (mReferrerInfo) {
+    rv = httpChannel->SetReferrerInfo(mReferrerInfo);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
   }
+
   // convey the mAllowSTS flags
   rv = httpChannel->SetAllowSTS(mAllowSTS);
   MOZ_ASSERT(NS_SUCCEEDED(rv));
 
   // convey the Accept header value
   {
     nsAutoCString oldAcceptValue;
     nsresult hasHeader = mRequestHead.GetHeader(nsHttp::Accept, oldAcceptValue);
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -47,16 +47,17 @@
 #include "mozilla/net/ChannelEventQueue.h"
 #include "mozilla/Move.h"
 #include "mozilla/Tuple.h"
 #include "nsIThrottledInputChannel.h"
 #include "nsTArray.h"
 #include "nsCOMPtr.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "nsStringEnumerator.h"
+#include "mozilla/dom/ReferrerInfo.h"
 
 #define HTTP_BASE_CHANNEL_IID                        \
   {                                                  \
     0x9d5cde03, 0xe6e9, 0x4612, {                    \
       0xbf, 0xef, 0xbb, 0x66, 0xf3, 0xbb, 0x74, 0x46 \
     }                                                \
   }
 
@@ -178,21 +179,20 @@ class HttpBaseChannel : public nsHashPro
   NS_IMETHOD GetContentEncodings(nsIUTF8StringEnumerator** aEncodings) override;
   NS_IMETHOD DoApplyContentConversions(nsIStreamListener* aNextListener,
                                        nsIStreamListener** aNewNextListener,
                                        nsISupports* aCtxt) override;
 
   // HttpBaseChannel::nsIHttpChannel
   NS_IMETHOD GetRequestMethod(nsACString& aMethod) override;
   NS_IMETHOD SetRequestMethod(const nsACString& aMethod) override;
-  NS_IMETHOD GetReferrer(nsIURI** referrer) override;
-  NS_IMETHOD SetReferrer(nsIURI* referrer) override;
-  NS_IMETHOD GetReferrerPolicy(uint32_t* referrerPolicy) override;
-  NS_IMETHOD SetReferrerWithPolicy(nsIURI* referrer,
-                                   uint32_t referrerPolicy) override;
+  NS_IMETHOD GetReferrerInfo(nsIReferrerInfo** aReferrerInfo) override;
+  NS_IMETHOD SetReferrerInfo(nsIReferrerInfo* aReferrerInfo) override;
+  NS_IMETHOD SetReferrerInfoWithoutClone(
+      nsIReferrerInfo* aReferrerInfo) override;
   NS_IMETHOD GetRequestHeader(const nsACString& aHeader,
                               nsACString& aValue) override;
   NS_IMETHOD SetRequestHeader(const nsACString& aHeader,
                               const nsACString& aValue, bool aMerge) override;
   NS_IMETHOD SetEmptyRequestHeader(const nsACString& aHeader) override;
   NS_IMETHOD VisitRequestHeaders(nsIHttpHeaderVisitor* visitor) override;
   NS_IMETHOD VisitNonDefaultRequestHeaders(
       nsIHttpHeaderVisitor* visitor) override;
@@ -399,18 +399,16 @@ class HttpBaseChannel : public nsHashPro
   MOZ_MUST_USE nsresult OverrideSecurityInfo(nsISupports* aSecurityInfo);
 
  public: /* Necko internal use only... */
   int64_t GetAltDataLength() { return mAltDataLength; }
   bool IsNavigation();
 
   bool IsDeliveringAltData() const { return mDeliveringAltData; }
 
-  static bool IsReferrerSchemeAllowed(nsIURI* aReferrer);
-
   static void PropagateReferenceIfNeeded(nsIURI* aURI,
                                          nsCOMPtr<nsIURI>& aRedirectURI);
 
   // Return whether upon a redirect code of httpStatus for method, the
   // request method should be rewritten to GET.
   static bool ShouldRewriteRedirectToGET(
       uint32_t httpStatus, nsHttpRequestHead::ParsedMethodType method);
 
@@ -437,37 +435,35 @@ class HttpBaseChannel : public nsHashPro
   void InternalSetUploadStreamLength(uint64_t aLength) {
     mReqContentLength = aLength;
   }
 
   void SetUploadStreamHasHeaders(bool hasHeaders) {
     mUploadStreamHasHeaders = hasHeaders;
   }
 
-  MOZ_MUST_USE nsresult SetReferrerWithPolicyInternal(
-      nsIURI* referrer, uint32_t originalReferrerPolicy,
-      uint32_t referrerPolicy) {
-    nsAutoCString spec;
-    nsresult rv = referrer->GetAsciiSpec(spec);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-    mOriginalReferrer = referrer;
-    mReferrer = referrer;
-    mOriginalReferrerPolicy = originalReferrerPolicy;
-    mReferrerPolicy = referrerPolicy;
-    rv = mRequestHead.SetHeader(nsHttp::Referer, spec);
-    return rv;
+  virtual nsresult SetReferrerHeader(const nsACString& aReferrer) {
+    ENSURE_CALLED_BEFORE_CONNECT();
+    return mRequestHead.SetHeader(nsHttp::Referer, aReferrer);
+  }
+
+  nsresult ClearReferrerHeader() {
+    ENSURE_CALLED_BEFORE_CONNECT();
+    return mRequestHead.ClearHeader(nsHttp::Referer);
   }
 
   MOZ_MUST_USE nsresult SetTopWindowURI(nsIURI* aTopWindowURI) {
     mTopWindowURI = aTopWindowURI;
     return NS_OK;
   }
 
+  // Set referrerInfo and compute the referrer header if neccessary.
+  nsresult SetReferrerInfo(nsIReferrerInfo* aReferrerInfo, bool aClone,
+                           bool aCompute);
+
  protected:
   nsresult GetTopWindowURI(nsIURI* aURIBeingLoaded, nsIURI** aTopWindowURI);
 
   // Handle notifying listener, removing from loadgroup if request failed.
   void DoNotifyListener();
   virtual void DoNotifyListenerCleanup() = 0;
 
   // drop reference to listener, its callbacks, and the progress sink
@@ -549,37 +545,30 @@ class HttpBaseChannel : public nsHashPro
   // all the references need to be proxy released on main thread.
   nsCOMPtr<nsIURI> mURI;
   nsCOMPtr<nsIURI> mOriginalURI;
   nsCOMPtr<nsIURI> mDocumentURI;
   nsCOMPtr<nsILoadGroup> mLoadGroup;
   nsCOMPtr<nsILoadInfo> mLoadInfo;
   nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
   nsCOMPtr<nsIProgressEventSink> mProgressSink;
-  // When the referrer is set before calling AsyncOpen, we remember the referrer
-  // argument passed in case we need to readjust the referrer header being used
-  // depending on the active cookie policy once we have determined whether the
-  // channel is a tracking third-party resource or not.
-  nsCOMPtr<nsIURI> mOriginalReferrer;
-  nsCOMPtr<nsIURI> mReferrer;
+  nsCOMPtr<nsIReferrerInfo> mReferrerInfo;
   nsCOMPtr<nsIApplicationCache> mApplicationCache;
   nsCOMPtr<nsIURI> mAPIRedirectToURI;
   nsCOMPtr<nsIURI> mProxyURI;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsCOMPtr<nsIURI> mTopWindowURI;
   nsCOMPtr<nsIStreamListener> mListener;
   // An instance of nsHTTPCompressConv
   nsCOMPtr<nsIStreamListener> mCompressListener;
 
  private:
   // Proxy release all members above on main thread.
   void ReleaseMainThreadOnlyReferences();
 
-  bool IsCrossOriginWithReferrer();
-
   nsresult ExplicitSetUploadStreamLength(uint64_t aContentLength,
                                          bool aStreamHasHeaders);
 
   void MaybeResumeAsyncOpen();
 
  protected:
   nsCString mSpec;  // ASCII encoded URL spec
   nsCString mContentTypeHint;
@@ -745,18 +734,16 @@ class HttpBaseChannel : public nsHashPro
   uint32_t mSuspendCount;
 
   // Per channel transport window override (0 means no override)
   uint32_t mInitialRwin;
 
   uint32_t mProxyResolveFlags;
 
   uint32_t mContentDispositionHint;
-  uint32_t mOriginalReferrerPolicy;
-  uint32_t mReferrerPolicy;
 
   uint32_t mCorsMode;
   uint32_t mRedirectMode;
 
   // If this channel was created as the result of a redirect, then this value
   // will reflect the redirect flags passed to the SetupReplacementChannel()
   // method.
   uint32_t mLastRedirectFlags;
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -2014,18 +2014,23 @@ mozilla::ipc::IPCResult HttpChannelChild
 void HttpChannelChild::CleanupRedirectingChannel(nsresult rv) {
   // Redirecting to new channel: shut this down and init new channel
   if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, NS_BINDING_ABORTED);
 
   if (NS_SUCCEEDED(rv)) {
     if (mLoadInfo) {
       nsCString remoteAddress;
       Unused << GetRemoteAddress(remoteAddress);
+      nsCOMPtr<nsIURI> referrer;
+      if (mReferrerInfo) {
+        referrer = mReferrerInfo->GetComputedReferrer();
+      }
+
       nsCOMPtr<nsIRedirectHistoryEntry> entry = new nsRedirectHistoryEntry(
-          GetURIPrincipal(), mReferrer, remoteAddress);
+          GetURIPrincipal(), referrer, remoteAddress);
 
       mLoadInfo->AppendRedirectHistoryEntry(entry, false);
     }
   } else {
     NS_WARNING("CompleteRedirectSetup failed, HttpChannelChild already open?");
   }
 
   // Release ref to new channel.
@@ -2180,45 +2185,35 @@ HttpChannelChild::CompleteRedirectSetup(
 
 NS_IMETHODIMP
 HttpChannelChild::OnRedirectVerifyCallback(nsresult result) {
   LOG(("HttpChannelChild::OnRedirectVerifyCallback [this=%p]\n", this));
   MOZ_ASSERT(NS_IsMainThread());
   Maybe<URIParams> redirectURI;
   nsresult rv;
 
-  uint32_t referrerPolicy = REFERRER_POLICY_UNSET;
-  Maybe<URIParams> 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
     // to let nsHttpChannel::ContinueProcessResponse2 know it's redirecting to
     // another protocol and throw an error.
     LOG(("  redirecting to a protocol that doesn't implement nsIChildChannel"));
     result = NS_ERROR_DOM_BAD_URI;
   }
 
+  nsCOMPtr<nsIReferrerInfo> referrerInfo;
   if (newHttpChannel) {
     // Must not be called until after redirect observers called.
     newHttpChannel->SetOriginalURI(mOriginalURI);
-
-    rv = newHttpChannel->GetReferrerPolicy(&referrerPolicy);
-    MOZ_ASSERT(NS_SUCCEEDED(rv));
-    nsCOMPtr<nsIURI> newChannelReferrerURI;
-    rv = newHttpChannel->GetReferrer(getter_AddRefs(newChannelReferrerURI));
-    MOZ_ASSERT(NS_SUCCEEDED(rv));
-
-    SerializeURI(newChannelReferrerURI, referrerURI);
+    referrerInfo = newHttpChannel->GetReferrerInfo();
   }
 
   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
@@ -2303,18 +2298,18 @@ HttpChannelChild::OnRedirectVerifyCallba
     newChannelLoadInfo = newChannel->LoadInfo();
   }
 
   ChildLoadInfoForwarderArgs loadInfoForwarder;
   LoadInfoToChildLoadInfoForwarder(newChannelLoadInfo, &loadInfoForwarder);
 
   if (mIPCOpen)
     SendRedirect2Verify(result, *headerTuples, loadInfoForwarder, loadFlags,
-                        referrerPolicy, referrerURI, redirectURI,
-                        corsPreflightArgs, chooseAppcache);
+                        referrerInfo, redirectURI, corsPreflightArgs,
+                        chooseAppcache);
 
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // HttpChannelChild::nsIRequest
 //-----------------------------------------------------------------------------
 
@@ -2705,24 +2700,22 @@ nsresult HttpChannelChild::ContinueAsync
   SetTopLevelContentWindowId(contentWindowId);
 
   HttpChannelOpenArgs openArgs;
   // No access to HttpChannelOpenArgs members, but they each have a
   // function with the struct name that returns a ref.
   SerializeURI(mURI, openArgs.uri());
   SerializeURI(mOriginalURI, openArgs.original());
   SerializeURI(mDocumentURI, openArgs.doc());
-  SerializeURI(mOriginalReferrer, openArgs.originalReferrer());
-  openArgs.originalReferrerPolicy() = mOriginalReferrerPolicy;
-  openArgs.referrerPolicy() = mReferrerPolicy;
   SerializeURI(mAPIRedirectToURI, openArgs.apiRedirectTo());
   openArgs.loadFlags() = mLoadFlags;
   openArgs.requestHeaders() = mClientSetRequestHeaders;
   mRequestHead.Method(openArgs.requestMethod());
   openArgs.preferredAlternativeTypes() = mPreferredCachedAltDataTypes;
+  openArgs.referrerInfo() = mReferrerInfo;
 
   AutoIPCStream autoStream(openArgs.uploadStream());
   if (mUploadStream) {
     autoStream.Serialize(mUploadStream, ContentChild::GetSingleton());
     autoStream.TakeOptionalValue();
   }
 
   if (mResponseHead) {
@@ -2874,34 +2867,16 @@ nsresult HttpChannelChild::ContinueAsync
   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;
 
   RequestHeaderTuple* tuple = mClientSetRequestHeaders.AppendElement();
   if (!tuple) return NS_ERROR_OUT_OF_MEMORY;
@@ -3651,16 +3626,30 @@ nsresult HttpChannelChild::AsyncCallImpl
 
   if (NS_SUCCEEDED(rv) && retval) {
     *retval = event;
   }
 
   return rv;
 }
 
+nsresult HttpChannelChild::SetReferrerHeader(const nsACString& aReferrer) {
+  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);
+    }
+  }
+
+  return HttpBaseChannel::SetReferrerHeader(aReferrer);
+}
+
 class CancelEvent final : public NeckoTargetChannelEvent<HttpChannelChild> {
  public:
   CancelEvent(HttpChannelChild* aChild, nsresult aRv)
       : NeckoTargetChannelEvent<HttpChannelChild>(aChild), mRv(aRv) {
     MOZ_ASSERT(!NS_IsMainThread());
     MOZ_ASSERT(aChild);
   }
 
--- a/netwerk/protocol/http/HttpChannelChild.h
+++ b/netwerk/protocol/http/HttpChannelChild.h
@@ -90,18 +90,16 @@ class HttpChannelChild final : public PH
   NS_IMETHOD Cancel(nsresult status) override;
   NS_IMETHOD Suspend() override;
   NS_IMETHOD Resume() override;
   // nsIChannel
   NS_IMETHOD GetSecurityInfo(nsISupports** aSecurityInfo) override;
   NS_IMETHOD AsyncOpen(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 UpgradeToSecure() override;
   NS_IMETHOD GetProtocolVersion(nsACString& aProtocolVersion) override;
   // nsIHttpChannelInternal
   NS_IMETHOD SetupFallbackChannel(const char* aFallbackKey) override;
@@ -115,16 +113,18 @@ class HttpChannelChild final : public PH
   NS_IMETHOD ResumeAt(uint64_t startPos, const nsACString& entityID) override;
 
   // IPDL holds a reference while the PHttpChannel protocol is live (starting at
   // AsyncOpen, and ending at either OnStopRequest or any IPDL error, either of
   // which call NeckoChild::DeallocPHttpChannelChild()).
   void AddIPDLReference();
   void ReleaseIPDLReference();
 
+  nsresult SetReferrerHeader(const nsACString& aReferrer) override;
+
   MOZ_MUST_USE bool IsSuspended();
 
   void FlushedForDiversion();
 
   void OnCopyComplete(nsresult aStatus) override;
 
   // Callback while background channel is ready.
   void OnBackgroundChildReady(HttpBackgroundChannelChild* aBgChild);
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -128,18 +128,17 @@ void HttpChannelParent::ActorDestroy(Act
 
 bool HttpChannelParent::Init(const HttpChannelCreationArgs& aArgs) {
   LOG(("HttpChannelParent::Init [this=%p]\n", this));
   AUTO_PROFILER_LABEL("HttpChannelParent::Init", NETWORK);
   switch (aArgs.type()) {
     case HttpChannelCreationArgs::THttpChannelOpenArgs: {
       const HttpChannelOpenArgs& a = aArgs.get_HttpChannelOpenArgs();
       return DoAsyncOpen(
-          a.uri(), a.original(), a.doc(), a.originalReferrer(),
-          a.originalReferrerPolicy(), a.referrerPolicy(), a.apiRedirectTo(),
+          a.uri(), a.original(), a.doc(), a.referrerInfo(), a.apiRedirectTo(),
           a.topWindowURI(), a.loadFlags(), a.requestHeaders(),
           a.requestMethod(), a.uploadStream(), a.uploadStreamHasHeaders(),
           a.priority(), a.classOfService(), a.redirectionLimit(), a.allowSTS(),
           a.thirdPartyFlags(), a.resumeAt(), a.startPos(), a.entityID(),
           a.chooseApplicationCache(), a.appCacheClientID(), a.allowSpdy(),
           a.allowAltSvc(), a.beConservative(), a.tlsFlags(), a.loadInfo(),
           a.synthesizedResponseHead(), a.synthesizedSecurityInfoSerialization(),
           a.cacheKey(), a.requestContextID(), a.preflightArgs(),
@@ -372,19 +371,17 @@ void HttpChannelParent::InvokeAsyncOpen(
   rv = mChannel->AsyncOpen(mParentListener);
   if (NS_FAILED(rv)) {
     AsyncOpenFailed(rv);
   }
 }
 
 bool HttpChannelParent::DoAsyncOpen(
     const URIParams& aURI, const Maybe<URIParams>& aOriginalURI,
-    const Maybe<URIParams>& aDocURI,
-    const Maybe<URIParams>& aOriginalReferrerURI,
-    const uint32_t& aOriginalReferrerPolicy, const uint32_t& aReferrerPolicy,
+    const Maybe<URIParams>& aDocURI, nsIReferrerInfo* aReferrerInfo,
     const Maybe<URIParams>& aAPIRedirectToURI,
     const Maybe<URIParams>& aTopWindowURI, const uint32_t& aLoadFlags,
     const RequestHeaderTuples& requestHeaders, const nsCString& requestMethod,
     const Maybe<IPCStream>& uploadStream, const bool& uploadStreamHasHeaders,
     const int16_t& priority, const uint32_t& classOfService,
     const uint8_t& redirectionLimit, const bool& allowSTS,
     const uint32_t& thirdPartyFlags, const bool& doResumeAt,
     const uint64_t& startPos, const nsCString& entityID,
@@ -415,17 +412,16 @@ bool HttpChannelParent::DoAsyncOpen(
   nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
   if (!uri) {
     // URIParams does MOZ_ASSERT if null, but we need to protect opt builds from
     // null deref here.
     return false;
   }
   nsCOMPtr<nsIURI> originalUri = DeserializeURI(aOriginalURI);
   nsCOMPtr<nsIURI> docUri = DeserializeURI(aDocURI);
-  nsCOMPtr<nsIURI> referrerUri = DeserializeURI(aOriginalReferrerURI);
   nsCOMPtr<nsIURI> apiRedirectToUri = DeserializeURI(aAPIRedirectToURI);
   nsCOMPtr<nsIURI> topWindowUri = DeserializeURI(aTopWindowURI);
 
   LOG(("HttpChannelParent RecvAsyncOpen [this=%p uri=%s, gid=%" PRIu64
        " topwinid=%" PRIx64 "]\n",
        this, uri->GetSpecOrDefault().get(), aChannelId,
        aTopLevelOuterContentWindowId));
 
@@ -472,21 +468,22 @@ bool HttpChannelParent::DoAsyncOpen(
   if (mPBOverride != kPBOverride_Unset) {
     httpChannel->SetPrivate(mPBOverride == kPBOverride_Private ? true : false);
   }
 
   if (doResumeAt) httpChannel->ResumeAt(startPos, entityID);
 
   if (originalUri) httpChannel->SetOriginalURI(originalUri);
   if (docUri) httpChannel->SetDocumentURI(docUri);
-  if (referrerUri) {
-    rv = httpChannel->SetReferrerWithPolicyInternal(
-        referrerUri, aOriginalReferrerPolicy, aReferrerPolicy);
+  if (aReferrerInfo) {
+    // Referrer header is computed in child no need to recompute here
+    rv = httpChannel->SetReferrerInfo(aReferrerInfo, false, false);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
   }
+
   if (apiRedirectToUri) httpChannel->RedirectTo(apiRedirectToUri);
   if (topWindowUri) {
     rv = httpChannel->SetTopWindowURI(topWindowUri);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
   }
 
   if (aLoadFlags != nsIRequest::LOAD_NORMAL)
     httpChannel->SetLoadFlags(aLoadFlags);
@@ -832,18 +829,17 @@ mozilla::ipc::IPCResult HttpChannelParen
     const nsCString& charset) {
   if (mCacheEntry) mCacheEntry->SetMetaDataElement("charset", charset.get());
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult HttpChannelParent::RecvRedirect2Verify(
     const nsresult& aResult, const RequestHeaderTuples& changedHeaders,
     const ChildLoadInfoForwarderArgs& aLoadInfoForwarder,
-    const uint32_t& loadFlags, const uint32_t& referrerPolicy,
-    const Maybe<URIParams>& aReferrerURI,
+    const uint32_t& loadFlags, nsIReferrerInfo* aReferrerInfo,
     const Maybe<URIParams>& aAPIRedirectURI,
     const Maybe<CorsPreflightArgs>& aCorsPreflightArgs,
     const bool& aChooseAppcache) {
   LOG(("HttpChannelParent::RecvRedirect2Verify [this=%p result=%" PRIx32 "]\n",
        this, static_cast<uint32_t>(aResult)));
 
   // Result from the child.  If something fails here, we might overwrite a
   // success with a further failure.
@@ -884,19 +880,25 @@ mozilla::ipc::IPCResult HttpChannelParen
       if (aCorsPreflightArgs.isSome()) {
         nsCOMPtr<nsIHttpChannelInternal> newInternalChannel =
             do_QueryInterface(newHttpChannel);
         MOZ_RELEASE_ASSERT(newInternalChannel);
         const CorsPreflightArgs& args = aCorsPreflightArgs.ref();
         newInternalChannel->SetCorsPreflightParameters(args.unsafeHeaders());
       }
 
-      nsCOMPtr<nsIURI> referrerUri = DeserializeURI(aReferrerURI);
-      rv = newHttpChannel->SetReferrerWithPolicy(referrerUri, referrerPolicy);
-      MOZ_ASSERT(NS_SUCCEEDED(rv));
+      if (aReferrerInfo) {
+        RefPtr<HttpBaseChannel> baseChannel = do_QueryObject(newHttpChannel);
+        MOZ_ASSERT(baseChannel);
+        if (baseChannel) {
+          // Referrer header is computed in child no need to recompute here
+          rv = baseChannel->SetReferrerInfo(aReferrerInfo, false, false);
+          MOZ_ASSERT(NS_SUCCEEDED(rv));
+        }
+      }
 
       nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
           do_QueryInterface(newHttpChannel);
       if (appCacheChannel) {
         appCacheChannel->SetChooseApplicationCache(aChooseAppcache);
       }
 
       nsCOMPtr<nsILoadInfo> newLoadInfo = newHttpChannel->LoadInfo();
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParent.h
@@ -134,19 +134,17 @@ class HttpChannelParent final : public n
  protected:
   // used to connect redirected-to channel in parent with just created
   // ChildChannel.  Used during redirects.
   MOZ_MUST_USE bool ConnectChannel(const uint32_t& channelId,
                                    const bool& shouldIntercept);
 
   MOZ_MUST_USE bool DoAsyncOpen(
       const URIParams& uri, const Maybe<URIParams>& originalUri,
-      const Maybe<URIParams>& docUri,
-      const Maybe<URIParams>& originalReferrerUri,
-      const uint32_t& originalReferrerPolicy, const uint32_t& referrerPolicy,
+      const Maybe<URIParams>& docUri, nsIReferrerInfo* aReferrerInfo,
       const Maybe<URIParams>& internalRedirectUri,
       const Maybe<URIParams>& topWindowUri, const uint32_t& loadFlags,
       const RequestHeaderTuples& requestHeaders, const nsCString& requestMethod,
       const Maybe<IPCStream>& uploadStream, const bool& uploadStreamHasHeaders,
       const int16_t& priority, const uint32_t& classOfService,
       const uint8_t& redirectionLimit, const bool& allowSTS,
       const uint32_t& thirdPartyFlags, const bool& doResumeAt,
       const uint64_t& startPos, const nsCString& entityID,
@@ -183,18 +181,17 @@ class HttpChannelParent final : public n
   virtual mozilla::ipc::IPCResult RecvSetCacheTokenCachedCharset(
       const nsCString& charset) override;
   virtual mozilla::ipc::IPCResult RecvSuspend() override;
   virtual mozilla::ipc::IPCResult RecvResume() override;
   virtual mozilla::ipc::IPCResult RecvCancel(const nsresult& status) override;
   virtual mozilla::ipc::IPCResult RecvRedirect2Verify(
       const nsresult& result, const RequestHeaderTuples& changedHeaders,
       const ChildLoadInfoForwarderArgs& aLoadInfoForwarder,
-      const uint32_t& loadFlags, const uint32_t& referrerPolicy,
-      const Maybe<URIParams>& aReferrerURI,
+      const uint32_t& loadFlags, nsIReferrerInfo* aReferrerInfo,
       const Maybe<URIParams>& apiRedirectUri,
       const Maybe<CorsPreflightArgs>& aCorsPreflightArgs,
       const bool& aChooseAppcache) override;
   virtual mozilla::ipc::IPCResult RecvDocumentChannelCleanup(
       const bool& clearCacheEntry) override;
   virtual mozilla::ipc::IPCResult RecvMarkOfflineCacheEntryAsForeign() override;
   virtual mozilla::ipc::IPCResult RecvDivertOnDataAvailable(
       const nsCString& data, const uint64_t& offset,
--- a/netwerk/protocol/http/NullHttpChannel.cpp
+++ b/netwerk/protocol/http/NullHttpChannel.cpp
@@ -132,33 +132,27 @@ NullHttpChannel::GetRequestMethod(nsACSt
 }
 
 NS_IMETHODIMP
 NullHttpChannel::SetRequestMethod(const nsACString& aRequestMethod) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
-NullHttpChannel::GetReferrer(nsIURI** aReferrer) {
+NullHttpChannel::GetReferrerInfo(nsIReferrerInfo** aReferrerInfo) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
-NullHttpChannel::SetReferrer(nsIURI* aReferrer) {
+NullHttpChannel::SetReferrerInfo(nsIReferrerInfo* aReferrerInfo) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
-NullHttpChannel::GetReferrerPolicy(uint32_t* aReferrerPolicy) {
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-NS_IMETHODIMP
-NullHttpChannel::SetReferrerWithPolicy(nsIURI* referrer,
-                                       uint32_t referrerPolicy) {
+NullHttpChannel::SetReferrerInfoWithoutClone(nsIReferrerInfo* aReferrerInfo) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 NullHttpChannel::GetRequestHeader(const nsACString& aHeader,
                                   nsACString& _retval) {
   _retval.Truncate();
   return NS_ERROR_NOT_IMPLEMENTED;
--- a/netwerk/protocol/http/PHttpChannel.ipdl
+++ b/netwerk/protocol/http/PHttpChannel.ipdl
@@ -14,16 +14,17 @@ include NeckoChannelParams;
 include IPCServiceWorkerDescriptor;
 include IPCStream;
 
 include "mozilla/net/NeckoMessageUtils.h";
 
 using class nsHttpHeaderArray from "nsHttpHeaderArray.h";
 using mozilla::net::NetAddr from "mozilla/net/DNS.h";
 using struct mozilla::net::ResourceTimingStruct from "mozilla/net/TimingStruct.h";
+using refcounted class nsIReferrerInfo from "mozilla/dom/ReferrerInfoUtils.h";
 
 namespace mozilla {
 namespace net {
 
 //-------------------------------------------------------------------
 protocol PHttpChannel
 {
   manager PNecko;
@@ -39,18 +40,18 @@ parent:
   async Suspend();
   async Resume();
 
   async Cancel(nsresult status);
 
   // Reports approval/veto of redirect by child process redirect observers
   async Redirect2Verify(nsresult result, RequestHeaderTuples changedHeaders,
                         ChildLoadInfoForwarderArgs loadInfoForwarder,
-                        uint32_t loadFlags, uint32_t referrerPolicy,
-                        URIParams? referrerUri, URIParams? apiRedirectTo,
+                        uint32_t loadFlags, nsIReferrerInfo referrerInfo,
+                        URIParams? apiRedirectTo,
                         CorsPreflightArgs? corsPreflightArgs,
                         bool chooseAppcache);
 
   // Sent to the parent in order signal that the child side listeners have been
   // set up and the parent side of the channel can be opened.
   async CrossProcessRedirectDone(nsresult result);
 
   // For document loads we keep this protocol open after child's
--- a/netwerk/protocol/http/nsCORSListenerProxy.cpp
+++ b/netwerk/protocol/http/nsCORSListenerProxy.cpp
@@ -964,17 +964,17 @@ nsresult nsCORSListenerProxy::UpdateChan
   nsAutoCString origin;
   rv = nsContentUtils::GetASCIIOrigin(mOriginHeaderPrincipal, origin);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aChannel);
   NS_ENSURE_TRUE(http, NS_ERROR_FAILURE);
 
   // hide the Origin header when requesting from .onion and requesting CORS
-  if (gHttpHandler->HideOnionReferrerSource()) {
+  if (dom::ReferrerInfo::HideOnionReferrerSource()) {
     nsCOMPtr<nsIURI> potentialOnionUri;  // the candidate uri in header Origin:
     rv = mOriginHeaderPrincipal->GetURI(getter_AddRefs(potentialOnionUri));
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsAutoCString potentialOnionHost;
     rv = potentialOnionUri ? potentialOnionUri->GetAsciiHost(potentialOnionHost)
                            : NS_ERROR_FAILURE;
     NS_ENSURE_SUCCESS(rv, rv);
@@ -1532,24 +1532,25 @@ nsresult nsCORSListenerProxy::StartCORSP
       new nsCORSPreflightListener(principal, aCallback, loadContext,
                                   withCredentials, method, preflightHeaders);
 
   rv = preflightChannel->SetNotificationCallbacks(preflightListener);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Per https://fetch.spec.whatwg.org/#cors-preflight-fetch step 1, the
   // request's referrer and referrer policy should match the original request.
-  uint32_t referrerPolicy = nsIHttpChannel::REFERRER_POLICY_UNSET;
-  rv = reqCh->GetReferrerPolicy(&referrerPolicy);
+  nsCOMPtr<nsIReferrerInfo> referrerInfo;
+  rv = reqCh->GetReferrerInfo(getter_AddRefs(referrerInfo));
   NS_ENSURE_SUCCESS(rv, rv);
-  nsCOMPtr<nsIURI> requestReferrerURI;
-  rv = reqCh->GetReferrer(getter_AddRefs(requestReferrerURI));
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = preCh->SetReferrerWithPolicy(requestReferrerURI, referrerPolicy);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (referrerInfo) {
+    nsCOMPtr<nsIReferrerInfo> newReferrerInfo =
+        static_cast<dom::ReferrerInfo*>(referrerInfo.get())->Clone();
+    rv = preCh->SetReferrerInfo(newReferrerInfo);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
 
   // Start preflight
   rv = preflightChannel->AsyncOpen(preflightListener);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Return newly created preflight channel
   preflightChannel.forget(aPreflightChannel);
 
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -2437,24 +2437,22 @@ nsresult nsHttpChannel::ProcessResponse(
         Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 11);
         break;
     }
   }
 
   // Let the predictor know whether this was a cacheable response or not so
   // that it knows whether or not to possibly prefetch this resource in the
   // future.
-  // We use GetReferringPage because mReferrer may not be set at all, or may
-  // not be a full URI (HttpBaseChannel::SetReferrer has the gorey details).
-  // If that's null, though, we'll fall back to mReferrer just in case (this
-  // is especially useful in xpcshell tests, where we don't have an actual
-  // pageload to get a referrer from).
+  // We use GetReferringPage because mReferrerInfo may not be set at all(this is
+  // especially useful in xpcshell tests, where we don't have an actual pageload
+  // to get a referrer from).
   nsCOMPtr<nsIURI> referrer = GetReferringPage();
-  if (!referrer) {
-    referrer = mReferrer;
+  if (!referrer && mReferrerInfo) {
+    referrer = mReferrerInfo->GetOriginalReferrer();
   }
 
   if (referrer) {
     nsCOMPtr<nsILoadContextInfo> lci = GetLoadContextInfo(this);
     mozilla::net::Predictor::UpdateCacheability(
         referrer, mURI, httpStatus, mRequestHead, mResponseHead, lci,
         IsThirdPartyTrackingResource());
   }
@@ -9602,30 +9600,30 @@ void nsHttpChannel::SetOriginHeader() {
     // Origin header suppressed by user setting
     return;
   }
 
   nsCOMPtr<nsIURI> referrer;
   mLoadInfo->TriggeringPrincipal()->GetURI(getter_AddRefs(referrer));
 
   nsAutoCString origin("null");
-  if (referrer && IsReferrerSchemeAllowed(referrer)) {
+  if (referrer && dom::ReferrerInfo::IsReferrerSchemeAllowed(referrer)) {
     nsContentUtils::GetASCIIOrigin(referrer, origin);
   }
 
   // Restrict Origin to same-origin loads if requested by user or leaving from
   // .onion
   if (sSendOriginHeader == 1) {
     nsAutoCString currentOrigin;
     nsContentUtils::GetASCIIOrigin(mURI, currentOrigin);
     if (!origin.EqualsIgnoreCase(currentOrigin.get())) {
       // Origin header suppressed by user setting
       return;
     }
-  } else if (gHttpHandler->HideOnionReferrerSource()) {
+  } else if (dom::ReferrerInfo::HideOnionReferrerSource()) {
     nsAutoCString host;
     if (referrer && NS_SUCCEEDED(referrer->GetAsciiHost(host)) &&
         StringEndsWith(host, NS_LITERAL_CSTRING(".onion"))) {
       nsAutoCString currentOrigin;
       nsContentUtils::GetASCIIOrigin(mURI, currentOrigin);
       if (!origin.EqualsIgnoreCase(currentOrigin.get())) {
         // Origin header is suppressed by .onion
         return;
@@ -10218,28 +10216,34 @@ nsresult nsHttpChannel::RedirectToInterc
   }
 
   return rv;
 }
 
 void nsHttpChannel::ReEvaluateReferrerAfterTrackingStatusIsKnown() {
   if (StaticPrefs::network_cookie_cookieBehavior() ==
       nsICookieService::BEHAVIOR_REJECT_TRACKER) {
-    // If our referrer has been set before, the JS/DOM caller did not
-    // previously provide us with an explicit referrer policy value and our
-    // current referrer policy is equal to the default policy if we thought the
-    // channel wasn't a third-party tracking channel, we may need to set our
-    // referrer with referrer policy once again to ensure our defaults properly
-    // take effect now.
     bool isPrivate =
         mLoadInfo && mLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
-    if (mOriginalReferrer && mOriginalReferrerPolicy == REFERRER_POLICY_UNSET &&
-        mReferrerPolicy ==
-            NS_GetDefaultReferrerPolicy(nullptr, nullptr, isPrivate)) {
-      SetReferrer(mOriginalReferrer);
+    // If our referrer has been set before, and our referrer policy is unset
+    // (default policy) if we thought the channel wasn't a third-party
+    // tracking channel, we may need to set our referrer with referrer policy
+    // once again to ensure our defaults properly take effect now.
+    if (mReferrerInfo) {
+      dom::ReferrerInfo* referrerInfo =
+          static_cast<dom::ReferrerInfo*>(mReferrerInfo.get());
+
+      if (referrerInfo->IsPolicyOverrided() &&
+          referrerInfo->GetReferrerPolicy() ==
+              ReferrerInfo::GetDefaultReferrerPolicy(nullptr, nullptr,
+                                                     isPrivate)) {
+        nsCOMPtr<nsIReferrerInfo> newReferrerInfo =
+            referrerInfo->CloneWithNewPolicy(RP_Unset);
+        SetReferrerInfo(newReferrerInfo, false, true);
+      }
     }
   }
 }
 
 namespace {
 
 class BackgroundRevalidatingListener : public nsIStreamListener {
   NS_DECL_ISUPPORTS
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -196,23 +196,16 @@ already_AddRefed<nsHttpHandler> nsHttpHa
   RefPtr<nsHttpHandler> httpHandler = gHttpHandler;
   return httpHandler.forget();
 }
 
 nsHttpHandler::nsHttpHandler()
     : mHttpVersion(HttpVersion::v1_1),
       mProxyHttpVersion(HttpVersion::v1_1),
       mCapabilities(NS_HTTP_ALLOW_KEEPALIVE),
-      mReferrerLevel(0xff)  // by default we always send a referrer
-      ,
-      mSpoofReferrerSource(false),
-      mHideOnionReferrerSource(false),
-      mReferrerTrimmingPolicy(0),
-      mReferrerXOriginTrimmingPolicy(0),
-      mReferrerXOriginPolicy(0),
       mFastFallbackToIPv4(false),
       mIdleTimeout(PR_SecondsToInterval(10)),
       mSpdyTimeout(PR_SecondsToInterval(180)),
       mResponseTimeout(PR_SecondsToInterval(300)),
       mResponseTimeoutEnabled(false),
       mNetworkChangedTimeout(5000),
       mMaxRequestAttempts(6),
       mMaxRequestDelay(10),
@@ -1301,48 +1294,16 @@ void nsHttpHandler::PrefsChanged(const c
                "(max-persistent-connections-per-proxy)"
                "UpdateParam failed (%08x)\n",
                static_cast<uint32_t>(rv)));
         }
       }
     }
   }
 
-  if (PREF_CHANGED(HTTP_PREF("sendRefererHeader"))) {
-    rv = Preferences::GetInt(HTTP_PREF("sendRefererHeader"), &val);
-    if (NS_SUCCEEDED(rv)) mReferrerLevel = (uint8_t)clamped(val, 0, 0xff);
-  }
-
-  if (PREF_CHANGED(HTTP_PREF("referer.spoofSource"))) {
-    rv = Preferences::GetBool(HTTP_PREF("referer.spoofSource"), &cVar);
-    if (NS_SUCCEEDED(rv)) mSpoofReferrerSource = cVar;
-  }
-
-  if (PREF_CHANGED(HTTP_PREF("referer.hideOnionSource"))) {
-    rv = Preferences::GetBool(HTTP_PREF("referer.hideOnionSource"), &cVar);
-    if (NS_SUCCEEDED(rv)) mHideOnionReferrerSource = cVar;
-  }
-
-  if (PREF_CHANGED(HTTP_PREF("referer.trimmingPolicy"))) {
-    rv = Preferences::GetInt(HTTP_PREF("referer.trimmingPolicy"), &val);
-    if (NS_SUCCEEDED(rv)) mReferrerTrimmingPolicy = (uint8_t)clamped(val, 0, 2);
-  }
-
-  if (PREF_CHANGED(HTTP_PREF("referer.XOriginTrimmingPolicy"))) {
-    rv = Preferences::GetInt(HTTP_PREF("referer.XOriginTrimmingPolicy"), &val);
-    if (NS_SUCCEEDED(rv))
-      mReferrerXOriginTrimmingPolicy = (uint8_t)clamped(val, 0, 2);
-  }
-
-  if (PREF_CHANGED(HTTP_PREF("referer.XOriginPolicy"))) {
-    rv = Preferences::GetInt(HTTP_PREF("referer.XOriginPolicy"), &val);
-    if (NS_SUCCEEDED(rv))
-      mReferrerXOriginPolicy = (uint8_t)clamped(val, 0, 0xff);
-  }
-
   if (PREF_CHANGED(HTTP_PREF("redirection-limit"))) {
     rv = Preferences::GetInt(HTTP_PREF("redirection-limit"), &val);
     if (NS_SUCCEEDED(rv)) mRedirectionLimit = (uint8_t)clamped(val, 0, 0xff);
   }
 
   if (PREF_CHANGED(HTTP_PREF("connection-retry-timeout"))) {
     rv = Preferences::GetInt(HTTP_PREF("connection-retry-timeout"), &val);
     if (NS_SUCCEEDED(rv)) mIdleSynTimeout = (uint16_t)clamped(val, 0, 3000);
--- a/netwerk/protocol/http/nsHttpHandler.h
+++ b/netwerk/protocol/http/nsHttpHandler.h
@@ -73,24 +73,16 @@ class nsHttpHandler final : public nsIHt
   MOZ_MUST_USE nsresult AddConnectionHeader(nsHttpRequestHead*,
                                             uint32_t capabilities);
   bool IsAcceptableEncoding(const char* encoding, bool isSecure);
 
   const nsCString& UserAgent();
 
   enum HttpVersion HttpVersion() { return mHttpVersion; }
   enum HttpVersion ProxyHttpVersion() { return mProxyHttpVersion; }
-  uint8_t ReferrerLevel() { return mReferrerLevel; }
-  bool SpoofReferrerSource() { return mSpoofReferrerSource; }
-  bool HideOnionReferrerSource() { return mHideOnionReferrerSource; }
-  uint8_t ReferrerTrimmingPolicy() { return mReferrerTrimmingPolicy; }
-  uint8_t ReferrerXOriginTrimmingPolicy() {
-    return mReferrerXOriginTrimmingPolicy;
-  }
-  uint8_t ReferrerXOriginPolicy() { return mReferrerXOriginPolicy; }
   uint8_t RedirectionLimit() { return mRedirectionLimit; }
   PRIntervalTime IdleTimeout() { return mIdleTimeout; }
   PRIntervalTime SpdyTimeout() { return mSpdyTimeout; }
   PRIntervalTime ResponseTimeout() {
     return mResponseTimeoutEnabled ? mResponseTimeout : 0;
   }
   PRIntervalTime ResponseTimeoutEnabled() { return mResponseTimeoutEnabled; }
   uint32_t NetworkChangedTimeout() { return mNetworkChangedTimeout; }
@@ -478,22 +470,16 @@ class nsHttpHandler final : public nsIHt
 
   //
   // prefs
   //
 
   enum HttpVersion mHttpVersion;
   enum HttpVersion mProxyHttpVersion;
   uint32_t mCapabilities;
-  uint8_t mReferrerLevel;
-  uint8_t mSpoofReferrerSource;
-  uint8_t mHideOnionReferrerSource;
-  uint8_t mReferrerTrimmingPolicy;
-  uint8_t mReferrerXOriginTrimmingPolicy;
-  uint8_t mReferrerXOriginPolicy;
 
   bool mFastFallbackToIPv4;
   PRIntervalTime mIdleTimeout;
   PRIntervalTime mSpdyTimeout;
   PRIntervalTime mResponseTimeout;
   bool mResponseTimeoutEnabled;
   uint32_t mNetworkChangedTimeout;  // milliseconds
   uint16_t mMaxRequestAttempts;
--- a/netwerk/protocol/http/nsIHttpChannel.idl
+++ b/netwerk/protocol/http/nsIHttpChannel.idl
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsIChannel.idl"
 
 interface nsIHttpHeaderVisitor;
+interface nsIReferrerInfo;
 
 /**
  * nsIHttpChannel
  *
  * This interface allows for the modification of HTTP request parameters and
  * the inspection of the resulting HTTP response status and headers when they
  * become available.
  */
@@ -34,39 +35,51 @@ interface nsIHttpChannel : nsIChannel
      * necessary to set the request method explicitly.  The documentation
      * for nsIUploadChannel has further details.
      *
      * @throws NS_ERROR_IN_PROGRESS if set after the channel has been opened.
      */
     [must_use] attribute ACString requestMethod;
 
     /**
-     * Get/set the HTTP referrer URI.  This is the address (URI) of the
+     * Get/set the referrer information.  This contains the referrer (URI) of the
      * resource from which this channel's URI was obtained (see RFC2616 section
-     * 14.36).
+     * 14.36) and the referrer policy applied to the referrer.
      *
      * This attribute may only be set before the channel is opened.
      *
+     * Setting this attribute will clone new referrerInfo object by default.
+     *
      * NOTE: The channel may silently refuse to set the Referer header if the
      * URI does not pass certain security checks (e.g., a "https://" URL will
      * never be sent as the referrer for a plaintext HTTP request).  The
      * implementation is not required to throw an exception when the referrer
      * URI is rejected.
      *
      * @throws NS_ERROR_IN_PROGRESS if set after the channel has been opened.
      * @throws NS_ERROR_FAILURE if used for setting referrer during
      *         visitRequestHeaders. Getting the value will not throw.
      */
-    [must_use] attribute nsIURI referrer;
+    [must_use, infallible] attribute nsIReferrerInfo referrerInfo;
+
+    /**
+     * Set referrer Info without clone new object.
+     * Use this api only when you are passing a referrerInfo to the channel with
+     * 1-1 relationship. Don't use this api if you will reuse the referrer info
+     * object later. For example when to use:
+     * channel.setReferrerInfoWithoutClone(new ReferrerInfo());
+     *
+     */
+    [must_use, noscript]
+    void setReferrerInfoWithoutClone(in nsIReferrerInfo aReferrerInfo);
 
     /**
      * Referrer policies. See ReferrerPolicy.h for more details.
      */
 
-
     /*   The undefined state, or no referrer policy, usually causing a fallback
          to a referrer policy definded elsewhere */
     const unsigned long REFERRER_POLICY_UNSET                      = 0;
 
     /*   default state, doesn't send referrer from https->http          */
     const unsigned long REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE = 1;
     /*   sends no referrer                                              */
     const unsigned long REFERRER_POLICY_NO_REFERRER                = 2;
@@ -82,30 +95,16 @@ interface nsIHttpChannel : nsIChannel
          No referrer when request from https->http. */
     const unsigned long REFERRER_POLICY_STRICT_ORIGIN              = 7;
     /*   send referrer when same-origin,
          Send origin when cross-origin from https->https or http->http(s)
          No referrer when request from https->http. */
     const unsigned long REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN = 8;
 
     /**
-     * Get the HTTP referrer policy.  The policy is retrieved from the meta
-     * referrer tag, which can be one of many values (see ReferrerPolicy.h for
-     * more details).
-     */
-    [must_use] readonly attribute unsigned long referrerPolicy;
-
-    /**
-     * Set the HTTP referrer URI with a referrer policy.
-     * @throws NS_ERROR_FAILURE if called during visitRequestHeaders.
-     */
-    [must_use]
-    void setReferrerWithPolicy(in nsIURI referrer, in unsigned long referrerPolicy);
-
-    /**
      * Returns the network protocol used to fetch the resource as identified
      * by the ALPN Protocol ID.
      *
      * @throws NS_ERROR_NOT_AVAILABLE if called before the response
      *         has been received (before onStartRequest).
      */
     [must_use] readonly attribute ACString protocolVersion;
 
--- a/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
+++ b/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
@@ -10,16 +10,17 @@
 #include "nsNetUtil.h"
 #include "nsContentUtils.h"
 #include "nsIHttpHeaderVisitor.h"
 #include "nsContentSecurityManager.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIInputStreamChannel.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/NullPrincipal.h"
+#include "nsIReferrerInfo.h"
 
 NS_IMPL_ADDREF(nsViewSourceChannel)
 NS_IMPL_RELEASE(nsViewSourceChannel)
 /*
   This QI uses NS_INTERFACE_MAP_ENTRY_CONDITIONAL to check for
   non-nullness of mHttpChannel, mCachingChannel, and mUploadChannel.
 */
 NS_INTERFACE_MAP_BEGIN(nsViewSourceChannel)
@@ -754,39 +755,32 @@ nsViewSourceChannel::GetRequestMethod(ns
 
 NS_IMETHODIMP
 nsViewSourceChannel::SetRequestMethod(const nsACString& aRequestMethod) {
   return !mHttpChannel ? NS_ERROR_NULL_POINTER
                        : mHttpChannel->SetRequestMethod(aRequestMethod);
 }
 
 NS_IMETHODIMP
-nsViewSourceChannel::GetReferrer(nsIURI** aReferrer) {
+nsViewSourceChannel::GetReferrerInfo(nsIReferrerInfo** aReferrerInfo) {
   return !mHttpChannel ? NS_ERROR_NULL_POINTER
-                       : mHttpChannel->GetReferrer(aReferrer);
-}
-
-NS_IMETHODIMP
-nsViewSourceChannel::SetReferrer(nsIURI* aReferrer) {
-  return !mHttpChannel ? NS_ERROR_NULL_POINTER
-                       : mHttpChannel->SetReferrer(aReferrer);
+                       : mHttpChannel->GetReferrerInfo(aReferrerInfo);
 }
 
 NS_IMETHODIMP
-nsViewSourceChannel::GetReferrerPolicy(uint32_t* aReferrerPolicy) {
+nsViewSourceChannel::SetReferrerInfo(nsIReferrerInfo* aReferrerInfo) {
   return !mHttpChannel ? NS_ERROR_NULL_POINTER
-                       : mHttpChannel->GetReferrerPolicy(aReferrerPolicy);
+                       : mHttpChannel->SetReferrerInfo(aReferrerInfo);
 }
 
-NS_IMETHODIMP
-nsViewSourceChannel::SetReferrerWithPolicy(nsIURI* aReferrer,
-                                           uint32_t aReferrerPolicy) {
+NS_IMETHODIMP nsViewSourceChannel::SetReferrerInfoWithoutClone(
+    nsIReferrerInfo* aReferrerInfo) {
   return !mHttpChannel
              ? NS_ERROR_NULL_POINTER
-             : mHttpChannel->SetReferrerWithPolicy(aReferrer, aReferrerPolicy);
+             : mHttpChannel->SetReferrerInfoWithoutClone(aReferrerInfo);
 }
 
 NS_IMETHODIMP
 nsViewSourceChannel::GetRequestHeader(const nsACString& aHeader,
                                       nsACString& aValue) {
   aValue.Truncate();
   return !mHttpChannel ? NS_ERROR_NULL_POINTER
                        : mHttpChannel->GetRequestHeader(aHeader, aValue);
--- a/netwerk/test/unit/test_head.js
+++ b/netwerk/test/unit/test_head.js
@@ -1,27 +1,30 @@
 //
 //  HTTP headers test
 //
 
 // Note: sets Cc and Ci variables
 
 const {HttpServer} = ChromeUtils.import("resource://testing-common/httpd.js");
+const ReferrerInfo = Components.Constructor("@mozilla.org/referrer-info;1",
+                                            "nsIReferrerInfo",
+                                            "init");
 
 XPCOMUtils.defineLazyGetter(this, "URL", function() {
   return "http://localhost:" + httpserver.identity.primaryPort;
 });
 
 var httpserver = new HttpServer();
 var testpath = "/simple";
 var httpbody = "0123456789";
 var channel;
 var ios;
 
-var dbg=0
+var dbg = 0;
 if (dbg) { print("============== START =========="); }
 
 function run_test() {
   setup_test();
   do_test_pending();
 }
 
 function setup_test() {
@@ -59,36 +62,35 @@ function setup_test() {
   channel.setEmptyRequestHeader("MergeWithEmpty");
   setOK = channel.getRequestHeader("MergeWithEmpty");
   Assert.equal(setOK, "");
   channel.setRequestHeader("MergeWithEmpty", "foo", true);
   setOK = channel.getRequestHeader("MergeWithEmpty");
   Assert.equal(setOK, "foo");
 
   var uri = NetUtil.newURI("http://foo1.invalid:80");
-  channel.referrer = uri;
-  Assert.ok(channel.referrer.equals(uri));
+  channel.referrerInfo = new ReferrerInfo(Ci.nsIHttpChannel.REFERRER_POLICY_UNSET, true, uri);
   setOK = channel.getRequestHeader("Referer");
   Assert.equal(setOK, "http://foo1.invalid/");
 
   uri = NetUtil.newURI("http://foo2.invalid:90/bar");
-  channel.referrer = uri;
+  channel.referrerInfo = new ReferrerInfo(Ci.nsIHttpChannel.REFERRER_POLICY_UNSET, true, uri);
   setOK = channel.getRequestHeader("Referer");
   Assert.equal(setOK, "http://foo2.invalid:90/bar");
 
   // ChannelListener defined in head_channels.js
   channel.asyncOpen(new ChannelListener(checkRequestResponse, channel));
 
   if (dbg) { print("============== setup_test: out"); }
 }
 
 function setupChannel(path) {
   var chan = NetUtil.newChannel({
     uri: URL + path,
-    loadUsingSystemPrincipal: true
+    loadUsingSystemPrincipal: true,
   });
   chan.QueryInterface(Ci.nsIHttpChannel);
   chan.requestMethod = "GET";
   return chan;
 }
 
 function serverHandler(metadata, response) {
   if (dbg) { print("============== serverHandler: in"); }
@@ -103,17 +105,17 @@ function serverHandler(metadata, respons
   Assert.equal(setOK, "");
   setOK = metadata.getHeader("MergeWithEmpty");
   Assert.equal(setOK, "foo");
   setOK = metadata.getHeader("Referer");
   Assert.equal(setOK, "http://foo2.invalid:90/bar");
 
   response.setHeader("Content-Type", "text/plain", false);
   response.setStatusLine("1.1", 200, "OK");
-  
+
   // note: httpd.js' "Response" class uses ',' (no space) for merge.
   response.setHeader("httpdMerge", "bar1", false);
   response.setHeader("httpdMerge", "bar2", true);
   response.setHeader("httpdMerge", "bar3", true);
   // Some special headers like Proxy-Authenticate merge with \n
   response.setHeader("Proxy-Authenticate", "line 1", true);
   response.setHeader("Proxy-Authenticate", "line 2", true);
   response.setHeader("Proxy-Authenticate", "line 3", true);
--- a/netwerk/test/unit/test_httpcancel.js
+++ b/netwerk/test/unit/test_httpcancel.js
@@ -3,16 +3,19 @@
 //
 // I've also shoehorned in a test that ENSURE_CALLED_BEFORE_CONNECT works as
 // expected: see comments that start with ENSURE_CALLED_BEFORE_CONNECT:
 
 const {HttpServer} = ChromeUtils.import("resource://testing-common/httpd.js");
 
 var ios = Cc["@mozilla.org/network/io-service;1"]
             .getService(Ci.nsIIOService);
+var ReferrerInfo = Components.Constructor("@mozilla.org/referrer-info;1",
+                                          "nsIReferrerInfo",
+                                          "init");
 var observer = {
   QueryInterface: function eventsink_qi(iid) {
     if (iid.equals(Ci.nsISupports) ||
         iid.equals(Ci.nsIObserver))
       return this;
     throw Cr.NS_ERROR_NO_INTERFACE;
   },
 
@@ -21,25 +24,25 @@ var observer = {
     subject.cancel(Cr.NS_BINDING_ABORTED);
 
     // ENSURE_CALLED_BEFORE_CONNECT: setting values should still work
     try {
       subject.QueryInterface(Ci.nsIHttpChannel);
       currentReferrer = subject.getRequestHeader("Referer");
       Assert.equal(currentReferrer, "http://site1.com/");
       var uri = ios.newURI("http://site2.com");
-      subject.referrer = uri;
+      subject.referrerInfo = new ReferrerInfo(Ci.nsIHttpChannel.REFERRER_POLICY_UNSET, true, uri);
     } catch (ex) {
       do_throw("Exception: " + ex);
     }
 
     var obs = Cc["@mozilla.org/observer-service;1"].getService();
     obs = obs.QueryInterface(Ci.nsIObserverService);
     obs.removeObserver(observer, "http-on-modify-request");
-  }
+  },
 };
 
 var listener = {
   onStartRequest: function test_onStartR(request) {
     Assert.equal(request.status, Cr.NS_BINDING_ABORTED);
 
     // ENSURE_CALLED_BEFORE_CONNECT: setting referrer should now fail
     try {
@@ -49,41 +52,40 @@ var listener = {
       var uri = ios.newURI("http://site3.com/");
 
       // Need to set NECKO_ERRORS_ARE_FATAL=0 else we'll abort process
       var env = Cc["@mozilla.org/process/environment;1"].
                   getService(Ci.nsIEnvironment);
       env.set("NECKO_ERRORS_ARE_FATAL", "0");
       // we expect setting referrer to fail
       try {
-        request.referrer = uri;
+        request.referrerInfo = new ReferrerInfo(Ci.nsIHttpChannel.REFERRER_POLICY_UNSET, true, uri);
         do_throw("Error should have been thrown before getting here");
-      } catch (ex) { } 
+      } catch (ex) { }
     } catch (ex) {
       do_throw("Exception: " + ex);
     }
   },
 
   onDataAvailable: function test_ODA() {
     do_throw("Should not get any data!");
   },
 
   onStopRequest: function test_onStopR(request, status) {
     httpserv.stop(do_test_finished);
-  }
+  },
 };
 
 function makeChan(url) {
   var chan = NetUtil.newChannel({uri: url, loadUsingSystemPrincipal: true})
                     .QueryInterface(Ci.nsIHttpChannel);
 
   // ENSURE_CALLED_BEFORE_CONNECT: set original value
   var uri = ios.newURI("http://site1.com");
-  chan.referrer = uri;
-
+  chan.referrerInfo = new ReferrerInfo(Ci.nsIHttpChannel.REFERRER_POLICY_UNSET, true, uri);
   return chan;
 }
 
 var httpserv = null;
 
 function execute_test() {
   var chan = makeChan("http://localhost:" +
                       httpserv.identity.primaryPort + "/failtest");
--- a/netwerk/test/unit/test_predictor.js
+++ b/netwerk/test/unit/test_predictor.js
@@ -1,10 +1,13 @@
 const {HttpServer} = ChromeUtils.import("resource://testing-common/httpd.js");
 var {Services} = ChromeUtils.import('resource://gre/modules/Services.jsm');
+const ReferrerInfo = Components.Constructor("@mozilla.org/referrer-info;1",
+                                            "nsIReferrerInfo",
+                                            "init");
 
 var running_single_process = false;
 
 var predictor = null;
 
 function is_child_process() {
   return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).processType == Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT;
 }
@@ -494,20 +497,20 @@ function test_prefetch_prime() {
     } else {
       sendCommand("predictor.learn(prefetch_tluri, null, predictor.LEARN_LOAD_TOPLEVEL, origin_attributes);");
       sendCommand("predictor.learn(prefetch_sruri, prefetch_tluri, predictor.LEARN_LOAD_SUBRESOURCE, origin_attributes);");
     }
 
     // This runs in the parent or only process
     var channel = NetUtil.newChannel({
       uri: prefetch_sruri.asciiSpec,
-      loadUsingSystemPrincipal: true
+      loadUsingSystemPrincipal: true,
     }).QueryInterface(Ci.nsIHttpChannel);
     channel.requestMethod = "GET";
-    channel.referrer = prefetch_tluri;
+    channel.referrerInfo = new ReferrerInfo(Ci.nsIHttpChannel.REFERRER_POLICY_UNSET, true, prefetch_tluri);
     channel.asyncOpen(prefetchListener);
   });
 }
 
 function test_prefetch() {
   // This test does not run in e10s-mode, so we'll just go ahead and skip it.
   // We've left the e10s test code in below, just in case someone wants to try
   // to make it work at some point in the future.
--- a/netwerk/test/unit/test_redirect_history.js
+++ b/netwerk/test/unit/test_redirect_history.js
@@ -1,9 +1,12 @@
 const {HttpServer} = ChromeUtils.import("resource://testing-common/httpd.js");
+const ReferrerInfo = Components.Constructor("@mozilla.org/referrer-info;1",
+                                            "nsIReferrerInfo",
+                                            "init");
 
 XPCOMUtils.defineLazyGetter(this, "URL", function() {
   return "http://localhost:" + httpServer.identity.primaryPort;
 });
 
 var httpServer = null;
 var randomPath = "/redirect/" + Math.random();
 var redirects = [];
@@ -57,12 +60,12 @@ function run_test()
                                      contentHandler);
     }
   }
   httpServer.start(-1);
 
   var chan = make_channel(URL + redirects[0]);
   var uri = NetUtil.newURI("http://test.com");
   httpChan = chan.QueryInterface(Ci.nsIHttpChannel);
-  httpChan.referrer = uri;
+  httpChan.referrerInfo = new ReferrerInfo(Ci.nsIHttpChannel.REFERRER_POLICY_UNSET, true, uri);
   chan.asyncOpen(new ChannelListener(finish_test, null));
   do_test_pending();
 }
--- a/netwerk/test/unit/test_referrer.js
+++ b/netwerk/test/unit/test_referrer.js
@@ -1,22 +1,25 @@
+const ReferrerInfo = Components.Constructor("@mozilla.org/referrer-info;1",
+                                            "nsIReferrerInfo",
+                                            "init");
 
 function getTestReferrer(server_uri, referer_uri, isPrivate=false) {
   var uri = NetUtil.newURI(server_uri)
   let referrer = NetUtil.newURI(referer_uri);
   let principal = Services.scriptSecurityManager.createCodebasePrincipal(referrer, {privateBrowsingId: isPrivate ? 1 : 0});
   var chan = NetUtil.newChannel({
     uri: uri,
     loadingPrincipal: principal,
     contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER,
     securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
   });
 
   chan.QueryInterface(Ci.nsIHttpChannel);
-  chan.referrer = referrer;
+  chan.referrerInfo = new ReferrerInfo(Ci.nsIHttpChannel.REFERRER_POLICY_UNSET, true, referrer);
   var header = null;
   try {
     header = chan.getRequestHeader("Referer");
   }
   catch (NS_ERROR_NOT_AVAILABLE) {}
   return header;
 }
 
--- a/netwerk/test/unit/test_referrer_cross_origin.js
+++ b/netwerk/test/unit/test_referrer_cross_origin.js
@@ -1,12 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
+const ReferrerInfo = Components.Constructor("@mozilla.org/referrer-info;1",
+                                            "nsIReferrerInfo",
+                                            "init");
 
 function test_policy(test) {
   info("Running test: " + test.toSource());
 
   let prefs = Services.prefs;
 
   if (test.trimmingPolicy !== undefined) {
     prefs.setIntPref("network.http.referer.trimmingPolicy",
@@ -28,28 +31,27 @@ function test_policy(test) {
     uri: test.url,
     loadingPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
     triggeringPrincipal: triggeringPrincipal,
     contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER,
     securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
   });
 
   chan.QueryInterface(Ci.nsIHttpChannel);
-  chan.setReferrerWithPolicy(referrer, test.policy);
+  chan.referrerInfo = new ReferrerInfo(test.policy, true, referrer);
+
   if (test.expectedReferrerSpec === undefined) {
     try {
       chan.getRequestHeader("Referer");
       do_throw("Should not find a Referer header!");
     } catch(e) {
     }
-    Assert.equal(chan.referrer, null);
   } else {
     let header = chan.getRequestHeader("Referer");
     Assert.equal(header, test.expectedReferrerSpec);
-    Assert.equal(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,
--- a/netwerk/test/unit/test_referrer_policy.js
+++ b/netwerk/test/unit/test_referrer_policy.js
@@ -1,8 +1,11 @@
+const ReferrerInfo = Components.Constructor("@mozilla.org/referrer-info;1",
+                                            "nsIReferrerInfo",
+                                            "init");
 
 function test_policy(test) {
   info("Running test: " + test.toSource());
 
   var prefs = Cc["@mozilla.org/preferences-service;1"]
     .getService(Ci.nsIPrefBranch);
   if (test.defaultReferrerPolicyPref !== undefined) {
     prefs.setIntPref("network.http.referer.defaultPolicy",
@@ -14,28 +17,27 @@ function test_policy(test) {
   var uri = NetUtil.newURI(test.url)
   var chan = NetUtil.newChannel({
     uri: uri,
     loadUsingSystemPrincipal: true
   });
 
   var referrer = NetUtil.newURI(test.referrer);
   chan.QueryInterface(Ci.nsIHttpChannel);
-  chan.setReferrerWithPolicy(referrer, test.policy);
+  chan.referrerInfo = new ReferrerInfo(test.policy, true, referrer);
+
   if (test.expectedReferrerSpec === undefined) {
     try {
       chan.getRequestHeader("Referer");
       do_throw("Should not find a Referer header!");
     } catch(e) {
     }
-    Assert.equal(chan.referrer, null);
   } else {
     var header = chan.getRequestHeader("Referer");
     Assert.equal(header, test.expectedReferrerSpec);
-    Assert.equal(chan.referrer.asciiSpec, test.expectedReferrerSpec);
   }
 }
 
 const nsIHttpChannel = Ci.nsIHttpChannel;
 // Assuming cross origin because we have no triggering principal available
 var gTests = [
   {
     policy: nsIHttpChannel.REFERRER_POLICY_UNSET,
--- a/taskcluster/scripts/misc/build-sccache.sh
+++ b/taskcluster/scripts/misc/build-sccache.sh
@@ -61,20 +61,28 @@ EOF
     make -j `nproc --all`
     # `make install` installs a *ton* of docs that we don't care about.
     # Just the software, please.
     make install_sw
     popd
 
     # We don't need to set OPENSSL_STATIC here, because we only have static
     # libraries in the directory we are passing.
-    env "OPENSSL_DIR=$OPENSSL_BUILD_DIRECTORY" cargo build --features "all dist-server" --verbose --release
+    if [ -n "${SCCACHE_GCS_KEY_PATH}" ]; then
+        env "OPENSSL_DIR=$OPENSSL_BUILD_DIRECTORY" cargo build --features "all dist-server gcs" --verbose --release
+    else
+        env "OPENSSL_DIR=$OPENSSL_BUILD_DIRECTORY" cargo build --features "all dist-server" --verbose --release
+    fi
     ;;
 MINGW*)
-    cargo build --verbose --release
+    if [ -n "${SCCACHE_GCS_KEY_PATH}" ]; then
+        cargo build --verbose --release --features=gcs
+    else
+        cargo build --verbose --release
+    fi
     ;;
 esac
 
 mkdir sccache2
 cp target/release/sccache* sccache2/
 tar -acf sccache2.tar.$COMPRESS_EXT sccache2
 mkdir -p $UPLOAD_DIR
 cp sccache2.tar.$COMPRESS_EXT $UPLOAD_DIR
--- a/testing/web-platform/tests/webrtc/RTCPeerConnection-setLocalDescription-offer.html
+++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-setLocalDescription-offer.html
@@ -166,9 +166,30 @@
     const offer = await pc1.createOffer({offerToReceiveAudio: true}); // [[LastOffer]] set
     const offer2 = await pc2.createOffer({offerToReceiveVideo: true});
     await pc1.setRemoteDescription(offer2);
     await pc1.createAnswer(); // [[LastAnswer]] set
     await pc1.setRemoteDescription({type: "rollback"});
     await pc1.setLocalDescription(offer);
   }, "Setting previously generated offer after a call to createAnswer should work");
 
+  promise_test(async t => {
+    const pc1 = new RTCPeerConnection();
+    t.add_cleanup(() => pc1.close());
+    const pc2 = new RTCPeerConnection();
+    t.add_cleanup(() => pc2.close());
+
+    await pc1.setLocalDescription(await pc1.createOffer({offerToReceiveAudio: true}));
+
+    const offer = await pc1.createOffer();
+    await pc1.setLocalDescription(offer);
+    await pc2.setRemoteDescription(offer);
+    const answer = await pc2.createAnswer();
+    await pc2.setLocalDescription(answer);
+    await pc1.setRemoteDescription(answer);
+
+    assert_equals(pc1.getTransceivers().length, 1);
+    assert_equals(pc1.getTransceivers()[0].receiver.track.kind, "audio");
+    assert_equals(pc2.getTransceivers().length, 1);
+    assert_equals(pc2.getTransceivers()[0].receiver.track.kind, "audio");
+  }, "Negotiation works when there has been a repeated setLocalDescription(offer)");
+
 </script>
--- a/toolkit/components/downloads/DownloadCore.jsm
+++ b/toolkit/components/downloads/DownloadCore.jsm
@@ -1907,19 +1907,24 @@ this.DownloadCopySaver.prototype = {
         channel.setPrivate(download.source.isPrivate);
       }
       if (channel instanceof Ci.nsIHttpChannel &&
           download.source.referrer) {
         // Sending Referrer header is computed at the time we initialize a
         // download (eg. user clicks on "Save Link As"). We use
         // REFERRER_POLICY_UNSAFE_URL to keep the referrer header the same
         // here.
-        channel.setReferrerWithPolicy(
-          NetUtil.newURI(download.source.referrer),
-          Ci.nsIHttpChannel.REFERRER_POLICY_UNSAFE_URL);
+        let ReferrerInfo = Components.Constructor(
+          "@mozilla.org/referrer-info;1",
+          "nsIReferrerInfo",
+          "init");
+        channel.referrerInfo = new ReferrerInfo(
+          Ci.nsIHttpChannel.REFERRER_POLICY_UNSAFE_URL,
+          true,
+          NetUtil.newURI(download.source.referrer));
       }
 
       // This makes the channel be corretly throttled during page loads
       // and also prevents its caching.
       if (channel instanceof Ci.nsIHttpChannelInternal) {
         channel.channelIsForDownload = true;
       }
 
@@ -2349,18 +2354,21 @@ this.DownloadLegacySaver.prototype = {
         if (!(ex instanceof Components.Exception) ||
             ex.result != Cr.NS_ERROR_NOT_RESUMABLE) {
           throw ex;
         }
       }
     }
 
     // For legacy downloads, we must update the referrer at this time.
-    if (aRequest instanceof Ci.nsIHttpChannel && aRequest.referrer) {
-      this.download.source.referrer = aRequest.referrer.spec;
+    if (aRequest instanceof Ci.nsIHttpChannel) {
+      let referrerInfo = aRequest.referrerInfo;
+      if (referrerInfo && referrerInfo.originalReferrer) {
+        this.download.source.referrer = referrerInfo.originalReferrer.spec;
+      }
     }
 
     this.addToHistory();
   },
 
   /**
    * Called by the nsITransfer implementation when the request has finished.
    *
--- a/toolkit/components/url-classifier/nsUrlClassifierUtils.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierUtils.cpp
@@ -555,23 +555,25 @@ static nsresult AddThreatSourceFromChann
 
   nsCString spec;
   rv = GetSpecWithoutSensitiveData(uri, spec);
   NS_ENSURE_SUCCESS(rv, rv);
   matchingSource->set_url(spec.get());
 
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
   if (httpChannel) {
-    nsCOMPtr<nsIURI> referrer;
-    rv = httpChannel->GetReferrer(getter_AddRefs(referrer));
-    if (NS_SUCCEEDED(rv) && referrer) {
-      nsCString referrerSpec;
-      rv = GetSpecWithoutSensitiveData(referrer, referrerSpec);
-      NS_ENSURE_SUCCESS(rv, rv);
-      matchingSource->set_referrer(referrerSpec.get());
+    nsCOMPtr<nsIReferrerInfo> referrerInfo = httpChannel->GetReferrerInfo();
+    if (referrerInfo) {
+      nsAutoCString referrerSpec;
+      nsCOMPtr<nsIURI> referrer = referrerInfo->GetComputedReferrer();
+      if (referrer) {
+        rv = GetSpecWithoutSensitiveData(referrer, referrerSpec);
+        NS_ENSURE_SUCCESS(rv, rv);
+        matchingSource->set_referrer(referrerSpec.get());
+      }
     }
   }
 
   nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
       do_QueryInterface(aChannel);
   if (httpChannelInternal) {
     nsCString remoteIp;
     rv = httpChannelInternal->GetRemoteAddress(remoteIp);
--- a/uriloader/prefetch/nsOfflineCacheUpdate.cpp
+++ b/uriloader/prefetch/nsOfflineCacheUpdate.cpp
@@ -33,16 +33,17 @@
 #include "nsIConsoleService.h"
 #include "mozilla/Logging.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Attributes.h"
 #include "nsContentUtils.h"
 #include "nsIPrincipal.h"
 #include "nsDiskCacheDeviceSQL.h"
+#include "ReferrerInfo.h"
 
 #include "nsXULAppAPI.h"
 
 using namespace mozilla;
 
 static const uint32_t kRescheduleLimit = 3;
 // Max number of retries for every entry of pinned app.
 static const uint32_t kPinnedEntryRetriesLimit = 3;
@@ -172,17 +173,19 @@ nsresult nsManifestCheck::Begin() {
                      nullptr,  // aCallbacks
                      nsIRequest::LOAD_BYPASS_CACHE);
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   // configure HTTP specific stuff
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
   if (httpChannel) {
-    rv = httpChannel->SetReferrer(mReferrerURI);
+    nsCOMPtr<nsIReferrerInfo> referrerInfo =
+        new mozilla::dom::ReferrerInfo(mReferrerURI);
+    rv = httpChannel->SetReferrerInfoWithoutClone(referrerInfo);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
     rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"),
                                        NS_LITERAL_CSTRING("offline-resource"),
                                        false);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
   }
 
   return mChannel->AsyncOpen(this);
@@ -352,17 +355,19 @@ nsresult nsOfflineCacheUpdateItem::OpenC
 
   // Set the new application cache as the target for write.
   rv = appCacheChannel->SetApplicationCacheForWrite(mApplicationCache);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // configure HTTP specific stuff
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
   if (httpChannel) {
-    rv = httpChannel->SetReferrer(mReferrerURI);
+    nsCOMPtr<nsIReferrerInfo> referrerInfo =
+        new mozilla::dom::ReferrerInfo(mReferrerURI);
+    rv = httpChannel->SetReferrerInfoWithoutClone(referrerInfo);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
     rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"),
                                        NS_LITERAL_CSTRING("offline-resource"),
                                        false);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
   }
 
   rv = mChannel->AsyncOpen(this);
--- a/uriloader/prefetch/nsPrefetchService.cpp
+++ b/uriloader/prefetch/nsPrefetchService.cpp
@@ -8,16 +8,17 @@
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/CORSMode.h"
 #include "mozilla/Components.h"
 #include "mozilla/dom/ClientInfo.h"
 #include "mozilla/dom/HTMLLinkElement.h"
 #include "mozilla/dom/ServiceWorkerDescriptor.h"
 #include "mozilla/Preferences.h"
+#include "ReferrerInfo.h"
 
 #include "nsICacheEntry.h"
 #include "nsIServiceManager.h"
 #include "nsICategoryManager.h"
 #include "nsIObserverService.h"
 #include "nsIWebProgress.h"
 #include "nsICacheInfoChannel.h"
 #include "nsIHttpChannel.h"
@@ -133,17 +134,19 @@ nsresult nsPrefetchNode::OpenChannel() {
       this,       // aCallbacks
       nsIRequest::LOAD_BACKGROUND | nsICachingChannel::LOAD_ONLY_IF_MODIFIED);
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   // configure HTTP specific stuff
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
   if (httpChannel) {
-    rv = httpChannel->SetReferrerWithPolicy(mReferrerURI, referrerPolicy);
+    nsCOMPtr<nsIReferrerInfo> referrerInfo =
+        new mozilla::dom::ReferrerInfo(mReferrerURI, referrerPolicy);
+    rv = httpChannel->SetReferrerInfoWithoutClone(referrerInfo);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
     rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"),
                                        NS_LITERAL_CSTRING("prefetch"), false);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
   }
 
   // Reduce the priority of prefetch network requests.
   nsCOMPtr<nsISupportsPriority> priorityChannel = do_QueryInterface(mChannel);
--- a/widget/android/WebExecutorSupport.cpp
+++ b/widget/android/WebExecutorSupport.cpp
@@ -22,16 +22,17 @@
 #include "nsIDNSRecord.h"
 
 #include "mozilla/net/DNS.h"  // for NetAddr
 #include "mozilla/net/CookieSettings.h"
 
 #include "nsNetUtil.h"  // for NS_NewURI, NS_NewChannel, NS_NewStreamLoader
 
 #include "InetAddress.h"  // for java::sdk::InetAddress and java::sdk::UnknownHostException
+#include "ReferrerInfo.h"
 
 namespace mozilla {
 using namespace net;
 
 namespace widget {
 
 static void CompleteWithError(java::GeckoResult::Param aResult,
                               nsresult aStatus) {
@@ -488,17 +489,18 @@ nsresult WebExecutorSupport::CreateStrea
   // Referrer
   RefPtr<nsIURI> referrerUri;
   const auto referrer = req->Referrer();
   if (referrer) {
     rv = NS_NewURI(getter_AddRefs(referrerUri), referrer->ToString());
     NS_ENSURE_SUCCESS(rv, NS_ERROR_MALFORMED_URI);
   }
 
-  rv = httpChannel->SetReferrer(referrerUri);
+  nsCOMPtr<nsIReferrerInfo> referrerInfo = new dom::ReferrerInfo(referrerUri);
+  rv = httpChannel->SetReferrerInfoWithoutClone(referrerInfo);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Cache mode
   nsCOMPtr<nsIHttpChannelInternal> internalChannel(
       do_QueryInterface(channel, &rv));
   NS_ENSURE_SUCCESS(rv, rv);
 
   int32_t cacheMode;