Bug 1517703 - Part 1 - Implement ReferrerInfo class r=smaug
authorThomas Nguyen <tnguyen@mozilla.com>
Tue, 12 Feb 2019 19:35:32 +0000
changeset 458781 64c8b805491a
parent 458780 2d0505c52681
child 458782 586348ccba9b
push id35548
push useropoprus@mozilla.com
push dateWed, 13 Feb 2019 09:48:26 +0000
treeherdermozilla-central@93e37c529818 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1517703
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1517703 - Part 1 - Implement ReferrerInfo class r=smaug The class contains original full referrer and referrer policy will be applied to the referrer. Differential Revision: https://phabricator.services.mozilla.com/D17923
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
docshell/base/nsDocShellLoadState.cpp
docshell/base/nsDocShellLoadState.h
docshell/base/nsIWebNavigation.idl
docshell/shistory/nsISHEntry.idl
docshell/shistory/nsSHEntry.cpp
docshell/shistory/nsSHEntry.h
dom/base/Location.cpp
dom/base/nsFrameLoader.cpp
dom/bindings/Bindings.conf
dom/clients/manager/ClientNavigateOpChild.cpp
dom/interfaces/security/moz.build
dom/interfaces/security/nsIReferrerInfo.idl
dom/ipc/ContentChild.cpp
dom/security/ReferrerInfo.cpp
dom/security/ReferrerInfo.h
dom/security/moz.build
dom/webidl/LoadURIOptions.webidl
layout/build/components.conf
toolkit/components/browser/nsWebBrowser.cpp
toolkit/components/windowwatcher/nsWindowWatcher.cpp
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -58,16 +58,17 @@
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/TabGroup.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/dom/ChildSHistory.h"
 #include "mozilla/dom/LoadURIOptionsBinding.h"
 
 #include "mozilla/net/ReferrerPolicy.h"
 #include "mozilla/net/UrlClassifierFeatureFactory.h"
+#include "ReferrerInfo.h"
 
 #include "nsIApplicationCacheChannel.h"
 #include "nsIApplicationCacheContainer.h"
 #include "nsIAppShell.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsIAuthPrompt.h"
 #include "nsIAuthPrompt2.h"
 #include "nsICachingChannel.h"
@@ -326,17 +327,16 @@ nsDocShell::nsDocShell(BrowsingContext* 
       mPreviousEntryIndex(-1),
       mLoadedEntryIndex(-1),
       mChildOffset(0),
       mSandboxFlags(0),
       mBusyFlags(BUSY_FLAGS_NONE),
       mAppType(nsIDocShell::APP_TYPE_UNKNOWN),
       mLoadType(0),
       mDefaultLoadFlags(nsIRequest::LOAD_NORMAL),
-      mReferrerPolicy((uint32_t)mozilla::net::ReferrerPolicy::RP_Unset),
       mFailedLoadType(0),
       mFrameType(FRAME_TYPE_REGULAR),
       mPrivateBrowsingId(0),
       mDisplayMode(nsIDocShell::DISPLAY_MODE_BROWSER),
       mJSRunToCompletionDepth(0),
       mTouchEventsOverride(nsIDocShell::TOUCHEVENTS_OVERRIDE_NONE),
       mMetaViewportOverride(nsIDocShell::META_VIEWPORT_OVERRIDE_NONE),
       mFullscreenAllowed(CHECK_ATTRIBUTES),
@@ -3892,34 +3892,32 @@ nsresult nsDocShell::LoadURI(const nsASt
 
   // Don't pass certain flags that aren't needed and end up confusing
   // ConvertLoadTypeToDocShellInfoLoadType.  We do need to ensure that they are
   // passed to LoadURI though, since it uses them.
   uint32_t extraFlags = (loadFlags & EXTRA_LOAD_FLAGS);
   loadFlags &= ~EXTRA_LOAD_FLAGS;
 
   RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(uri);
+  loadState->SetReferrerInfo(aLoadURIOptions.mReferrerInfo);
 
   /*
    * If the user "Disables Protection on This Page", we have to make sure to
    * remember the users decision when opening links in child tabs [Bug 906190]
    */
   if (loadFlags & LOAD_FLAGS_ALLOW_MIXED_CONTENT) {
     loadState->SetLoadType(
         MAKE_LOAD_TYPE(LOAD_NORMAL_ALLOW_MIXED_CONTENT, loadFlags));
   } else {
     loadState->SetLoadType(MAKE_LOAD_TYPE(LOAD_NORMAL, loadFlags));
   }
 
   loadState->SetLoadFlags(extraFlags);
   loadState->SetFirstParty(true);
   loadState->SetPostDataStream(postData);
-  loadState->SetReferrer(aLoadURIOptions.mReferrerURI);
-  loadState->SetReferrerPolicy(
-      (mozilla::net::ReferrerPolicy)aLoadURIOptions.mReferrerPolicy);
   loadState->SetHeadersStream(aLoadURIOptions.mHeaders);
   loadState->SetBaseURI(aLoadURIOptions.mBaseURI);
   loadState->SetTriggeringPrincipal(aLoadURIOptions.mTriggeringPrincipal);
   loadState->SetForceAllowDataURI(forceAllowDataURI);
 
   if (fixupInfo) {
     nsAutoString searchProvider, keyword;
     fixupInfo->GetKeywordProviderName(searchProvider);
@@ -4587,29 +4585,26 @@ nsDocShell::Reload(uint32_t aReloadFlags
     MOZ_ASSERT(triggeringPrincipal, "Need a valid triggeringPrincipal");
     if (mUseStrictSecurityChecks && !triggeringPrincipal) {
       return NS_ERROR_FAILURE;
     }
 
     // Stack variables to ensure changes to the member variables don't affect to
     // the call.
     nsCOMPtr<nsIURI> currentURI = mCurrentURI;
-    nsCOMPtr<nsIURI> referrerURI = mReferrerURI;
-    uint32_t referrerPolicy = mReferrerPolicy;
 
     // Reload always rewrites result principal URI.
     Maybe<nsCOMPtr<nsIURI>> emplacedResultPrincipalURI;
     emplacedResultPrincipalURI.emplace(std::move(resultPrincipalURI));
 
     RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(currentURI);
+    loadState->SetReferrerInfo(mReferrerInfo);
     loadState->SetOriginalURI(originalURI);
     loadState->SetMaybeResultPrincipalURI(emplacedResultPrincipalURI);
     loadState->SetLoadReplace(loadReplace);
-    loadState->SetReferrer(referrerURI);
-    loadState->SetReferrerPolicy((mozilla::net::ReferrerPolicy)referrerPolicy);
     loadState->SetTriggeringPrincipal(triggeringPrincipal);
     loadState->SetPrincipalToInherit(triggeringPrincipal);
     loadState->SetLoadFlags(flags);
     loadState->SetTypeHint(NS_ConvertUTF16toUTF8(contentTypeHint));
     loadState->SetLoadType(loadType);
     loadState->SetFirstParty(true);
     loadState->SetSrcdocData(srcdoc);
     loadState->SetSourceDocShell(this);
@@ -4689,26 +4684,16 @@ nsDocShell::GetCurrentURI(nsIURI** aURI)
   NS_ENSURE_ARG_POINTER(aURI);
 
   nsCOMPtr<nsIURI> uri = mCurrentURI;
   uri.forget(aURI);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDocShell::GetReferringURI(nsIURI** aURI) {
-  NS_ENSURE_ARG_POINTER(aURI);
-
-  *aURI = mReferrerURI;
-  NS_IF_ADDREF(*aURI);
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsDocShell::InitSessionHistory() {
   MOZ_ASSERT(!mIsBeingDestroyed);
 
   // Make sure that we are the root DocShell, and set a handle to root docshell
   // in the session history.
   nsCOMPtr<nsIDocShellTreeItem> root;
   GetSameTypeRootTreeItem(getter_AddRefs(root));
   if (root != this) {
@@ -5779,27 +5764,23 @@ nsresult nsDocShell::ForceRefreshURIFrom
 }
 
 NS_IMETHODIMP
 nsDocShell::ForceRefreshURI(nsIURI* aURI, nsIPrincipal* aPrincipal,
                             int32_t aDelay, bool aMetaRefresh) {
   NS_ENSURE_ARG(aURI);
 
   RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aURI);
-
   /* We do need to pass in a referrer, but we don't want it to
    * be sent to the server.
-   */
-  loadState->SetSendReferrer(false);
-
-  /* for most refreshes the current URI is an appropriate
+   * For most refreshes the current URI is an appropriate
    * internal referrer
    */
-  loadState->SetReferrer(mCurrentURI);
-
+  nsCOMPtr<nsIReferrerInfo> referrerInfo =
+      new ReferrerInfo(mCurrentURI, mozilla::net::RP_Unset, false);
   loadState->SetOriginalURI(mCurrentURI);
   loadState->SetResultPrincipalURI(aURI);
   loadState->SetResultPrincipalURIIsSome(true);
   loadState->SetKeepResultPrincipalURIIfSet(true);
 
   // Set the triggering pricipal to aPrincipal if available, or current
   // document's principal otherwise.
   nsCOMPtr<nsIPrincipal> principal = aPrincipal;
@@ -5824,25 +5805,22 @@ nsDocShell::ForceRefreshURI(nsIURI* aURI
      * we have in mind (15000 ms as defined by REFRESH_REDIRECT_TIMER).
      * Pass a REPLACE flag to LoadURI().
      */
     loadState->SetLoadType(LOAD_NORMAL_REPLACE);
 
     /* for redirects we mimic HTTP, which passes the
      *  original referrer
      */
-    nsCOMPtr<nsIURI> internalReferrer;
-    GetReferringURI(getter_AddRefs(internalReferrer));
-    if (internalReferrer) {
-      loadState->SetReferrer(internalReferrer);
-    }
+    referrerInfo = mReferrerInfo;
   } else {
     loadState->SetLoadType(LOAD_REFRESH);
   }
 
+  loadState->SetReferrerInfo(referrerInfo);
   loadState->SetLoadFlags(
       nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL);
   loadState->SetFirstParty(true);
 
   /*
    * LoadURI(...) will cancel all refresh timers... This causes the
    * Timer and its refreshData instance to be released...
    */
@@ -6101,17 +6079,17 @@ nsDocShell::SetupRefreshURI(nsIChannel* 
           do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
       NS_ENSURE_SUCCESS(rv, rv);
 
       nsCOMPtr<nsIPrincipal> principal;
       rv = secMan->GetChannelResultPrincipal(aChannel,
                                              getter_AddRefs(principal));
       NS_ENSURE_SUCCESS(rv, rv);
 
-      SetupReferrerFromChannel(aChannel);
+      SetupReferrerInfoFromChannel(aChannel);
       rv = SetupRefreshURIFromHeader(mCurrentURI, principal, refreshHeader);
       if (NS_SUCCEEDED(rv)) {
         return NS_REFRESHURI_HEADER_FOUND;
       }
     }
   }
   return rv;
 }
@@ -6484,29 +6462,25 @@ void nsDocShell::OnRedirectStateChange(n
     // 1. Internal redirects are ignored because they are specific to the
     //    channel implementation.
     // 2. POSTs are not saved by global history.
     //
     // Regardless, we need to propagate the previous visit to the new
     // channel.
     SaveLastVisit(aNewChannel, previousURI, previousFlags);
   } else {
-    nsCOMPtr<nsIURI> referrer;
-    // Treat referrer as null if there is an error getting it.
-    (void)NS_GetReferrerFromChannel(aOldChannel, getter_AddRefs(referrer));
-
     // Get the HTTP response code, if available.
     uint32_t responseStatus = 0;
     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aOldChannel);
     if (httpChannel) {
       Unused << httpChannel->GetResponseStatus(&responseStatus);
     }
 
     // Add visit N -1 => N
-    AddURIVisit(oldURI, referrer, previousURI, previousFlags, responseStatus);
+    AddURIVisit(oldURI, previousURI, previousFlags, responseStatus);
 
     // Since N + 1 could be the final destination, we will not save N => N + 1
     // here.  OnNewURI will do that, so we will cache it.
     SaveLastVisit(aNewChannel, oldURI, aRedirectFlags);
   }
 
   // check if the new load should go through the application cache.
   nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
@@ -8750,21 +8724,17 @@ nsresult nsDocShell::PerformRetargeting(
       MOZ_ASSERT(!aLoadState->SHEntry());
       MOZ_ASSERT(aLoadState->FirstParty());  // Windowwatcher will assume this.
 
       RefPtr<nsDocShellLoadState> loadState =
           new nsDocShellLoadState(aLoadState->URI());
 
       // Set up our loadinfo so it will do the load as much like we would have
       // as possible.
-      loadState->SetReferrer(aLoadState->Referrer());
-      loadState->SetReferrerPolicy(
-          (mozilla::net::ReferrerPolicy)aLoadState->ReferrerPolicy());
-      loadState->SetSendReferrer(
-          !(aLoadState->HasLoadFlags(INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER)));
+      loadState->SetReferrerInfo(aLoadState->GetReferrerInfo());
       loadState->SetOriginalURI(aLoadState->OriginalURI());
 
       Maybe<nsCOMPtr<nsIURI>> resultPrincipalURI;
       aLoadState->GetMaybeResultPrincipalURI(resultPrincipalURI);
 
       loadState->SetMaybeResultPrincipalURI(resultPrincipalURI);
       loadState->SetKeepResultPrincipalURIIfSet(
           aLoadState->KeepResultPrincipalURIIfSet());
@@ -9349,20 +9319,24 @@ nsresult nsDocShell::InternalLoad(nsDocS
     mTiming->NotifyUnloadAccepted(mCurrentURI);
   }
 
   // Check if the webbrowser chrome wants the load to proceed; this can be
   // used to cancel attempts to load URIs in the wrong process.
   nsCOMPtr<nsIWebBrowserChrome3> browserChrome3 = do_GetInterface(mTreeOwner);
   if (browserChrome3) {
     bool shouldLoad;
+    nsCOMPtr<nsIURI> referrer;
+    nsIReferrerInfo* referrerInfo = aLoadState->GetReferrerInfo();
+    if (referrerInfo) {
+      referrerInfo->GetOriginalReferrer(getter_AddRefs(referrer));
+    }
     rv = browserChrome3->ShouldLoadURI(
-        this, aLoadState->URI(), aLoadState->Referrer(),
-        !!aLoadState->PostDataStream(), aLoadState->TriggeringPrincipal(),
-        &shouldLoad);
+        this, aLoadState->URI(), referrer, !!aLoadState->PostDataStream(),
+        aLoadState->TriggeringPrincipal(), &shouldLoad);
     if (NS_SUCCEEDED(rv) && !shouldLoad) {
       return NS_OK;
     }
   }
 
   // Whenever a top-level browsing context is navigated, the user agent MUST
   // lock the orientation of the document to the document's default
   // orientation. We don't explicitly check for a top-level browsing context
@@ -10052,40 +10026,47 @@ nsresult nsDocShell::DoURILoad(nsDocShel
       SetMixedContentChannel(nullptr);
     }
   }
 
   // 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()) {
       rv = httpChannelInternal->SetDocumentURI(aLoadState->URI());
       MOZ_ASSERT(NS_SUCCEEDED(rv));
     } else {
-      rv = httpChannelInternal->SetDocumentURI(aLoadState->Referrer());
+      rv = httpChannelInternal->SetDocumentURI(referrer);
       MOZ_ASSERT(NS_SUCCEEDED(rv));
     }
     rv = httpChannelInternal->SetRedirectMode(
         nsIHttpChannelInternal::REDIRECT_MODE_MANUAL);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
   }
 
   nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(channel));
   if (props) {
     // save true referrer for those who need it (e.g. xpinstall whitelisting)
     // Currently only http and ftp channels support this.
     props->SetPropertyAsInterface(
-        NS_LITERAL_STRING("docshell.internalReferrer"), aLoadState->Referrer());
+        NS_LITERAL_STRING("docshell.internalReferrer"), referrer);
   }
 
   nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(channel));
   /* Get the cache Key from SH */
   uint32_t cacheKey = 0;
   if (cacheChannel) {
     if (mLSHE) {
       cacheKey = mLSHE->GetCacheKey();
@@ -10148,21 +10129,20 @@ nsresult nsDocShell::DoURILoad(nsDocShel
     }
   }
 
   if (httpChannel) {
     if (aLoadState->HeadersStream()) {
       rv = AddHeadersToChannel(aLoadState->HeadersStream(), httpChannel);
     }
     // Set the referrer explicitly
-    if (aLoadState->Referrer() &&
+    if (referrer &&
         !(aLoadState->HasLoadFlags(INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER))) {
       // Referrer is currenly only set for link clicks here.
-      rv = httpChannel->SetReferrerWithPolicy(aLoadState->Referrer(),
-                                              aLoadState->ReferrerPolicy());
+      rv = httpChannel->SetReferrerWithPolicy(referrer, referrerPolicy);
       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);
@@ -10540,28 +10520,27 @@ nsresult nsDocShell::ScrollToAnchor(bool
     // return value; failure to scroll here (e.g. if there is no
     // root scrollframe) is not grounds for canceling the load!
     SetCurScrollPosEx(0, 0);
   }
 
   return NS_OK;
 }
 
-void nsDocShell::SetupReferrerFromChannel(nsIChannel* aChannel) {
+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)) {
-      SetReferrerURI(referrer);
-    }
-    uint32_t referrerPolicy;
-    rv = httpChannel->GetReferrerPolicy(&referrerPolicy);
-    if (NS_SUCCEEDED(rv)) {
-      SetReferrerPolicy(referrerPolicy);
+      uint32_t referrerPolicy;
+      rv = httpChannel->GetReferrerPolicy(&referrerPolicy);
+      if (NS_SUCCEEDED(rv)) {
+        SetReferrerInfo(new ReferrerInfo(referrer, referrerPolicy));
+      }
     }
   }
 }
 
 bool nsDocShell::OnNewURI(nsIURI* aURI, nsIChannel* aChannel,
                           nsIPrincipal* aTriggeringPrincipal,
                           nsIPrincipal* aPrincipalToInherit, uint32_t aLoadType,
                           bool aFireOnLocationChange, bool aAddToGlobalHistory,
@@ -10781,23 +10760,17 @@ bool nsDocShell::OnNewURI(nsIURI* aURI, 
 
     if (aLoadType & LOAD_CMD_RELOAD) {
       // On a reload request, we don't set redirecting flags.
       previousURI = aURI;
     } else {
       ExtractLastVisit(aChannel, getter_AddRefs(previousURI), &previousFlags);
     }
 
-    // Note: We don't use |referrer| when our global history is
-    //       based on IHistory.
-    nsCOMPtr<nsIURI> referrer;
-    // Treat referrer as null if there is an error getting it.
-    (void)NS_GetReferrerFromChannel(aChannel, getter_AddRefs(referrer));
-
-    AddURIVisit(aURI, referrer, previousURI, previousFlags, responseStatus);
+    AddURIVisit(aURI, previousURI, previousFlags, responseStatus);
   }
 
   // If this was a history load or a refresh, or it was a history load but
   // later changed to LOAD_NORMAL_REPLACE due to redirection, update the index
   // in session history.
   if (rootSH && ((mLoadType & (LOAD_CMD_HISTORY | LOAD_CMD_RELOAD)) ||
                  mLoadType == LOAD_NORMAL_REPLACE)) {
     mPreviousEntryIndex = rootSH->Index();
@@ -10811,17 +10784,17 @@ bool nsDocShell::OnNewURI(nsIURI* aURI, 
 
   // aCloneSHChildren exactly means "we are not loading a new document".
   uint32_t locationFlags =
       aCloneSHChildren ? uint32_t(LOCATION_CHANGE_SAME_DOCUMENT) : 0;
 
   bool onLocationChangeNeeded =
       SetCurrentURI(aURI, aChannel, aFireOnLocationChange, locationFlags);
   // Make sure to store the referrer from the channel, if any
-  SetupReferrerFromChannel(aChannel);
+  SetupReferrerInfoFromChannel(aChannel);
   return onLocationChangeNeeded;
 }
 
 bool nsDocShell::OnLoadingSite(nsIChannel* aChannel, bool aFireOnLocationChange,
                                bool aAddToGlobalHistory) {
   nsCOMPtr<nsIURI> uri;
   // If this a redirect, use the final url (uri)
   // else use the original url
@@ -10830,22 +10803,18 @@ bool nsDocShell::OnLoadingSite(nsIChanne
   NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
   NS_ENSURE_TRUE(uri, false);
 
   // Pass false for aCloneSHChildren, since we're loading a new page here.
   return OnNewURI(uri, aChannel, nullptr, nullptr, mLoadType,
                   aFireOnLocationChange, aAddToGlobalHistory, false);
 }
 
-void nsDocShell::SetReferrerURI(nsIURI* aURI) {
-  mReferrerURI = aURI;  // This assigment addrefs
-}
-
-void nsDocShell::SetReferrerPolicy(uint32_t aReferrerPolicy) {
-  mReferrerPolicy = aReferrerPolicy;
+void nsDocShell::SetReferrerInfo(nsIReferrerInfo* aReferrerInfo) {
+  mReferrerInfo = aReferrerInfo;  // This assigment addrefs
 }
 
 //*****************************************************************************
 // nsDocShell: Session History
 //*****************************************************************************
 
 NS_IMETHODIMP
 nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
@@ -11136,17 +11105,17 @@ nsDocShell::AddState(JS::Handle<JS::Valu
     document->SetDocumentURI(newURI);
     // We can't trust SetCurrentURI to do always fire locationchange events
     // when we expect it to, so we hack around that by doing it ourselves...
     SetCurrentURI(newURI, nullptr, false, LOCATION_CHANGE_SAME_DOCUMENT);
     if (mLoadType != LOAD_ERROR_PAGE) {
       FireDummyOnLocationChange();
     }
 
-    AddURIVisit(newURI, oldURI, oldURI, 0);
+    AddURIVisit(newURI, oldURI, 0);
 
     // AddURIVisit doesn't set the title for the new URI in global history,
     // so do that here.
     UpdateGlobalHistoryTitle(newURI);
 
     // Inform the favicon service that our old favicon applies to this new
     // URI.
     CopyFavicon(oldURI, newURI, document->NodePrincipal(),
@@ -11353,18 +11322,17 @@ nsresult nsDocShell::AddToSessionHistory
                 cacheKey,             // CacheKey
                 mContentTypeHint,     // Content-type
                 triggeringPrincipal,  // Channel or provided principal
                 principalToInherit, mHistoryID, mDynamicallyCreated);
 
   entry->SetOriginalURI(originalURI);
   entry->SetResultPrincipalURI(resultPrincipalURI);
   entry->SetLoadReplace(loadReplace);
-  entry->SetReferrerURI(referrerURI);
-  entry->SetReferrerPolicy(referrerPolicy);
+  entry->SetReferrerInfo(new ReferrerInfo(referrerURI, referrerPolicy));
   nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(aChannel);
   if (inStrmChan) {
     bool isSrcdocChannel;
     inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
     if (isSrcdocChannel) {
       nsAutoString srcdoc;
       inStrmChan->GetSrcdocData(srcdoc);
       entry->SetSrcdocData(srcdoc);
@@ -11462,23 +11430,22 @@ nsresult nsDocShell::LoadHistoryEntry(ns
   }
 
   NS_ENSURE_TRUE(aEntry, NS_ERROR_FAILURE);
 
   nsCOMPtr<nsIURI> uri = aEntry->GetURI();
   nsCOMPtr<nsIURI> originalURI = aEntry->GetOriginalURI();
   nsCOMPtr<nsIURI> resultPrincipalURI = aEntry->GetResultPrincipalURI();
   bool loadReplace = aEntry->GetLoadReplace();
-  nsCOMPtr<nsIURI> referrerURI = aEntry->GetReferrerURI();
-  uint32_t referrerPolicy = aEntry->GetReferrerPolicy();
   nsCOMPtr<nsIInputStream> postData = aEntry->GetPostData();
   nsAutoCString contentType;
   aEntry->GetContentType(contentType);
   nsCOMPtr<nsIPrincipal> triggeringPrincipal = aEntry->GetTriggeringPrincipal();
   nsCOMPtr<nsIPrincipal> principalToInherit = aEntry->GetPrincipalToInherit();
+  nsCOMPtr<nsIReferrerInfo> referrerInfo = aEntry->GetReferrerInfo();
 
   // Calling CreateAboutBlankContentViewer can set mOSHE to null, and if
   // that's the only thing holding a ref to aEntry that will cause aEntry to
   // die while we're loading it.  So hold a strong ref to aEntry here, just
   // in case.
   nsCOMPtr<nsISHEntry> kungFuDeathGrip(aEntry);
   nsresult rv;
   if (SchemeIsJavascript(uri)) {
@@ -11547,21 +11514,20 @@ nsresult nsDocShell::LoadHistoryEntry(ns
   // Passing nullptr as aSourceDocShell gives the same behaviour as before
   // aSourceDocShell was introduced. According to spec we should be passing
   // the source browsing context that was used when the history entry was
   // first created. bug 947716 has been created to address this issue.
   Maybe<nsCOMPtr<nsIURI>> emplacedResultPrincipalURI;
   emplacedResultPrincipalURI.emplace(std::move(resultPrincipalURI));
 
   RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(uri);
+  loadState->SetReferrerInfo(referrerInfo);
   loadState->SetOriginalURI(originalURI);
   loadState->SetMaybeResultPrincipalURI(emplacedResultPrincipalURI);
   loadState->SetLoadReplace(loadReplace);
-  loadState->SetReferrer(referrerURI);
-  loadState->SetReferrerPolicy((mozilla::net::ReferrerPolicy)referrerPolicy);
   loadState->SetTriggeringPrincipal(triggeringPrincipal);
   loadState->SetPrincipalToInherit(principalToInherit);
   loadState->SetLoadFlags(flags);
   loadState->SetTypeHint(contentType);
   loadState->SetPostDataStream(postData);
   loadState->SetLoadType(aLoadType);
   loadState->SetSHEntry(aEntry);
   loadState->SetFirstParty(true);
@@ -11814,18 +11780,17 @@ void nsDocShell::SaveLastVisit(nsIChanne
   }
 
   props->SetPropertyAsInterface(NS_LITERAL_STRING("docshell.previousURI"),
                                 aURI);
   props->SetPropertyAsUint32(NS_LITERAL_STRING("docshell.previousFlags"),
                              aChannelRedirectFlags);
 }
 
-void nsDocShell::AddURIVisit(nsIURI* aURI, nsIURI* aReferrerURI,
-                             nsIURI* aPreviousURI,
+void nsDocShell::AddURIVisit(nsIURI* aURI, nsIURI* aPreviousURI,
                              uint32_t aChannelRedirectFlags,
                              uint32_t aResponseStatus) {
   MOZ_ASSERT(aURI, "Visited URI is null!");
   MOZ_ASSERT(mLoadType != LOAD_ERROR_PAGE && mLoadType != LOAD_BYPASS_HISTORY,
              "Do not add error or bypass pages to global history");
 
   // Only content-type docshells save URI visits.  Also don't do
   // anything here if we're not supposed to use global history.
@@ -12605,48 +12570,48 @@ nsDocShell::OnLinkClickSync(nsIContent* 
     if (aNoOpenerImplied) {
       flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
     }
   }
 
   // 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
-  // referer, since the current URI in this docshell may be a
+  // 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
   // 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);
   if (!mScriptGlobal ||
       mScriptGlobal->AsOuter()->GetCurrentInnerWindow() != refererInner) {
     // We're no longer the current inner window
     return NS_OK;
   }
 
-  nsCOMPtr<nsIURI> referer = refererDoc->GetDocumentURI();
+  nsCOMPtr<nsIURI> referrer = refererDoc->GetDocumentURI();
   uint32_t refererPolicy = refererDoc->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;
     }
   }
 
-  // referer could be null here in some odd cases, but that's ok,
-  // we'll just load the link w/o sending a referer in those cases.
+  // 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;
   RefPtr<HTMLAnchorElement> anchor = HTMLAnchorElement::FromNode(aContent);
   if (anchor) {
     anchor->GetType(typeHint);
     NS_ConvertUTF16toUTF8 utf8Hint(typeHint);
     nsAutoCString type, dummy;
@@ -12659,34 +12624,37 @@ nsDocShell::OnLinkClickSync(nsIContent* 
   bool inOnLoadHandler = false;
   GetIsExecutingOnLoadHandler(&inOnLoadHandler);
   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);
   RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aURI);
-  loadState->SetReferrer(referer);
-  loadState->SetReferrerPolicy((mozilla::net::ReferrerPolicy)refererPolicy);
+  loadState->SetReferrerInfo(referrerInfo);
   loadState->SetTriggeringPrincipal(triggeringPrincipal);
   loadState->SetPrincipalToInherit(aContent->NodePrincipal());
   loadState->SetLoadFlags(flags);
   loadState->SetTarget(aTargetSpec);
   loadState->SetTypeHint(NS_ConvertUTF16toUTF8(typeHint));
   loadState->SetFileName(aFileName);
   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, referer, refererPolicy);
+    nsPingListener::DispatchPings(this, aContent, aURI, referrer,
+                                  refererPolicy);
   }
   return rv;
 }
 
 NS_IMETHODIMP
 nsDocShell::OnOverLink(nsIContent* aContent, nsIURI* aURI,
                        const nsAString& aTargetSpec) {
   if (aContent->IsEditable()) {
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -83,16 +83,17 @@ class nsIPrompt;
 class nsIScrollableFrame;
 class nsISecureBrowserUI;
 class nsISHistory;
 class nsIStringBundle;
 class nsIURIFixup;
 class nsIURILoader;
 class nsIWebBrowserFind;
 class nsIWidget;
+class nsIReferrerInfo;
 
 class nsDocShell;
 class nsDocShellEditorData;
 class nsDOMNavigationTiming;
 class nsDSURIContentListener;
 class nsGlobalWindowInner;
 class nsGlobalWindowOuter;
 
@@ -656,33 +657,31 @@ class nsDocShell final : public nsDocLoa
    * Helper function for adding a URI visit using IHistory.
    *
    * The IHistory API maintains chains of visits, tracking both HTTP referrers
    * and redirects for a user session. VisitURI requires the current URI and
    * the previous URI in the chain.
    *
    * Visits can be saved either during a redirect or when the request has
    * reached its final destination. The previous URI in the visit may be
-   * from another redirect or it may be the referrer.
+   * from another redirect.
    *
    * @pre aURI is not null.
    *
    * @param aURI
    *        The URI that was just visited
-   * @param aReferrerURI
-   *        The referrer URI of this request
    * @param aPreviousURI
-   *        The previous URI of this visit (may be the same as aReferrerURI)
+   *        The previous URI of this visit
    * @param aChannelRedirectFlags
    *        For redirects, the redirect flags from nsIChannelEventSink
    *        (0 otherwise)
    * @param aResponseStatus
    *        For HTTP channels, the response code (0 otherwise).
    */
-  void AddURIVisit(nsIURI* aURI, nsIURI* aReferrerURI, nsIURI* aPreviousURI,
+  void AddURIVisit(nsIURI* aURI, nsIURI* aPreviousURI,
                    uint32_t aChannelRedirectFlags,
                    uint32_t aResponseStatus = 0);
 
   // Sets the current document's current state object to the given SHEntry's
   // state object. The current state object is eventually given to the page
   // in the PopState event.
   nsresult SetDocCurrentStateObj(nsISHEntry* aShEntry);
 
@@ -823,19 +822,18 @@ class nsDocShell final : public nsDocLoa
   // children docshells.
   void FirePageHideNotificationInternal(bool aIsUnload,
                                         bool aSkipCheckingDynEntries);
 
   // Dispatch a runnable to the TabGroup associated to this docshell.
   nsresult DispatchToTabGroup(mozilla::TaskCategory aCategory,
                               already_AddRefed<nsIRunnable>&& aRunnable);
 
-  void SetupReferrerFromChannel(nsIChannel* aChannel);
-  void SetReferrerURI(nsIURI* aURI);
-  void SetReferrerPolicy(uint32_t aReferrerPolicy);
+  void SetupReferrerInfoFromChannel(nsIChannel* aChannel);
+  void SetReferrerInfo(nsIReferrerInfo* aReferrerInfo);
   void ReattachEditorToWindow(nsISHEntry* aSHEntry);
   void RecomputeCanExecuteScripts();
   void ClearFrameHistory(nsISHEntry* aEntry);
   void UpdateGlobalHistoryTitle(nsIURI* aURI);
   bool IsFrame();
   bool CanSetOriginAttributes();
   bool ShouldBlockLoadingForBackButton();
   bool ShouldDiscardLayoutState(nsIHttpChannel* aChannel);
@@ -959,17 +957,17 @@ class nsDocShell final : public nsDocLoa
    */
   nsCString mContentTypeHint;
 
   // An observed docshell wrapper is created when recording markers is enabled.
   mozilla::UniquePtr<mozilla::ObservedDocShell> mObserved;
 
   // mCurrentURI should be marked immutable on set if possible.
   nsCOMPtr<nsIURI> mCurrentURI;
-  nsCOMPtr<nsIURI> mReferrerURI;
+  nsCOMPtr<nsIReferrerInfo> mReferrerInfo;
 
   // Reference to the SHEntry for this docshell until the page is destroyed.
   // Somebody give me better name
   nsCOMPtr<nsISHEntry> mOSHE;
 
   // Reference to the SHEntry for this docshell until the page is loaded
   // Somebody give me better name.
   // If mLSHE is non-null, non-pushState subframe loads don't create separate
@@ -1048,17 +1046,16 @@ class nsDocShell final : public nsDocLoa
   // -1 if the docshell is added dynamically to the parent shell.
   int32_t mChildOffset;
 
   uint32_t mSandboxFlags;
   BusyFlags mBusyFlags;
   AppType mAppType;
   uint32_t mLoadType;
   uint32_t mDefaultLoadFlags;
-  uint32_t mReferrerPolicy;
   uint32_t mFailedLoadType;
 
   // Are we a regular frame, a browser frame, or an app frame?
   FrameType mFrameType;
 
   // This represents the state of private browsing in the docshell.
   // Currently treated as a binary value: 1 - in private mode, 0 - not private
   // mode On content docshells mPrivateBrowsingId ==
--- a/docshell/base/nsDocShellLoadState.cpp
+++ b/docshell/base/nsDocShellLoadState.cpp
@@ -5,33 +5,32 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsDocShellLoadState.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIWebNavigation.h"
 #include "nsIChildChannel.h"
+#include "ReferrerInfo.h"
 
 #include "mozilla/OriginAttributes.h"
 #include "mozilla/NullPrincipal.h"
 
 #include "mozilla/dom/PContent.h"
 
 nsDocShellLoadState::nsDocShellLoadState(nsIURI* aURI)
     : mURI(aURI),
       mResultPrincipalURIIsSome(false),
       mKeepResultPrincipalURIIfSet(false),
       mLoadReplace(false),
       mInheritPrincipal(false),
       mPrincipalIsExplicit(false),
       mForceAllowDataURI(false),
       mOriginalFrameSrc(false),
-      mSendReferrer(true),
-      mReferrerPolicy(mozilla::net::RP_Unset),
       mLoadType(LOAD_NORMAL),
       mTarget(),
       mSrcdocData(VoidString()),
       mLoadFlags(0),
       mFirstParty(false),
       mTypeHint(VoidCString()),
       mFileName(VoidString()),
       mIsFromProcessingFrameAttributes(false) {
@@ -42,27 +41,27 @@ nsDocShellLoadState::nsDocShellLoadState
   MOZ_ASSERT(aLoadState.URI(), "Cannot create a LoadState with a null URI!");
   mResultPrincipalURIIsSome = aLoadState.ResultPrincipalURIIsSome();
   mKeepResultPrincipalURIIfSet = aLoadState.KeepResultPrincipalURIIfSet();
   mLoadReplace = aLoadState.LoadReplace();
   mInheritPrincipal = aLoadState.InheritPrincipal();
   mPrincipalIsExplicit = aLoadState.PrincipalIsExplicit();
   mForceAllowDataURI = aLoadState.ForceAllowDataURI();
   mOriginalFrameSrc = aLoadState.OriginalFrameSrc();
-  mSendReferrer = aLoadState.SendReferrer();
-  mReferrerPolicy = (mozilla::net::ReferrerPolicy)aLoadState.ReferrerPolicy();
   mLoadType = aLoadState.LoadType();
   mTarget = aLoadState.Target();
   mLoadFlags = aLoadState.LoadFlags();
   mFirstParty = aLoadState.FirstParty();
   mTypeHint = aLoadState.TypeHint();
   mFileName = aLoadState.FileName();
   mIsFromProcessingFrameAttributes =
       aLoadState.IsFromProcessingFrameAttributes();
-  mReferrer = aLoadState.Referrer();
+  mReferrerInfo =
+      new ReferrerInfo(aLoadState.Referrer(), aLoadState.ReferrerPolicy(),
+                       aLoadState.SendReferrer());
   mURI = aLoadState.URI();
   mOriginalURI = aLoadState.OriginalURI();
   mBaseURI = aLoadState.BaseURI();
   mTriggeringPrincipal = aLoadState.TriggeringPrincipal();
   mPrincipalToInherit = aLoadState.PrincipalToInherit();
 }
 
 nsDocShellLoadState::~nsDocShellLoadState() {}
@@ -100,20 +99,22 @@ nsresult nsDocShellLoadState::CreateFrom
   }
   loadState->SetTriggeringPrincipal(loadInfo->TriggeringPrincipal());
 
   // Return the newly created loadState.
   loadState.forget(aResult);
   return NS_OK;
 }
 
-nsIURI* nsDocShellLoadState::Referrer() const { return mReferrer; }
+nsIReferrerInfo* nsDocShellLoadState::GetReferrerInfo() const {
+  return mReferrerInfo;
+}
 
-void nsDocShellLoadState::SetReferrer(nsIURI* aReferrer) {
-  mReferrer = aReferrer;
+void nsDocShellLoadState::SetReferrerInfo(nsIReferrerInfo* aReferrerInfo) {
+  mReferrerInfo = aReferrerInfo;
 }
 
 nsIURI* nsDocShellLoadState::URI() const { return mURI; }
 
 void nsDocShellLoadState::SetURI(nsIURI* aURI) { mURI = aURI; }
 
 nsIURI* nsDocShellLoadState::OriginalURI() const { return mOriginalURI; }
 
@@ -226,31 +227,16 @@ void nsDocShellLoadState::SetPostDataStr
 nsIInputStream* nsDocShellLoadState::HeadersStream() const {
   return mHeadersStream;
 }
 
 void nsDocShellLoadState::SetHeadersStream(nsIInputStream* aHeadersStream) {
   mHeadersStream = aHeadersStream;
 }
 
-bool nsDocShellLoadState::SendReferrer() const { return mSendReferrer; }
-
-void nsDocShellLoadState::SetSendReferrer(bool aSendReferrer) {
-  mSendReferrer = aSendReferrer;
-}
-
-mozilla::net::ReferrerPolicy nsDocShellLoadState::ReferrerPolicy() const {
-  return mReferrerPolicy;
-}
-
-void nsDocShellLoadState::SetReferrerPolicy(
-    mozilla::net::ReferrerPolicy aReferrerPolicy) {
-  mReferrerPolicy = aReferrerPolicy;
-}
-
 const nsString& nsDocShellLoadState::SrcdocData() const { return mSrcdocData; }
 
 void nsDocShellLoadState::SetSrcdocData(const nsAString& aSrcdocData) {
   mSrcdocData = aSrcdocData;
 }
 
 nsIDocShell* nsDocShellLoadState::SourceDocShell() const {
   return mSourceDocShell;
@@ -392,19 +378,20 @@ nsresult nsDocShellLoadState::SetupTrigg
   // triggered the load. If there is no referrer URI, we fall back to using the
   // SystemPrincipal. It's safe to assume that no provided triggeringPrincipal
   // and no referrer simulate a load that was triggered by the system. It's
   // important to note that this block of code needs to appear *after* the block
   // where we munge the principalToInherit, because otherwise we would never
   // enter code blocks checking if the principalToInherit is null and we will
   // end up with a wrong inheritPrincipal flag.
   if (!mTriggeringPrincipal) {
-    if (mReferrer) {
+    if (mReferrerInfo) {
+      nsCOMPtr<nsIURI> referrer = mReferrerInfo->GetOriginalReferrer();
       mTriggeringPrincipal =
-          BasePrincipal::CreateCodebasePrincipal(mReferrer, aOriginAttributes);
+          BasePrincipal::CreateCodebasePrincipal(referrer, aOriginAttributes);
 
       if (!mTriggeringPrincipal) {
         return NS_ERROR_FAILURE;
       }
     } else {
       mTriggeringPrincipal = nsContentUtils::GetSystemPrincipal();
     }
   }
@@ -416,20 +403,19 @@ void nsDocShellLoadState::CalculateLoadU
   mLoadFlags = 0;
 
   if (mInheritPrincipal) {
     MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(mPrincipalToInherit),
                "Should not inherit SystemPrincipal");
     mLoadFlags |= nsDocShell::INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL;
   }
 
-  if (!mSendReferrer) {
+  if (mReferrerInfo && !mReferrerInfo->GetSendReferrer()) {
     mLoadFlags |= nsDocShell::INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER;
   }
-
   if (oldLoadFlags & nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
     mLoadFlags |= nsDocShell::INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
   }
 
   if (oldLoadFlags & nsIWebNavigation::LOAD_FLAGS_FIRST_LOAD) {
     mLoadFlags |= nsDocShell::INTERNAL_LOAD_FLAGS_FIRST_LOAD;
   }
 
@@ -458,26 +444,26 @@ DocShellLoadStateInit nsDocShellLoadStat
   DocShellLoadStateInit loadState;
   loadState.ResultPrincipalURIIsSome() = mResultPrincipalURIIsSome;
   loadState.KeepResultPrincipalURIIfSet() = mKeepResultPrincipalURIIfSet;
   loadState.LoadReplace() = mLoadReplace;
   loadState.InheritPrincipal() = mInheritPrincipal;
   loadState.PrincipalIsExplicit() = mPrincipalIsExplicit;
   loadState.ForceAllowDataURI() = mForceAllowDataURI;
   loadState.OriginalFrameSrc() = mOriginalFrameSrc;
-  loadState.SendReferrer() = mSendReferrer;
-  loadState.ReferrerPolicy() = mReferrerPolicy;
   loadState.LoadType() = mLoadType;
   loadState.Target() = mTarget;
   loadState.LoadFlags() = mLoadFlags;
   loadState.FirstParty() = mFirstParty;
   loadState.TypeHint() = mTypeHint;
   loadState.FileName() = mFileName;
   loadState.IsFromProcessingFrameAttributes() =
       mIsFromProcessingFrameAttributes;
-  loadState.Referrer() = mReferrer;
   loadState.URI() = mURI;
   loadState.OriginalURI() = mOriginalURI;
   loadState.BaseURI() = mBaseURI;
   loadState.TriggeringPrincipal() = mTriggeringPrincipal;
   loadState.PrincipalToInherit() = mPrincipalToInherit;
+  loadState.Referrer() = mReferrerInfo->GetOriginalReferrer();
+  loadState.SendReferrer() = mReferrerInfo->GetSendReferrer();
+  loadState.ReferrerPolicy() = mReferrerInfo->GetReferrerPolicy();
   return loadState;
 }
--- a/docshell/base/nsDocShellLoadState.h
+++ b/docshell/base/nsDocShellLoadState.h
@@ -13,16 +13,17 @@
 #include "nsDocShellLoadTypes.h"
 #include "mozilla/net/ReferrerPolicy.h"
 
 class nsIInputStream;
 class nsISHEntry;
 class nsIURI;
 class nsIDocShell;
 class nsIChildChannel;
+class nsIReferrerInfo;
 class OriginAttibutes;
 namespace mozilla {
 namespace dom {
 class DocShellLoadStateInit;
 }  // namespace dom
 }  // namespace mozilla
 
 /**
@@ -36,19 +37,19 @@ class nsDocShellLoadState final {
   explicit nsDocShellLoadState(nsIURI* aURI);
   explicit nsDocShellLoadState(mozilla::dom::DocShellLoadStateInit& aLoadState);
 
   static nsresult CreateFromPendingChannel(nsIChildChannel* aPendingChannel,
                                            nsDocShellLoadState** aResult);
 
   // Getters and Setters
 
-  nsIURI* Referrer() const;
+  nsIReferrerInfo* GetReferrerInfo() const;
 
-  void SetReferrer(nsIURI* aReferrer);
+  void SetReferrerInfo(nsIReferrerInfo* aReferrerInfo);
 
   nsIURI* URI() const;
 
   void SetURI(nsIURI* aURI);
 
   nsIURI* OriginalURI() const;
 
   void SetOriginalURI(nsIURI* aOriginalURI);
@@ -108,24 +109,16 @@ class nsDocShellLoadState final {
   nsIInputStream* PostDataStream() const;
 
   void SetPostDataStream(nsIInputStream* aStream);
 
   nsIInputStream* HeadersStream() const;
 
   void SetHeadersStream(nsIInputStream* aHeadersStream);
 
-  bool SendReferrer() const;
-
-  void SetSendReferrer(bool aSendReferrer);
-
-  mozilla::net::ReferrerPolicy ReferrerPolicy() const;
-
-  void SetReferrerPolicy(mozilla::net::ReferrerPolicy aReferrerPolicy);
-
   bool IsSrcdocLoad() const;
 
   const nsString& SrcdocData() const;
 
   void SetSrcdocData(const nsAString& aSrcdocData);
 
   nsIDocShell* SourceDocShell() const;
 
@@ -200,17 +193,17 @@ class nsDocShellLoadState final {
 
  protected:
   // Destructor can't be defaulted or inlined, as header doesn't have all type
   // includes it needs to do so.
   ~nsDocShellLoadState();
 
  protected:
   // This is the referrer for the load.
-  nsCOMPtr<nsIURI> mReferrer;
+  nsCOMPtr<nsIReferrerInfo> mReferrerInfo;
 
   // The URI we are navigating to. Will not be null once set.
   nsCOMPtr<nsIURI> mURI;
 
   // The URI to set as the originalURI on the channel that does the load. If
   // null, aURI will be set as the originalURI.
   nsCOMPtr<nsIURI> mOriginalURI;
 
@@ -268,24 +261,16 @@ class nsDocShellLoadState final {
   // If this attribute is true, then a top-level navigation
   // to a data URI will be allowed.
   bool mForceAllowDataURI;
 
   // If this attribute is true, this load corresponds to a frame
   // element loading its original src (or srcdoc) attribute.
   bool mOriginalFrameSrc;
 
-  // True if the referrer should be sent, false if it shouldn't be sent, even if
-  // it's available. This attribute defaults to true.
-  bool mSendReferrer;
-
-  // Referrer policy for the load. This attribute holds one of the values
-  // (REFERRER_POLICY_*) defined in nsIHttpChannel.
-  mozilla::net::ReferrerPolicy mReferrerPolicy;
-
   // Contains a load type as specified by the nsDocShellLoadTypes::load*
   // constants
   uint32_t mLoadType;
 
   // Active Session History entry (if loading from SH)
   nsCOMPtr<nsISHEntry> mSHEntry;
 
   // Target for load, like _content, _blank etc.
--- a/docshell/base/nsIWebNavigation.idl
+++ b/docshell/base/nsIWebNavigation.idl
@@ -233,17 +233,17 @@ interface nsIWebNavigation : nsISupports
    * loading.
    *
    * @param aURI
    *        The URI string to load.  For HTTP and FTP URLs and possibly others,
    *        characters above U+007F will be converted to UTF-8 and then URL-
    *        escaped per the rules of RFC 2396.
    * @param aLoadURIOptions
    *        A JSObject defined in LoadURIOptions.webidl holding info like e.g.
-   *        the triggeringPrincipal, the referrer URI, the referrer policy.
+   *        the triggeringPrincipal, the referrer info.
    */
   [implicit_jscontext, binaryname(LoadURIFromScript)]
   void loadURI(in AString aURI,
                in jsval   aLoadURIOptions);
 
   /**
    * A C++ friendly version of loadURI
    */
@@ -303,21 +303,16 @@ interface nsIWebNavigation : nsISupports
    * for unexpected error situations.
    */
   readonly attribute Document document;
 
   /**
    * The currently loaded URI or null.
    */
   readonly attribute nsIURI currentURI;
- 
-  /**
-   * The referring URI for the currently loaded URI or null.
-   */
-  readonly attribute nsIURI referringURI;
 
   /**
    * The session history object used by this web navigation instance. This
    * object will be a mozilla::dom::ChildSHistory object, but is returned as
    * nsISupports so it can be called from JS code.
    */
   [binaryname(SessionHistoryXPCOM)]
   readonly attribute nsISupports sessionHistory;
--- a/docshell/shistory/nsISHEntry.idl
+++ b/docshell/shistory/nsISHEntry.idl
@@ -16,16 +16,17 @@ interface nsILayoutHistoryState;
 interface nsIContentViewer;
 interface nsIURI;
 interface nsIInputStream;
 interface nsIDocShellTreeItem;
 interface nsIStructuredCloneContainer;
 interface nsIBFCacheEntry;
 interface nsIPrincipal;
 interface nsISHistory;
+interface nsIReferrerInfo;
 
 %{C++
 #include "nsRect.h"
 class nsDocShellEditorData;
 class nsSHEntryShared;
 %}
 [ref] native nsIntRect(nsIntRect);
 [ptr] native nsDocShellEditorDataPtr(nsDocShellEditorData);
@@ -65,24 +66,18 @@ interface nsISHEntry : nsISupports
     /**
      * Was the entry created as a result of a subframe navigation?
      * - Will be 'false' when a frameset page is visited for the first time.
      * - Will be 'true' for all history entries created as a result of a
      *   subframe navigation.
      */
     [infallible] attribute boolean isSubFrame;
 
-    /** Referrer URI */
-    [infallible] attribute nsIURI referrerURI;
-
-    /**
-     * Referrer policy, holding one of the values (REFERRER_POLICY_*) defined
-     * in nsIHttpChannel.
-     */
-    [infallible] attribute unsigned long referrerPolicy;
+    /** Referrer Info*/
+    [infallible] attribute nsIReferrerInfo referrerInfo;
 
     /** Content viewer, for fast restoration of presentation */
     [infallible] attribute nsIContentViewer contentViewer;
 
     /** Whether the content viewer is marked "sticky" */
     [infallible] attribute boolean sticky;
 
     /** Saved state of the global window object */
--- a/docshell/shistory/nsSHEntry.cpp
+++ b/docshell/shistory/nsSHEntry.cpp
@@ -15,24 +15,24 @@
 #include "nsIInputStream.h"
 #include "nsILayoutHistoryState.h"
 #include "nsIStructuredCloneContainer.h"
 #include "nsIURI.h"
 #include "nsSHEntryShared.h"
 #include "nsSHistory.h"
 
 #include "mozilla/net/ReferrerPolicy.h"
+#include "nsIReferrerInfo.h"
 
 namespace dom = mozilla::dom;
 
 static uint32_t gEntryID = 0;
 
 nsSHEntry::nsSHEntry()
     : mShared(new nsSHEntryShared()),
-      mReferrerPolicy(mozilla::net::RP_Unset),
       mLoadType(0),
       mID(gEntryID++),
       mScrollPositionX(0),
       mScrollPositionY(0),
       mParent(nullptr),
       mLoadReplace(false),
       mURIWasModified(false),
       mIsSrcdocEntry(false),
@@ -40,18 +40,17 @@ nsSHEntry::nsSHEntry()
       mLoadedInThisProcess(false),
       mPersist(true) {}
 
 nsSHEntry::nsSHEntry(const nsSHEntry& aOther)
     : mShared(aOther.mShared),
       mURI(aOther.mURI),
       mOriginalURI(aOther.mOriginalURI),
       mResultPrincipalURI(aOther.mResultPrincipalURI),
-      mReferrerURI(aOther.mReferrerURI),
-      mReferrerPolicy(aOther.mReferrerPolicy),
+      mReferrerInfo(aOther.mReferrerInfo),
       mTitle(aOther.mTitle),
       mPostData(aOther.mPostData),
       mLoadType(0)  // XXX why not copy?
       ,
       mID(aOther.mID),
       mScrollPositionX(0)  // XXX why not copy?
       ,
       mScrollPositionY(0)  // XXX why not copy?
@@ -151,37 +150,25 @@ nsSHEntry::GetLoadReplace(bool* aLoadRep
 
 NS_IMETHODIMP
 nsSHEntry::SetLoadReplace(bool aLoadReplace) {
   mLoadReplace = aLoadReplace;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsSHEntry::GetReferrerURI(nsIURI** aReferrerURI) {
-  *aReferrerURI = mReferrerURI;
-  NS_IF_ADDREF(*aReferrerURI);
+nsSHEntry::GetReferrerInfo(nsIReferrerInfo** aReferrerInfo) {
+  *aReferrerInfo = mReferrerInfo;
+  NS_IF_ADDREF(*aReferrerInfo);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsSHEntry::SetReferrerURI(nsIURI* aReferrerURI) {
-  mReferrerURI = aReferrerURI;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsSHEntry::GetReferrerPolicy(uint32_t* aReferrerPolicy) {
-  *aReferrerPolicy = mReferrerPolicy;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsSHEntry::SetReferrerPolicy(uint32_t aReferrerPolicy) {
-  mReferrerPolicy = aReferrerPolicy;
+nsSHEntry::SetReferrerInfo(nsIReferrerInfo* aReferrerInfo) {
+  mReferrerInfo = aReferrerInfo;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSHEntry::SetContentViewer(nsIContentViewer* aViewer) {
   return mShared->SetContentViewer(aViewer);
 }
 
--- a/docshell/shistory/nsSHEntry.h
+++ b/docshell/shistory/nsSHEntry.h
@@ -12,16 +12,17 @@
 #include "nsISHEntry.h"
 #include "nsString.h"
 
 #include "mozilla/Attributes.h"
 
 class nsSHEntryShared;
 class nsIInputStream;
 class nsIURI;
+class nsIReferrerInfo;
 
 class nsSHEntry final : public nsISHEntry {
  public:
   nsSHEntry();
   nsSHEntry(const nsSHEntry& aOther);
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSISHENTRY
@@ -37,18 +38,17 @@ class nsSHEntry final : public nsISHEntr
   // We share the state in here with other SHEntries which correspond to the
   // same document.
   RefPtr<nsSHEntryShared> mShared;
 
   // See nsSHEntry.idl for comments on these members.
   nsCOMPtr<nsIURI> mURI;
   nsCOMPtr<nsIURI> mOriginalURI;
   nsCOMPtr<nsIURI> mResultPrincipalURI;
-  nsCOMPtr<nsIURI> mReferrerURI;
-  uint32_t mReferrerPolicy;
+  nsCOMPtr<nsIReferrerInfo> mReferrerInfo;
   nsString mTitle;
   nsCOMPtr<nsIInputStream> mPostData;
   uint32_t mLoadType;
   uint32_t mID;
   int32_t mScrollPositionX;
   int32_t mScrollPositionY;
   nsISHEntry* mParent;
   nsCOMArray<nsISHEntry> mChildren;
--- a/dom/base/Location.cpp
+++ b/dom/base/Location.cpp
@@ -30,16 +30,17 @@
 #include "nsGlobalWindow.h"
 #include "mozilla/Likely.h"
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/Components.h"
 #include "mozilla/NullPrincipal.h"
 #include "mozilla/Unused.h"
 #include "mozilla/dom/LocationBinding.h"
 #include "mozilla/dom/ScriptSettings.h"
+#include "ReferrerInfo.h"
 
 namespace mozilla {
 namespace dom {
 
 Location::Location(nsPIDOMWindowInner* aWindow, nsIDocShell* aDocShell)
     : mInnerWindow(aWindow) {
   // aDocShell can be null if it gets called after nsDocShell::Destory().
   mDocShell = do_GetWeakReference(aDocShell);
@@ -146,18 +147,19 @@ already_AddRefed<nsDocShellLoadState> Lo
   }
 
   // Create load info
   RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aURI);
 
   loadState->SetTriggeringPrincipal(triggeringPrincipal);
 
   if (sourceURI) {
-    loadState->SetReferrer(sourceURI);
-    loadState->SetReferrerPolicy(referrerPolicy);
+    nsCOMPtr<nsIReferrerInfo> referrerInfo =
+        new ReferrerInfo(sourceURI, referrerPolicy);
+    loadState->SetReferrerInfo(referrerInfo);
   }
 
   return loadState.forget();
 }
 
 nsresult Location::GetURI(nsIURI** aURI, bool aGetInnermostURI) {
   *aURI = nullptr;
 
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -47,16 +47,17 @@
 #include "nsGlobalWindow.h"
 #include "nsHTMLDocument.h"
 #include "nsPIWindowRoot.h"
 #include "nsLayoutUtils.h"
 #include "nsMappedAttributes.h"
 #include "nsView.h"
 #include "nsBaseWidget.h"
 #include "nsQueryObject.h"
+#include "ReferrerInfo.h"
 
 #include "nsIURI.h"
 #include "nsIURL.h"
 #include "nsNetUtil.h"
 
 #include "nsGkAtoms.h"
 #include "nsNameSpaceManager.h"
 
@@ -425,35 +426,34 @@ nsresult nsFrameLoader::ReallyStartLoadi
   // Use referrer as long as it is not an NullPrincipalURI.
   // We could add a method such as GetReferrerURI to principals to make this
   // cleaner, but given that we need to start using Source Browsing Context for
   // referrer (see Bug 960639) this may be wasted effort at this stage.
   if (referrer) {
     bool isNullPrincipalScheme;
     rv = referrer->SchemeIs(NS_NULLPRINCIPAL_SCHEME, &isNullPrincipalScheme);
     if (NS_SUCCEEDED(rv) && !isNullPrincipalScheme) {
-      loadState->SetReferrer(referrer);
+      // get referrer policy for this iframe:
+      // first load document wide policy, then
+      // load iframe referrer attribute if enabled in preferences
+      // per element referrer overrules document wide referrer if enabled
+      net::ReferrerPolicy referrerPolicy =
+          mOwnerContent->OwnerDoc()->GetReferrerPolicy();
+      HTMLIFrameElement* iframe = HTMLIFrameElement::FromNode(mOwnerContent);
+      if (iframe) {
+        net::ReferrerPolicy iframeReferrerPolicy =
+            iframe->GetReferrerPolicyAsEnum();
+        if (iframeReferrerPolicy != net::RP_Unset) {
+          referrerPolicy = iframeReferrerPolicy;
+        }
+      }
+      loadState->SetReferrerInfo(new ReferrerInfo(referrer, referrerPolicy));
     }
   }
 
-  // get referrer policy for this iframe:
-  // first load document wide policy, then
-  // load iframe referrer attribute if enabled in preferences
-  // per element referrer overrules document wide referrer if enabled
-  net::ReferrerPolicy referrerPolicy =
-      mOwnerContent->OwnerDoc()->GetReferrerPolicy();
-  HTMLIFrameElement* iframe = HTMLIFrameElement::FromNode(mOwnerContent);
-  if (iframe) {
-    net::ReferrerPolicy iframeReferrerPolicy =
-        iframe->GetReferrerPolicyAsEnum();
-    if (iframeReferrerPolicy != net::RP_Unset) {
-      referrerPolicy = iframeReferrerPolicy;
-    }
-  }
-  loadState->SetReferrerPolicy(referrerPolicy);
 
   // Default flags:
   int32_t flags = nsIWebNavigation::LOAD_FLAGS_NONE;
 
   // Flags for browser frame:
   if (OwnerIsMozBrowserFrame()) {
     flags = nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP |
             nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL;
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1891,8 +1891,9 @@ addExternalIface('TabParent', nativeType
 addExternalIface('URI', nativeType='nsIURI', headerFile='nsIURI.h',
                  notflattened=True)
 addExternalIface('XULCommandDispatcher', notflattened=True)
 addExternalIface('XULTemplateResult', nativeType='nsIXULTemplateResult',
                  notflattened=True)
 addExternalIface('XULTemplateRuleFilter', nativeType='nsIXULTemplateRuleFilter',
                  notflattened=True)
 addExternalIface('nsISHistory', nativeType='nsISHistory', notflattened=True)
+addExternalIface('ReferrerInfo', nativeType='nsIReferrerInfo')
--- a/dom/clients/manager/ClientNavigateOpChild.cpp
+++ b/dom/clients/manager/ClientNavigateOpChild.cpp
@@ -11,16 +11,17 @@
 #include "nsIDocShell.h"
 #include "nsDocShellLoadState.h"
 #include "nsIWebNavigation.h"
 #include "nsIWebProgress.h"
 #include "nsIWebProgressListener.h"
 #include "nsNetUtil.h"
 #include "nsPIDOMWindow.h"
 #include "nsURLHelper.h"
+#include "ReferrerInfo.h"
 
 namespace mozilla {
 namespace dom {
 
 namespace {
 
 class NavigateLoadListener final : public nsIWebProgressListener,
                                    public nsSupportsWeakReference {
@@ -221,19 +222,20 @@ RefPtr<ClientOpPromise> ClientNavigateOp
   nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
   nsCOMPtr<nsIWebProgress> webProgress = do_GetInterface(docShell);
   if (!docShell || !webProgress) {
     return ClientOpPromise::CreateAndReject(NS_ERROR_DOM_INVALID_STATE_ERR,
                                             __func__);
   }
 
   RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(url);
-
+  nsCOMPtr<nsIReferrerInfo> referrerInfo =
+      new ReferrerInfo(doc->GetDocumentURI(), doc->GetReferrerPolicy());
   loadState->SetTriggeringPrincipal(principal);
-  loadState->SetReferrerPolicy(doc->GetReferrerPolicy());
+  loadState->SetReferrerInfo(referrerInfo);
   loadState->SetLoadType(LOAD_STOP_CONTENT);
   loadState->SetSourceDocShell(docShell);
   loadState->SetLoadFlags(nsIWebNavigation::LOAD_FLAGS_NONE);
   loadState->SetFirstParty(true);
   rv = docShell->LoadURI(loadState);
   if (NS_FAILED(rv)) {
     return ClientOpPromise::CreateAndReject(rv, __func__);
   }
--- a/dom/interfaces/security/moz.build
+++ b/dom/interfaces/security/moz.build
@@ -4,13 +4,14 @@
 # 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/.
 
 with Files("**"):
     BUG_COMPONENT = ("Core", "DOM: Security")
 
 XPIDL_SOURCES += [
     'nsIContentSecurityManager.idl',
-    'nsIContentSecurityPolicy.idl'
+    'nsIContentSecurityPolicy.idl',
+    'nsIReferrerInfo.idl'
 ]
 
 XPIDL_MODULE = 'dom_security'
 
new file mode 100644
--- /dev/null
+++ b/dom/interfaces/security/nsIReferrerInfo.idl
@@ -0,0 +1,39 @@
+/* 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 "nsISupports.idl"
+#include "nsISerializable.idl"
+
+interface 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;
+
+  /**
+  * Indicates if the referrer should not be sent or not even when it's available.
+  */
+  [infallible] attribute boolean sendReferrer;
+
+  /**
+   * 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.
+   */
+
+  void init(in uint32_t aReferrerPolicy,
+           [optional] in boolean aSendReferrer,
+           [optional] in nsIURI aOriginalReferrer);
+};
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -161,16 +161,17 @@
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsContentPermissionHelper.h"
 #include "nsPluginHost.h"
 #ifdef NS_PRINTING
 #  include "nsPrintingProxy.h"
 #endif
 #include "nsWindowMemoryReporter.h"
+#include "nsIReferrerInfo.h"
 
 #include "IHistory.h"
 #include "nsNetUtil.h"
 
 #include "base/message_loop.h"
 #include "base/process_util.h"
 #include "base/task.h"
 
@@ -773,22 +774,22 @@ static nsresult GetCreateWindowParams(mo
   NS_ADDREF(*aTriggeringPrincipal = doc->NodePrincipal());
   nsCOMPtr<nsIURI> baseURI = doc->GetDocBaseURI();
   if (!baseURI) {
     NS_ERROR("Document didn't return a base URI");
     return NS_ERROR_FAILURE;
   }
 
   baseURI->GetSpec(aBaseURIString);
-
   if (aLoadState) {
-    if (!aLoadState->SendReferrer()) {
+    nsCOMPtr<nsIReferrerInfo> referrerInfo = aLoadState->GetReferrerInfo();
+    if (referrerInfo && referrerInfo->GetSendReferrer()) {
+      referrerInfo->GetReferrerPolicy(aReferrerPolicy);
+    } else {
       *aReferrerPolicy = mozilla::net::RP_No_Referrer;
-    } else {
-      *aReferrerPolicy = aLoadState->ReferrerPolicy();
     }
   }
 
   RefPtr<nsDocShell> openerDocShell =
       static_cast<nsDocShell*>(opener->GetDocShell());
   if (!openerDocShell) {
     return NS_OK;
   }
new file mode 100644
--- /dev/null
+++ b/dom/security/ReferrerInfo.cpp
@@ -0,0 +1,117 @@
+/* -*- 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 "nsIObjectInputStream.h"
+#include "nsIObjectOutputStream.h"
+#include "nsIClassInfoImpl.h"
+
+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)
+
+ReferrerInfo::ReferrerInfo(nsIURI* aOriginalReferrer,
+                           uint32_t aPolicy,
+                           bool aSendReferrer)
+    : mOriginalReferrer(aOriginalReferrer),
+      mPolicy(aPolicy),
+      mSendReferrer(aSendReferrer) {}
+
+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;
+}
+
+NS_IMETHODIMP
+ReferrerInfo::Init(uint32_t aReferrerPolicy,
+                   bool aSendReferrer,
+                   nsIURI* aOriginalReferrer) {
+  mPolicy = aReferrerPolicy;
+  mSendReferrer = aSendReferrer;
+  mOriginalReferrer = aOriginalReferrer;
+  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)) {
+    return rv;
+  }
+
+  mOriginalReferrer = do_QueryInterface(supports);
+
+  rv = aStream->Read32(&mPolicy);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  rv = aStream->ReadBoolean(&mSendReferrer);
+  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)) {
+    return rv;
+  }
+
+  rv = aStream->Write32(mPolicy);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  rv = aStream->WriteBoolean(mSendReferrer);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+}  // namespace dom
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/security/ReferrerInfo.h
@@ -0,0 +1,61 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_ReferrerInfo_h
+#define mozilla_dom_ReferrerInfo_h
+
+#include "nsCOMPtr.h"
+#include "nsIReferrerInfo.h"
+#include "nsISerializable.h"
+#include "mozilla/net/ReferrerPolicy.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;
+
+namespace mozilla {
+namespace dom {
+
+/**
+ * ReferrerInfo class holds a original referrer URL, as well as the referrer
+ * policy to be applied to this referrer.
+ *
+ **/
+class ReferrerInfo : public nsIReferrerInfo {
+ public:
+  ReferrerInfo () = default;
+  explicit ReferrerInfo(nsIURI* aOriginalReferrer,
+                        uint32_t aPolicy = mozilla::net::RP_Unset,
+                        bool aSendReferrer = true);
+
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIREFERRERINFO
+  NS_DECL_NSISERIALIZABLE
+
+ private:
+  virtual ~ReferrerInfo() {}
+
+  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;
+};
+
+}  // namespace dom
+}  // namespace mozilla
+
+#endif  // mozilla_dom_ReferrerInfo_h
--- a/dom/security/moz.build
+++ b/dom/security/moz.build
@@ -16,37 +16,40 @@ EXPORTS.mozilla.dom += [
     'CSPEvalChecker.h',
     'FramingChecker.h',
     'nsContentSecurityManager.h',
     'nsCSPContext.h',
     'nsCSPService.h',
     'nsCSPUtils.h',
     'nsMixedContentBlocker.h',
     'PolicyTokenizer.h',
+    'ReferrerInfo.h',
     'SRICheck.h',
     'SRILogHelper.h',
     'SRIMetadata.h',
 ]
 
 EXPORTS += [
     'nsContentSecurityManager.h',
     'nsMixedContentBlocker.h',
+    'ReferrerInfo.h',
 ]
 
 UNIFIED_SOURCES += [
     'ContentVerifier.cpp',
     'CSPEvalChecker.cpp',
     'FramingChecker.cpp',
     'nsContentSecurityManager.cpp',
     'nsCSPContext.cpp',
     'nsCSPParser.cpp',
     'nsCSPService.cpp',
     'nsCSPUtils.cpp',
     'nsMixedContentBlocker.cpp',
     'PolicyTokenizer.cpp',
+    'ReferrerInfo.cpp',
     'SRICheck.cpp',
     'SRIMetadata.cpp',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
--- a/dom/webidl/LoadURIOptions.webidl
+++ b/dom/webidl/LoadURIOptions.webidl
@@ -1,15 +1,16 @@
 /* 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/. */
 
 interface Principal;
 interface URI;
 interface InputStream;
+interface ReferrerInfo;
 
 /**
  * This dictionary holds load arguments for docshell loads.
  */
 
 dictionary LoadURIOptions {
   /**
    * The principal that initiated the load.
@@ -18,27 +19,20 @@ dictionary LoadURIOptions {
 
   /**
    * Flags modifying load behaviour.  This parameter is a bitwise
    * combination of the load flags defined in nsIWebNavigation.idl.
    */
    long loadFlags = 0;
 
   /**
-   * The referring URI.  If this argument is null, then the referring
-   * URI will be inferred internally.
+   * The referring info of the load.  If this argument is null, then the
+   * referrer URI and referrer policy will be inferred internally.
    */
-  URI? referrerURI = null;
-
-  /**
-   * Referrer Policy for the load, defaults to REFERRER_POLICY_UNSET.
-   * Alternatively use one of REFERRER_POLICY_* constants from
-   * nsIHttpChannel.
-   */
-  long referrerPolicy = 0;
+   ReferrerInfo? referrerInfo = null;
 
   /**
    * If the URI to be loaded corresponds to a HTTP request, then this stream is
    * appended directly to the HTTP request headers.  It may be prefixed
    * with additional HTTP headers.  This stream must contain a "\r\n"
    * sequence separating any HTTP headers from the HTTP request body.
    */
   InputStream? postData = null;
@@ -52,9 +46,9 @@ dictionary LoadURIOptions {
    InputStream? headers = null;
 
   /**
    * Set to indicate a base URI to be associated with the load. Note
    * that at present this argument is only used with view-source aURIs
    * and cannot be used to resolve aURI.
    */
   URI? baseURI = null;
-};
\ No newline at end of file
+};
--- a/layout/build/components.conf
+++ b/layout/build/components.conf
@@ -260,16 +260,22 @@ Classes = [
         'type': 'nsMixedContentBlocker',
         'headers': ['mozilla/dom/nsMixedContentBlocker.h'],
         'categories': {
             'content-policy': '@mozilla.org/mixedcontentblocker;1',
             'net-channel-event-sinks': '@mozilla.org/mixedcontentblocker;1',
         },
     },
     {
+        'cid': '{041a129f-10ce-4bda-a60d-e027a26d5ed0}',
+        'contract_ids': ['@mozilla.org/referrer-info;1'],
+        'type': 'mozilla::dom::ReferrerInfo',
+        'headers': ['mozilla/dom/ReferrerInfo.h'],
+    },
+    {
         'cid': '{4bbe1b96-8956-457f-a03f-9c27435f2afa}',
         'contract_ids': ['@mozilla.org/net/osfileconstantsservice;1'],
         'singleton': True,
         'type': 'mozilla::OSFileConstantsService',
         'headers': ['mozilla/OSFileConstants.h'],
         'constructor': 'mozilla::OSFileConstantsService::GetOrCreate',
     },
     {
--- a/toolkit/components/browser/nsWebBrowser.cpp
+++ b/toolkit/components/browser/nsWebBrowser.cpp
@@ -606,23 +606,16 @@ nsWebBrowser::Stop(uint32_t aStopFlags) 
 
 NS_IMETHODIMP
 nsWebBrowser::GetCurrentURI(nsIURI** aURI) {
   NS_ENSURE_STATE(mDocShell);
 
   return mDocShellAsNav->GetCurrentURI(aURI);
 }
 
-NS_IMETHODIMP
-nsWebBrowser::GetReferringURI(nsIURI** aURI) {
-  NS_ENSURE_STATE(mDocShell);
-
-  return mDocShellAsNav->GetReferringURI(aURI);
-}
-
 // XXX(nika): Consider making the mozilla::dom::ChildSHistory version the
 // canonical one?
 NS_IMETHODIMP
 nsWebBrowser::GetSessionHistoryXPCOM(nsISupports** aSessionHistory) {
   NS_ENSURE_ARG_POINTER(aSessionHistory);
   *aSessionHistory = nullptr;
   if (mDocShell) {
     RefPtr<mozilla::dom::ChildSHistory> shistory =
--- a/toolkit/components/windowwatcher/nsWindowWatcher.cpp
+++ b/toolkit/components/windowwatcher/nsWindowWatcher.cpp
@@ -66,16 +66,17 @@
 #include "mozilla/dom/Storage.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/TabGroup.h"
 #include "nsIXULWindow.h"
 #include "nsIXULBrowserWindow.h"
 #include "nsGlobalWindow.h"
+#include "ReferrerInfo.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 /****************************************************************
  ******************** nsWatcherWindowEntry **********************
  ****************************************************************/
 
@@ -1072,19 +1073,19 @@ nsresult nsWindowWatcher::OpenWindowInte
        Also using GetDocument to force document creation seems to
        screw up focus in the hidden window; see bug 36016.
     */
     RefPtr<Document> doc = GetEntryDocument();
     if (!doc && parentWindow) {
       doc = parentWindow->GetExtantDoc();
     }
     if (doc) {
-      // Set the referrer
-      loadState->SetReferrer(doc->GetDocumentURI());
-      loadState->SetReferrerPolicy(doc->GetReferrerPolicy());
+      nsCOMPtr<nsIReferrerInfo> referrerInfo =
+          new ReferrerInfo(doc->GetDocumentURI(), doc->GetReferrerPolicy());
+      loadState->SetReferrerInfo(referrerInfo);
     }
   }
 
   if (isNewToplevelWindow) {
     // Notify observers that the window is open and ready.
     // The window has not yet started to load a document.
     nsCOMPtr<nsIObserverService> obsSvc =
         mozilla::services::GetObserverService();