Bug 704320 - Parse and implement meta tag-based referrer policies for documents and nsHttpChannels. (r=bz,mcmanus)
authorOwen Chu <owenchu@gmail.com>
Tue, 18 Nov 2014 08:46:29 -0500
changeset 228117 4c2d59fd05fb757fa64d113c6ddc497d091e810d
parent 228116 bf73bf22dfba714bc4de5e9a5e5095f07ef21bd3
child 228118 a35d5482084117afe7005794de8627327683627b
push id91
push userdburns@mozilla.com
push dateTue, 18 Nov 2014 22:19:20 +0000
reviewersbz, mcmanus
bugs704320
milestone36.0a1
Bug 704320 - Parse and implement meta tag-based referrer policies for documents and nsHttpChannels. (r=bz,mcmanus)
browser/components/sessionstore/SessionHistory.jsm
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
docshell/base/nsDocShellLoadInfo.cpp
docshell/base/nsDocShellLoadInfo.h
docshell/base/nsIDocShell.idl
docshell/base/nsIDocShellLoadInfo.idl
docshell/shistory/public/nsISHEntry.idl
docshell/shistory/src/nsSHEntry.cpp
docshell/shistory/src/nsSHEntry.h
dom/base/nsDocument.cpp
dom/base/nsDocument.h
dom/base/nsFrameLoader.cpp
dom/base/nsGkAtomList.h
dom/base/nsIDocument.h
dom/html/HTMLMetaElement.cpp
netwerk/base/public/ReferrerPolicy.h
netwerk/base/public/moz.build
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/http/HttpBaseChannel.h
netwerk/protocol/http/HttpChannelParent.cpp
netwerk/protocol/http/nsIHttpChannel.idl
netwerk/protocol/viewsource/nsViewSourceChannel.cpp
parser/html/nsHtml5SpeculativeLoad.cpp
parser/html/nsHtml5SpeculativeLoad.h
parser/html/nsHtml5TreeBuilderCppSupplement.h
parser/html/nsHtml5TreeOpExecutor.cpp
parser/html/nsHtml5TreeOpExecutor.h
--- a/browser/components/sessionstore/SessionHistory.jsm
+++ b/browser/components/sessionstore/SessionHistory.jsm
@@ -182,18 +182,20 @@ let SessionHistoryInternal = {
       // nsISerializable or something.
       entry.cacheKey = cacheKey.data;
     }
     entry.ID = shEntry.ID;
     entry.docshellID = shEntry.docshellID;
 
     // We will include the property only if it's truthy to save a couple of
     // bytes when the resulting object is stringified and saved to disk.
-    if (shEntry.referrerURI)
+    if (shEntry.referrerURI) {
       entry.referrer = shEntry.referrerURI.spec;
+      entry.referrerPolicy = shEntry.referrerPolicy;
+    }
 
     if (shEntry.srcdocData)
       entry.srcdocData = shEntry.srcdocData;
 
     if (shEntry.isSrcdocEntry)
       entry.isSrcdocEntry = shEntry.isSrcdocEntry;
 
     if (shEntry.baseURI)
@@ -335,18 +337,20 @@ let SessionHistoryInternal = {
 
     shEntry.setURI(Utils.makeURI(entry.url));
     shEntry.setTitle(entry.title || entry.url);
     if (entry.subframe)
       shEntry.setIsSubFrame(entry.subframe || false);
     shEntry.loadType = Ci.nsIDocShellLoadInfo.loadHistory;
     if (entry.contentType)
       shEntry.contentType = entry.contentType;
-    if (entry.referrer)
+    if (entry.referrer) {
       shEntry.referrerURI = Utils.makeURI(entry.referrer);
+      shEntry.referrerPolicy = entry.referrerPolicy;
+    }
     if (entry.isSrcdocEntry)
       shEntry.srcdocData = entry.srcdocData;
     if (entry.baseURI)
       shEntry.baseURI = Utils.makeURI(entry.baseURI);
 
     if (entry.cacheKey) {
       var cacheKey = Cc["@mozilla.org/supports-PRUint32;1"].
                      createInstance(Ci.nsISupportsPRUint32);
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -33,16 +33,17 @@
 #include "nsIDOMElement.h"
 #include "nsIDOMStorage.h"
 #include "nsIContentViewer.h"
 #include "nsIDocumentLoaderFactory.h"
 #include "nsCURILoader.h"
 #include "nsDocShellCID.h"
 #include "nsDOMCID.h"
 #include "nsNetUtil.h"
+#include "mozilla/net/ReferrerPolicy.h"
 #include "nsRect.h"
 #include "prenv.h"
 #include "nsIDOMWindow.h"
 #include "nsIGlobalObject.h"
 #include "nsIWebBrowserChrome.h"
 #include "nsPoint.h"
 #include "nsIObserverService.h"
 #include "nsIPrompt.h"
@@ -549,16 +550,17 @@ nsPingListener::AsyncOnChannelRedirect(n
 }
 
 struct SendPingInfo {
   int32_t numPings;
   int32_t maxPings;
   bool    requireSameHost;
   nsIURI *target;
   nsIURI *referrer;
+  uint32_t referrerPolicy;
 };
 
 static void
 SendPing(void *closure, nsIContent *content, nsIURI *uri, nsIIOService *ios)
 {
   SendPingInfo *info = static_cast<SendPingInfo *>(closure);
   if (info->maxPings > -1 && info->numPings >= info->maxPings)
     return;
@@ -628,17 +630,17 @@ SendPing(void *closure, nsIContent *cont
       if (NS_SUCCEEDED(info->referrer->GetSpec(pingFrom)))
         httpChan->SetRequestHeader(NS_LITERAL_CSTRING("Ping-From"), pingFrom, false);
     }
 
     // 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)
-      httpChan->SetReferrer(info->referrer);
+      httpChan->SetReferrerWithPolicy(info->referrer, info->referrerPolicy);
   }
 
   nsCOMPtr<nsIUploadChannel2> uploadChan = do_QueryInterface(httpChan);
   if (!uploadChan)
     return;
 
   NS_NAMED_LITERAL_CSTRING(uploadData, "PING");
 
@@ -686,28 +688,32 @@ SendPing(void *closure, nsIContent *cont
     // If we failed to setup the timer, then we should just cancel the channel
     // because we won't be able to ensure that it goes away in a timely manner.
     chan->Cancel(NS_ERROR_ABORT);
   }
 }
 
 // Spec: http://whatwg.org/specs/web-apps/current-work/#ping
 static void
-DispatchPings(nsIContent *content, nsIURI *target, nsIURI *referrer)
+DispatchPings(nsIContent *content,
+              nsIURI *target,
+              nsIURI *referrer,
+              uint32_t referrerPolicy)
 {
   SendPingInfo info;
 
   if (!PingsEnabled(&info.maxPings, &info.requireSameHost))
     return;
   if (info.maxPings == 0)
     return;
 
   info.numPings = 0;
   info.target = target;
   info.referrer = referrer;
+  info.referrerPolicy = referrerPolicy;
 
   ForEachPing(content, SendPing, &info);
 }
 
 static nsDOMPerformanceNavigationType
 ConvertLoadTypeToNavigationType(uint32_t aLoadType)
 {
   // Not initialized, assume it's normal load.
@@ -1341,16 +1347,17 @@ nsDocShell::LoadURI(nsIURI * aURI,
     }
     nsCOMPtr<nsIURI> referrer;
     nsCOMPtr<nsIInputStream> postStream;
     nsCOMPtr<nsIInputStream> headersStream;
     nsCOMPtr<nsISupports> owner;
     bool inheritOwner = false;
     bool ownerIsExplicit = false;
     bool sendReferrer = true;
+    uint32_t referrerPolicy = mozilla::net::RP_Default;
     bool isSrcdoc = false;
     nsCOMPtr<nsISHEntry> shEntry;
     nsXPIDLString target;
     nsAutoString srcdoc;
     nsCOMPtr<nsIDocShell> sourceDocShell;
     nsCOMPtr<nsIURI> baseURI;
 
     uint32_t loadType = MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags);    
@@ -1374,16 +1381,17 @@ nsDocShell::LoadURI(nsIURI * aURI,
         aLoadInfo->GetOwner(getter_AddRefs(owner));
         aLoadInfo->GetInheritOwner(&inheritOwner);
         aLoadInfo->GetOwnerIsExplicit(&ownerIsExplicit);
         aLoadInfo->GetSHEntry(getter_AddRefs(shEntry));
         aLoadInfo->GetTarget(getter_Copies(target));
         aLoadInfo->GetPostDataStream(getter_AddRefs(postStream));
         aLoadInfo->GetHeadersStream(getter_AddRefs(headersStream));
         aLoadInfo->GetSendReferrer(&sendReferrer);
+        aLoadInfo->GetReferrerPolicy(&referrerPolicy);
         aLoadInfo->GetIsSrcdocLoad(&isSrcdoc);
         aLoadInfo->GetSrcdocData(srcdoc);
         aLoadInfo->GetSourceDocShell(getter_AddRefs(sourceDocShell));
         aLoadInfo->GetBaseURI(getter_AddRefs(baseURI));
     }
 
 #if defined(PR_LOGGING) && defined(DEBUG)
     if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
@@ -1597,16 +1605,17 @@ nsDocShell::LoadURI(nsIURI * aURI,
     if (aLoadFlags & LOAD_FLAGS_FORCE_ALLOW_COOKIES)
         flags |= INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES;
 
     if (isSrcdoc)
         flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
 
     return InternalLoad(aURI,
                         referrer,
+                        referrerPolicy,
                         owner,
                         flags,
                         target.get(),
                         nullptr,         // No type hint
                         NullString(),    // No forced download
                         postStream,
                         headersStream,
                         loadType,
@@ -5260,18 +5269,18 @@ nsDocShell::LoadErrorPage(nsIURI *aURI, 
     // end of the URL, so append it last.
     errorPageUrl.AppendLiteral("&d=");
     errorPageUrl.AppendASCII(escapedDescription.get());
 
     nsCOMPtr<nsIURI> errorPageURI;
     rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    return InternalLoad(errorPageURI, nullptr, nullptr,
-                        INTERNAL_LOAD_FLAGS_INHERIT_OWNER, nullptr, nullptr,
+    return InternalLoad(errorPageURI, nullptr, mozilla::net::RP_Default,
+                        nullptr, INTERNAL_LOAD_FLAGS_INHERIT_OWNER, nullptr, nullptr,
                         NullString(), nullptr, nullptr, LOAD_ERROR_PAGE,
                         nullptr, true, NullString(), this, nullptr, nullptr,
                         nullptr);
 }
 
 
 NS_IMETHODIMP
 nsDocShell::Reload(uint32_t aReloadFlags)
@@ -5323,16 +5332,17 @@ nsDocShell::Reload(uint32_t aReloadFlags
             if (doc->IsSrcdocDocument()) {
                 doc->GetSrcdocData(srcdoc);
                 flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
                 baseURI = doc->GetBaseURI();
             }
         }
         rv = InternalLoad(mCurrentURI,
                           mReferrerURI,
+                          mReferrerPolicy,
                           principal,
                           flags,
                           nullptr,         // No window target
                           NS_LossyConvertUTF16toASCII(contentTypeHint).get(),
                           NullString(),    // No forced download
                           nullptr,         // No post data
                           nullptr,         // No headers data
                           loadType,        // Load type
@@ -9225,27 +9235,29 @@ void CopyFavicon(nsIURI *aOldURI, nsIURI
 #endif
 }
 
 } // anonymous namespace
 
 class InternalLoadEvent : public nsRunnable
 {
 public:
-    InternalLoadEvent(nsDocShell* aDocShell, nsIURI * aURI, nsIURI * aReferrer,
+    InternalLoadEvent(nsDocShell* aDocShell, nsIURI * aURI,
+                      nsIURI * aReferrer, uint32_t aReferrerPolicy,
                       nsISupports * aOwner, uint32_t aFlags,
                       const char* aTypeHint, nsIInputStream * aPostData,
                       nsIInputStream * aHeadersData, uint32_t aLoadType,
                       nsISHEntry * aSHEntry, bool aFirstParty,
                       const nsAString &aSrcdoc, nsIDocShell* aSourceDocShell,
                       nsIURI * aBaseURI) :
         mSrcdoc(aSrcdoc),
         mDocShell(aDocShell),
         mURI(aURI),
         mReferrer(aReferrer),
+        mReferrerPolicy(aReferrerPolicy),
         mOwner(aOwner),
         mPostData(aPostData),
         mHeadersData(aHeadersData),
         mSHEntry(aSHEntry),
         mFlags(aFlags),
         mLoadType(aLoadType),
         mFirstParty(aFirstParty),
         mSourceDocShell(aSourceDocShell),
@@ -9253,17 +9265,19 @@ public:
     {
         // Make sure to keep null things null as needed
         if (aTypeHint) {
             mTypeHint = aTypeHint;
         }
     }
 
     NS_IMETHOD Run() {
-        return mDocShell->InternalLoad(mURI, mReferrer, mOwner, mFlags,
+        return mDocShell->InternalLoad(mURI, mReferrer,
+                                       mReferrerPolicy,
+                                       mOwner, mFlags,
                                        nullptr, mTypeHint.get(),
                                        NullString(), mPostData, mHeadersData,
                                        mLoadType, mSHEntry, mFirstParty,
                                        mSrcdoc, mSourceDocShell, mBaseURI,
                                        nullptr, nullptr);
     }
 
 private:
@@ -9271,16 +9285,17 @@ private:
     // Use IDL strings so .get() returns null by default
     nsXPIDLString mWindowTarget;
     nsXPIDLCString mTypeHint;
     nsString mSrcdoc;
 
     nsRefPtr<nsDocShell> mDocShell;
     nsCOMPtr<nsIURI> mURI;
     nsCOMPtr<nsIURI> mReferrer;
+    uint32_t mReferrerPolicy;
     nsCOMPtr<nsISupports> mOwner;
     nsCOMPtr<nsIInputStream> mPostData;
     nsCOMPtr<nsIInputStream> mHeadersData;
     nsCOMPtr<nsISHEntry> mSHEntry;
     uint32_t mFlags;
     uint32_t mLoadType;
     bool mFirstParty;
     nsCOMPtr<nsIDocShell> mSourceDocShell;
@@ -9325,16 +9340,17 @@ nsDocShell::CreatePrincipalFromReferrer(
                                        outPrincipal);
   NS_ENSURE_SUCCESS(rv, rv);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::InternalLoad(nsIURI * aURI,
                          nsIURI * aReferrer,
+                         uint32_t aReferrerPolicy,
                          nsISupports * aOwner,
                          uint32_t aFlags,
                          const char16_t *aWindowTarget,
                          const char* aTypeHint,
                          const nsAString& aFileName,
                          nsIInputStream * aPostData,
                          nsIInputStream * aHeadersData,
                          uint32_t aLoadType,
@@ -9581,16 +9597,17 @@ nsDocShell::InternalLoad(nsIURI * aURI,
 
         //
         // Transfer the load to the target DocShell...  Pass nullptr as the
         // window target name from to prevent recursive retargeting!
         //
         if (NS_SUCCEEDED(rv) && targetDocShell) {
             rv = targetDocShell->InternalLoad(aURI,
                                               aReferrer,
+                                              aReferrerPolicy,
                                               owner,
                                               aFlags,
                                               nullptr,         // No window target
                                               aTypeHint,
                                               NullString(),    // No forced download
                                               aPostData,
                                               aHeadersData,
                                               aLoadType,
@@ -9661,17 +9678,18 @@ nsDocShell::InternalLoad(nsIURI * aURI,
             // the unload event also a replace load, so we don't
             // create extra history entries.
             if (LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
                 mLoadType = LOAD_NORMAL_REPLACE;
             }
             
             // Do this asynchronously
             nsCOMPtr<nsIRunnable> ev =
-                new InternalLoadEvent(this, aURI, aReferrer, aOwner, aFlags,
+                new InternalLoadEvent(this, aURI, aReferrer,
+                                      aReferrerPolicy, aOwner, aFlags,
                                       aTypeHint, aPostData, aHeadersData,
                                       aLoadType, aSHEntry, aFirstParty, aSrcdoc,
                                       aSourceDocShell, aBaseURI);
             return NS_DispatchToCurrentThread(ev);
         }
 
         // Just ignore this load attempt
         return NS_OK;
@@ -10113,16 +10131,17 @@ nsDocShell::InternalLoad(nsIURI * aURI,
 
     mozilla::net::PredictorPredict(aURI, nullptr,
                                    nsINetworkPredictor::PREDICT_LOAD,
                                    this, nullptr);
 
     nsCOMPtr<nsIRequest> req;
     rv = DoURILoad(aURI, aReferrer,
                    !(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER),
+                   aReferrerPolicy,
                    owner, aTypeHint, aFileName, aPostData, aHeadersData,
                    aFirstParty, aDocShell, getter_AddRefs(req),
                    (aFlags & INTERNAL_LOAD_FLAGS_FIRST_LOAD) != 0,
                    (aFlags & INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER) != 0,
                    (aFlags & INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES) != 0,
                    srcdoc, aBaseURI, contentType);
     if (req && aRequest)
         NS_ADDREF(*aRequest = req);
@@ -10186,16 +10205,17 @@ nsDocShell::GetInheritedPrincipal(bool a
 
     return nullptr;
 }
 
 nsresult
 nsDocShell::DoURILoad(nsIURI * aURI,
                       nsIURI * aReferrerURI,
                       bool aSendReferrer,
+                      uint32_t aReferrerPolicy,
                       nsISupports * aOwner,
                       const char * aTypeHint,
                       const nsAString & aFileName,
                       nsIInputStream * aPostData,
                       nsIInputStream * aHeadersData,
                       bool aFirstParty,
                       nsIDocShell ** aDocShell,
                       nsIRequest ** aRequest,
@@ -10515,17 +10535,17 @@ nsDocShell::DoURILoad(nsIURI * aURI,
             }
         }
         if (aHeadersData) {
             rv = AddHeadersToChannel(aHeadersData, httpChannel);
         }
         // Set the referrer explicitly
         if (aReferrerURI && aSendReferrer) {
             // Referrer is currenly only set for link clicks here.
-            httpChannel->SetReferrer(aReferrerURI);
+            httpChannel->SetReferrerWithPolicy(aReferrerURI, aReferrerPolicy);
         }
     }
 
     nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(channel);
     if (scriptChannel) {
         // Allow execution against our context if the principals match
         scriptChannel->
             SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
@@ -10851,16 +10871,21 @@ nsDocShell::SetupReferrerFromChannel(nsI
 {
     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);
+        }
     }
 }
 
 bool
 nsDocShell::OnNewURI(nsIURI * aURI, nsIChannel * aChannel, nsISupports* aOwner,
                      uint32_t aLoadType, bool aFireOnLocationChange,
                      bool aAddToGlobalHistory, bool aCloneSHChildren)
 {
@@ -11118,16 +11143,22 @@ nsDocShell::OnLoadingSite(nsIChannel * a
 }
 
 void
 nsDocShell::SetReferrerURI(nsIURI * aURI)
 {
     mReferrerURI = aURI;        // This assigment addrefs
 }
 
+void
+nsDocShell::SetReferrerPolicy(uint32_t referrerPolicy)
+{
+    mReferrerPolicy = referrerPolicy;
+}
+
 //*****************************************************************************
 // nsDocShell: Session History
 //*****************************************************************************
 
 NS_IMETHODIMP
 nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
                      const nsAString& aURL, bool aReplace, JSContext* aCx)
 {
@@ -11547,16 +11578,17 @@ nsDocShell::AddToSessionHistory(nsIURI *
         if (!entry) {
             return NS_ERROR_OUT_OF_MEMORY;
         }
     }
 
     // Get the post data & referrer
     nsCOMPtr<nsIInputStream> inputStream;
     nsCOMPtr<nsIURI> referrerURI;
+    uint32_t referrerPolicy = mozilla::net::RP_Default;
     nsCOMPtr<nsISupports> cacheKey;
     nsCOMPtr<nsISupports> owner = aOwner;
     bool expired = false;
     bool discardLayoutState = false;
     nsCOMPtr<nsICachingChannel> cacheChannel;
     if (aChannel) {
         cacheChannel = do_QueryInterface(aChannel);
 
@@ -11573,16 +11605,17 @@ nsDocShell::AddToSessionHistory(nsIURI *
             GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
         }
         if (httpChannel) {
             nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
             if (uploadChannel) {
                 uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
             }
             httpChannel->GetReferrer(getter_AddRefs(referrerURI));
+            httpChannel->GetReferrerPolicy(&referrerPolicy);
 
             discardLayoutState = ShouldDiscardLayoutState(httpChannel);
         }
         aChannel->GetOwner(getter_AddRefs(owner));
         if (!owner) {
             nsCOMPtr<nsILoadInfo> loadInfo;
             aChannel->GetLoadInfo(getter_AddRefs(loadInfo));
             if (loadInfo) {
@@ -11603,16 +11636,17 @@ nsDocShell::AddToSessionHistory(nsIURI *
                   inputStream,       // Post data stream
                   nullptr,            // LayoutHistory state
                   cacheKey,          // CacheKey
                   mContentTypeHint,  // Content-type
                   owner,             // Channel or provided owner
                   mHistoryID,
                   mDynamicallyCreated);
     entry->SetReferrerURI(referrerURI);
+    entry->SetReferrerPolicy(referrerPolicy);
     nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(aChannel);
     if (inStrmChan) {
         bool isSrcdocChannel;
         inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
         if (isSrcdocChannel) {
             nsAutoString srcdoc;
             inStrmChan->GetSrcdocData(srcdoc);
             entry->SetSrcdocData(srcdoc);
@@ -11702,24 +11736,27 @@ nsDocShell::LoadHistoryEntry(nsISHEntry 
 {
     if (!IsNavigationAllowed()) {
         return NS_OK;
     }
     
     nsCOMPtr<nsIURI> uri;
     nsCOMPtr<nsIInputStream> postData;
     nsCOMPtr<nsIURI> referrerURI;
+    uint32_t referrerPolicy;
     nsAutoCString contentType;
     nsCOMPtr<nsISupports> owner;
 
     NS_ENSURE_TRUE(aEntry, NS_ERROR_FAILURE);
 
     NS_ENSURE_SUCCESS(aEntry->GetURI(getter_AddRefs(uri)), NS_ERROR_FAILURE);
     NS_ENSURE_SUCCESS(aEntry->GetReferrerURI(getter_AddRefs(referrerURI)),
                       NS_ERROR_FAILURE);
+    NS_ENSURE_SUCCESS(aEntry->GetReferrerPolicy(&referrerPolicy),
+                      NS_ERROR_FAILURE);
     NS_ENSURE_SUCCESS(aEntry->GetPostData(getter_AddRefs(postData)),
                       NS_ERROR_FAILURE);
     NS_ENSURE_SUCCESS(aEntry->GetContentType(contentType), NS_ERROR_FAILURE);
     NS_ENSURE_SUCCESS(aEntry->GetOwner(getter_AddRefs(owner)),
                       NS_ERROR_FAILURE);
 
     // Calling CreateAboutBlankContentViewer can set mOSHE to null, and if
     // that's the only thing holding a ref to aEntry that will cause aEntry to
@@ -11786,16 +11823,17 @@ nsDocShell::LoadHistoryEntry(nsISHEntry 
     }
 
     // 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.
     rv = InternalLoad(uri,
                       referrerURI,
+                      referrerPolicy,
                       owner,
                       flags,
                       nullptr,            // No window target
                       contentType.get(),  // Type hint
                       NullString(),       // No forced file download
                       postData,           // Post data stream
                       nullptr,            // No headers stream
                       aLoadType,          // Load type
@@ -13187,16 +13225,17 @@ nsDocShell::OnLinkClickSync(nsIContent *
   NS_ENSURE_TRUE(refererInner, NS_ERROR_UNEXPECTED);
   if (!mScriptGlobal ||
       mScriptGlobal->GetCurrentInnerWindow() != refererInner) {
       // We're no longer the current inner window
       return NS_OK;
   }
 
   nsCOMPtr<nsIURI> referer = refererDoc->GetDocumentURI();
+  uint32_t refererPolicy = refererDoc->GetReferrerPolicy();
 
   // 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.
 
   nsAutoString target(aTargetSpec);
 
   // If this is an anchor element, grab its type property to use as a hint
   nsAutoString typeHint;
@@ -13215,16 +13254,17 @@ nsDocShell::OnLinkClickSync(nsIContent *
   nsCOMPtr<nsIURI> clonedURI;
   aURI->Clone(getter_AddRefs(clonedURI));
   if (!clonedURI) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   nsresult rv = InternalLoad(clonedURI,                 // New URI
                              referer,                   // Referer URI
+                             refererPolicy,             // Referer policy
                              aContent->NodePrincipal(), // Owner is our node's
                                                         // principal
                              flags,
                              target.get(),              // Window target
                              NS_LossyConvertUTF16toASCII(typeHint).get(),
                              aFileName,                 // Download as file
                              aPostDataStream,           // Post data stream
                              aHeadersDataStream,        // Headers stream
@@ -13232,17 +13272,17 @@ nsDocShell::OnLinkClickSync(nsIContent *
                              nullptr,                   // No SHEntry
                              true,                      // first party site
                              NullString(),              // No srcdoc
                              this,                      // We are the source
                              nullptr,                   // baseURI not needed
                              aDocShell,                 // DocShell out-param
                              aRequest);                 // Request out-param
   if (NS_SUCCEEDED(rv)) {
-    DispatchPings(aContent, aURI, referer);
+    DispatchPings(aContent, aURI, referer, refererPolicy);
   }
   return rv;
 }
 
 NS_IMETHODIMP
 nsDocShell::OnOverLink(nsIContent* aContent,
                        nsIURI* aURI,
                        const char16_t* aTargetSpec)
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -305,16 +305,17 @@ protected:
     // Actually open a channel and perform a URI load.  Note: whatever owner is
     // passed to this function will be set on the channel.  Callers who wish to
     // not have an owner on the channel should just pass null.
     // If aSrcdoc is not void, the load will be considered as a srcdoc load,
     // and the contents of aSrcdoc will be loaded instead of aURI.
     virtual nsresult DoURILoad(nsIURI * aURI,
                                nsIURI * aReferrer,
                                bool aSendReferrer,
+                               uint32_t aReferrerPolicy,
                                nsISupports * aOwner,
                                const char * aTypeHint,
                                const nsAString & aFileName,
                                nsIInputStream * aPostData,
                                nsIInputStream * aHeadersData,
                                bool firstParty,
                                nsIDocShell ** aDocShell,
                                nsIRequest ** aRequest,
@@ -353,16 +354,17 @@ protected:
     // aCloneSHChildren argument as aCloneChildren.
     bool OnNewURI(nsIURI * aURI, nsIChannel * aChannel, nsISupports* aOwner,
                     uint32_t aLoadType,
                     bool aFireOnLocationChange,
                     bool aAddToGlobalHistory,
                     bool aCloneSHChildren);
 
     virtual void SetReferrerURI(nsIURI * aURI);
+    virtual void SetReferrerPolicy(uint32_t referrerPolicy);
 
     // Session History
     virtual bool ShouldAddToSessionHistory(nsIURI * aURI);
     // Either aChannel or aOwner must be null.  If aChannel is
     // present, the owner should be gotten from it.
     // If aCloneChildren is true, then our current session history's
     // children will be cloned onto the new entry.  This should be
     // used when we aren't actually changing the document while adding
@@ -747,16 +749,17 @@ protected:
     nsCOMPtr<nsISupportsArray> mSavedRefreshURIList;
     nsRefPtr<nsDSURIContentListener> mContentListener;
     nsCOMPtr<nsIContentViewer> mContentViewer;
     nsCOMPtr<nsIWidget>        mParentWidget;
 
     // mCurrentURI should be marked immutable on set if possible.
     nsCOMPtr<nsIURI>           mCurrentURI;
     nsCOMPtr<nsIURI>           mReferrerURI;
+    uint32_t                   mReferrerPolicy;
     nsRefPtr<nsGlobalWindow>   mScriptGlobal;
     nsCOMPtr<nsISHistory>      mSessionHistory;
     nsCOMPtr<nsIGlobalHistory2> mGlobalHistory;
     nsCOMPtr<nsIWebBrowserFind> mFind;
     nsCOMPtr<nsICommandManager> mCommandManager;
     // Reference to the SHEntry for this docshell until the page is destroyed.
     // Somebody give me better name
     nsCOMPtr<nsISHEntry>       mOSHE;
--- a/docshell/base/nsDocShellLoadInfo.cpp
+++ b/docshell/base/nsDocShellLoadInfo.cpp
@@ -5,25 +5,27 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Local Includes
 #include "nsDocShellLoadInfo.h"
 #include "nsISHEntry.h"
 #include "nsIInputStream.h"
 #include "nsIURI.h"
 #include "nsIDocShell.h"
+#include "mozilla/net/ReferrerPolicy.h"
 
 //*****************************************************************************
 //***    nsDocShellLoadInfo: Object Management
 //*****************************************************************************
 
 nsDocShellLoadInfo::nsDocShellLoadInfo()
   : mInheritOwner(false),
     mOwnerIsExplicit(false),
     mSendReferrer(true),
+    mReferrerPolicy(mozilla::net::RP_Default),
     mLoadType(nsIDocShellLoadInfo::loadNormal),
     mIsSrcdocLoad(false)
 {
 }
 
 nsDocShellLoadInfo::~nsDocShellLoadInfo()
 {
 }
@@ -187,16 +189,28 @@ NS_IMETHODIMP nsDocShellLoadInfo::GetSen
 }
 
 NS_IMETHODIMP nsDocShellLoadInfo::SetSendReferrer(bool aSendReferrer)
 {
    mSendReferrer = aSendReferrer;
    return NS_OK;
 }
 
+NS_IMETHODIMP nsDocShellLoadInfo::GetReferrerPolicy(nsDocShellInfoReferrerPolicy* aReferrerPolicy)
+{
+   *aReferrerPolicy = mReferrerPolicy;
+   return NS_OK;
+}
+
+NS_IMETHODIMP nsDocShellLoadInfo::SetReferrerPolicy(nsDocShellInfoReferrerPolicy aReferrerPolicy)
+{
+    mReferrerPolicy = aReferrerPolicy;
+    return NS_OK;
+}
+
 NS_IMETHODIMP nsDocShellLoadInfo::GetIsSrcdocLoad(bool* aIsSrcdocLoad)
 {
    *aIsSrcdocLoad = mIsSrcdocLoad;
    return NS_OK;
 }
 
 NS_IMETHODIMP nsDocShellLoadInfo::GetSrcdocData(nsAString &aSrcdocData)
 {
--- a/docshell/base/nsDocShellLoadInfo.h
+++ b/docshell/base/nsDocShellLoadInfo.h
@@ -32,16 +32,17 @@ protected:
   virtual ~nsDocShellLoadInfo();
 
 protected:
   nsCOMPtr<nsIURI>                 mReferrer;
   nsCOMPtr<nsISupports>            mOwner;
   bool                             mInheritOwner;
   bool                             mOwnerIsExplicit;
   bool                             mSendReferrer;
+  nsDocShellInfoReferrerPolicy     mReferrerPolicy;
   nsDocShellInfoLoadType           mLoadType;
   nsCOMPtr<nsISHEntry>             mSHEntry;
   nsString                         mTarget;
   nsCOMPtr<nsIInputStream>         mPostDataStream;
   nsCOMPtr<nsIInputStream>         mHeadersStream;
   bool                             mIsSrcdocLoad;
   nsString                         mSrcdocData;
   nsCOMPtr<nsIDocShell>            mSourceDocShell;
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -49,17 +49,17 @@ interface nsIWebBrowserPrint;
 interface nsIVariant;
 interface nsIPrivacyTransitionObserver;
 interface nsIReflowObserver;
 interface nsIScrollObserver;
 interface nsITabParent;
  
 typedef unsigned long nsLoadFlags;
 
-[scriptable, builtinclass, uuid(4e3de242-0b2a-4cf0-81b5-a5fe8628431c)]
+[scriptable, builtinclass, uuid(c2756385-bc54-417b-9ae4-c5a40053a2a3)]
 interface nsIDocShell : nsIDocShellTreeItem
 {
   /**
    * Loads a given URI.  This will give priority to loading the requested URI
    * in the object implementing	this interface.  If it can't be loaded here
    * however, the URL dispatcher will go through its normal process of content
    * loading.
    *
@@ -127,16 +127,17 @@ interface nsIDocShell : nsIDocShellTreeI
 
   /**
    * Loads the given URI.  This method is identical to loadURI(...) except
    * that its parameter list is broken out instead of being packaged inside
    * of an nsIDocShellLoadInfo object...
    *
    * @param aURI            - The URI to load.
    * @param aReferrer       - Referring URI
+   * @param aReferrerPolicy - Referrer policy
    * @param aOwner          - Owner (security principal) 
    * @param aInheritOwner   - Flag indicating whether the owner of the current
    *                          document should be inherited if aOwner is null.
    * @param aStopActiveDoc  - Flag indicating whether loading the current
    *                          document should be stopped.
    * @param aWindowTarget   - Window target for the load.
    * @param aTypeHint       - A hint as to the content-type of the resulting
    *                          data.  May be null or empty if no hint.
@@ -152,16 +153,17 @@ interface nsIDocShell : nsIDocShellTreeI
    *                          of aURI.
    * @param aSourceDocShell - The source browsing context for the navigation.
    * @param aBaseURI        - The base URI to be used for the load.  Set in
    *                          srcdoc loads as it cannot otherwise be inferred
    *                          in certain situations such as view-source.
    */
   [noscript]void internalLoad(in nsIURI aURI,
                               in nsIURI aReferrer,
+                              in unsigned long aReferrerPolicy,
                               in nsISupports aOwner,
                               in uint32_t aFlags,
                               in wstring aWindowTarget,
                               in string aTypeHint,
                               in AString aFileName,
                               in nsIInputStream aPostDataStream,
                               in nsIInputStream aHeadersStream,
                               in unsigned long aLoadFlags,
--- a/docshell/base/nsIDocShellLoadInfo.idl
+++ b/docshell/base/nsIDocShellLoadInfo.idl
@@ -12,18 +12,19 @@
  */
  
 interface nsIURI;
 interface nsIInputStream;
 interface nsISHEntry;
 interface nsIDocShell;
 
 typedef long nsDocShellInfoLoadType;
+typedef unsigned long nsDocShellInfoReferrerPolicy;
 
-[scriptable, uuid(c8d3b1e1-565a-427e-9d68-b109910ce9b7)]
+[scriptable, uuid(c63e9d64-490d-48bf-8013-b5d8ee4dbc25)]
 interface nsIDocShellLoadInfo : nsISupports
 {
     /** This is the referrer for the load. */
     attribute nsIURI referrer;
 
     /** The owner of the load, that is, the entity responsible for 
      *  causing the load to occur. This should be a nsIPrincipal typically.
      */
@@ -80,16 +81,21 @@ interface nsIDocShellLoadInfo : nsISuppo
     /** Additional headers */
     attribute nsIInputStream headersStream;
 
     /** True if the referrer should be sent, false if it shouldn't be
      *  sent, even if it's available. This attribute defaults to true.
      */
     attribute boolean sendReferrer;
 
+    /** Referrer policy for the load. This attribute holds one of
+     *  the values (REFERRER_POLICY_*) defined in nsIHttpChannel.
+     */
+    attribute nsDocShellInfoReferrerPolicy referrerPolicy;
+
     /** True if the docshell has been created to load an iframe where the
      * srcdoc attribute has been set.  Set when srcdocData is specified.
      */
     readonly attribute boolean isSrcdocLoad;
 
     /** When set, the load will be interpreted as a srcdoc load, where contents
      * of this string will be loaded instead of the URI.  Setting srcdocData
      * sets isSrcdocLoad to true
--- a/docshell/shistory/public/nsISHEntry.idl
+++ b/docshell/shistory/public/nsISHEntry.idl
@@ -25,17 +25,17 @@ interface nsIBFCacheEntry;
 struct nsIntRect;
 class nsDocShellEditorData;
 class nsSHEntryShared;
 %}
 [ref] native nsIntRect(nsIntRect);
 [ptr] native nsDocShellEditorDataPtr(nsDocShellEditorData);
 [ptr] native nsSHEntryShared(nsSHEntryShared);
 
-[scriptable, uuid(9eed7e92-1121-46f2-95e5-2f5c0dca46f0)]
+[scriptable, uuid(d5fbeb10-f373-4677-b69a-2694aa706cac)]
 interface nsISHEntry : nsISupports
 {
     /**
      * A readonly property that returns the URI
      * of the current entry. The object returned is
      * of type nsIURI
      */
     readonly attribute nsIURI URI;
@@ -59,16 +59,21 @@ interface nsISHEntry : nsISupports
     readonly attribute boolean isSubFrame;
 
     /** URI for the document */
     void setURI(in nsIURI aURI);
 
     /** Referrer URI */
     attribute nsIURI referrerURI;
 
+    /** Referrer policy, holding one of the values (REFERRER_POLICY_*)
+     *  defined in nsIHttpChannel.
+     */
+    attribute unsigned long referrerPolicy;
+
     /** Content viewer, for fast restoration of presentation */
     attribute nsIContentViewer contentViewer;
 
     /** Whether the content viewer is marked "sticky" */
     attribute boolean sticky;
 
     /** Saved state of the global window object */
     attribute nsISupports windowState;
--- a/docshell/shistory/src/nsSHEntry.cpp
+++ b/docshell/shistory/src/nsSHEntry.cpp
@@ -10,43 +10,46 @@
 #include "nsDocShellEditorData.h"
 #include "nsSHEntryShared.h"
 #include "nsILayoutHistoryState.h"
 #include "nsIContentViewer.h"
 #include "nsISupportsArray.h"
 #include "nsIStructuredCloneContainer.h"
 #include "nsIInputStream.h"
 #include "nsIURI.h"
+#include "mozilla/net/ReferrerPolicy.h"
 #include <algorithm>
 
 namespace dom = mozilla::dom;
 
 static uint32_t gEntryID = 0;
 
 //*****************************************************************************
 //***    nsSHEntry: Object Management
 //*****************************************************************************
 
 
 nsSHEntry::nsSHEntry()
-  : mLoadType(0)
+  : mReferrerPolicy(mozilla::net::RP_Default)
+  , mLoadType(0)
   , mID(gEntryID++)
   , mScrollPositionX(0)
   , mScrollPositionY(0)
   , mParent(nullptr)
   , mURIWasModified(false)
   , mIsSrcdocEntry(false)
 {
   mShared = new nsSHEntryShared();
 }
 
 nsSHEntry::nsSHEntry(const nsSHEntry &other)
   : mShared(other.mShared)
   , mURI(other.mURI)
   , mReferrerURI(other.mReferrerURI)
+  , mReferrerPolicy(other.mReferrerPolicy)
   , mTitle(other.mTitle)
   , mPostData(other.mPostData)
   , mLoadType(0)         // XXX why not copy?
   , mID(other.mID)
   , mScrollPositionX(0)  // XXX why not copy?
   , mScrollPositionY(0)  // XXX why not copy?
   , mParent(other.mParent)
   , mURIWasModified(other.mURIWasModified)
@@ -129,16 +132,28 @@ NS_IMETHODIMP nsSHEntry::GetReferrerURI(
 }
 
 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;
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 nsSHEntry::SetContentViewer(nsIContentViewer *aViewer)
 {
   return mShared->SetContentViewer(aViewer);
 }
 
 NS_IMETHODIMP
 nsSHEntry::GetContentViewer(nsIContentViewer **aResult)
--- a/docshell/shistory/src/nsSHEntry.h
+++ b/docshell/shistory/src/nsSHEntry.h
@@ -45,16 +45,17 @@ private:
 
   // We share the state in here with other SHEntries which correspond to the
   // same document.
   nsRefPtr<nsSHEntryShared> mShared;
 
   // See nsSHEntry.idl for comments on these members.
   nsCOMPtr<nsIURI>         mURI;
   nsCOMPtr<nsIURI>         mReferrerURI;
+  uint32_t                 mReferrerPolicy;
   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/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -1534,16 +1534,18 @@ struct nsIDocument::FrameRequest
 
 static already_AddRefed<mozilla::dom::NodeInfo> nullNodeInfo;
 
 // ==================================================================
 // =
 // ==================================================================
 nsIDocument::nsIDocument()
   : nsINode(nullNodeInfo),
+    mReferrerPolicySet(false),
+    mReferrerPolicy(mozilla::net::RP_Default),
     mCharacterSet(NS_LITERAL_CSTRING("ISO-8859-1")),
     mNodeInfoManager(nullptr),
     mCompatMode(eCompatibility_FullStandards),
     mVisibilityState(dom::VisibilityState::Hidden),
     mIsInitialDocumentInWindow(false),
     mMayStartLayout(true),
     mVisible(true),
     mRemovedFromDocShell(false),
@@ -3696,16 +3698,30 @@ nsDocument::SetHeaderData(nsIAtom* aHead
       aHeaderField == nsGkAtoms::viewport_minimum_scale ||
       aHeaderField == nsGkAtoms::viewport_maximum_scale ||
       aHeaderField == nsGkAtoms::viewport_initial_scale ||
       aHeaderField == nsGkAtoms::viewport_height ||
       aHeaderField == nsGkAtoms::viewport_width ||
       aHeaderField ==  nsGkAtoms::viewport_user_scalable) {
     mViewportType = Unknown;
   }
+
+  // Referrer policy spec says to ignore any empty referrer policies.
+  if (aHeaderField == nsGkAtoms::referrer && !aData.IsEmpty()) {
+    ReferrerPolicy policy = mozilla::net::ReferrerPolicyFromString(aData);
+
+    // Referrer policy spec (section 6.1) says that once the referrer policy
+    // is set, any future attempts to change it result in No-Referrer.
+    if (!mReferrerPolicySet) {
+      mReferrerPolicy = policy;
+      mReferrerPolicySet = true;
+    } else if (mReferrerPolicy != policy) {
+      mReferrerPolicy = mozilla::net::RP_No_Referrer;
+    }
+  }
 }
 
 void
 nsDocument::TryChannelCharset(nsIChannel *aChannel,
                               int32_t& aCharsetSource,
                               nsACString& aCharset,
                               nsHtml5TreeOpExecutor* aExecutor)
 {
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -115,16 +115,17 @@ class CallbackFunction;
  * the ID's nsIdentifierMapEntry, the document.all result is released too.
  * Perhaps the document.all results should have their own hashtable
  * in nsHTMLDocument.
  */
 class nsIdentifierMapEntry : public nsStringHashKey
 {
 public:
   typedef mozilla::dom::Element Element;
+  typedef mozilla::net::ReferrerPolicy ReferrerPolicy;
 
   explicit nsIdentifierMapEntry(const nsAString& aKey) :
     nsStringHashKey(&aKey), mNameContentList(nullptr)
   {
   }
   explicit nsIdentifierMapEntry(const nsAString* aKey) :
     nsStringHashKey(aKey), mNameContentList(nullptr)
   {
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -427,16 +427,18 @@ nsFrameLoader::ReallyStartLoadingInterna
   if (referrer) {
     bool isNullPrincipalScheme;
     rv = referrer->SchemeIs(NS_NULLPRINCIPAL_SCHEME, &isNullPrincipalScheme);
     if (NS_SUCCEEDED(rv) && !isNullPrincipalScheme) {
       loadInfo->SetReferrer(referrer);
     }
   }
 
+  loadInfo->SetReferrerPolicy(mOwnerContent->OwnerDoc()->GetReferrerPolicy());
+
   // Default flags:
   int32_t flags = nsIWebNavigation::LOAD_FLAGS_NONE;
 
   // Flags for browser frame:
   if (OwnerIsBrowserFrame()) {
     flags = nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP |
             nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_OWNER;
   }
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -577,16 +577,17 @@ GK_ATOM(menubutton, "menubutton")
 GK_ATOM(menuButton, "menu-button")
 GK_ATOM(menugroup, "menugroup")
 GK_ATOM(menuitem, "menuitem")
 GK_ATOM(menulist, "menulist")
 GK_ATOM(menupopup, "menupopup")
 GK_ATOM(menuseparator, "menuseparator")
 GK_ATOM(message, "message")
 GK_ATOM(meta, "meta")
+GK_ATOM(referrer, "referrer")
 GK_ATOM(meter, "meter")
 GK_ATOM(method, "method")
 GK_ATOM(microdataProperties, "microdataProperties")
 GK_ATOM(middle, "middle")
 GK_ATOM(min, "min")
 GK_ATOM(minheight, "minheight")
 GK_ATOM(minimum_scale, "minimum-scale")
 GK_ATOM(minpos, "minpos")
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -14,16 +14,17 @@
 #include "nsGkAtoms.h"                   // for static class members
 #include "nsIDocumentObserver.h"         // for typedef (nsUpdateType)
 #include "nsILoadGroup.h"                // for member (in nsCOMPtr)
 #include "nsINode.h"                     // for base class
 #include "nsIScriptGlobalObject.h"       // for member (in nsCOMPtr)
 #include "nsPIDOMWindow.h"               // for use in inline functions
 #include "nsPropertyTable.h"             // for member
 #include "nsTHashtable.h"                // for member
+#include "mozilla/net/ReferrerPolicy.h"  // for member
 #include "nsWeakReference.h"
 #include "mozilla/dom/DocumentBinding.h"
 #include "mozilla/WeakPtr.h"
 #include "Units.h"
 #include "nsExpirationTracker.h"
 #include "nsClassHashtable.h"
 
 class imgIRequest;
@@ -164,16 +165,17 @@ NS_GetContentList(nsINode* aRootNode,
 //----------------------------------------------------------------------
 
 // Document interface.  This is implemented by all document objects in
 // Gecko.
 class nsIDocument : public nsINode
 {
   typedef mozilla::dom::GlobalObject GlobalObject;
 public:
+  typedef mozilla::net::ReferrerPolicy ReferrerPolicy;
   typedef mozilla::dom::Element Element;
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IDOCUMENT_IID)
   NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
 
 #ifdef MOZILLA_INTERNAL_API
   nsIDocument();
 #endif
@@ -267,16 +269,25 @@ public:
 
   /**
    * Set the base URI for the document loaded via XHR, when accessed from
    * chrome privileged script.
    */
   virtual void SetChromeXHRDocBaseURI(nsIURI* aURI) = 0;
 
   /**
+   * Return the referrer policy of the document. Return "default" if there's no
+   * valid meta referrer tag found in the document.
+   */
+  ReferrerPolicy GetReferrerPolicy() const
+  {
+    return mReferrerPolicy;
+  }
+
+  /**
    * Set the principal responsible for this document.
    */
   virtual void SetPrincipal(nsIPrincipal *aPrincipal) = 0;
 
   /**
    * Return the LoadGroup for the document. May return null.
    */
   already_AddRefed<nsILoadGroup> GetDocumentLoadGroup() const
@@ -2463,16 +2474,19 @@ protected:
   nsCOMPtr<nsIURI> mDocumentURI;
   nsCOMPtr<nsIURI> mOriginalURI;
   nsCOMPtr<nsIURI> mChromeXHRDocURI;
   nsCOMPtr<nsIURI> mDocumentBaseURI;
   nsCOMPtr<nsIURI> mChromeXHRDocBaseURI;
 
   nsWeakPtr mDocumentLoadGroup;
 
+  bool mReferrerPolicySet;
+  ReferrerPolicy mReferrerPolicy;
+
   mozilla::WeakPtr<nsDocShell> mDocumentContainer;
 
   nsCString mCharacterSet;
   int32_t mCharacterSetSource;
 
   // This is just a weak pointer; the parent document owns its children.
   nsIDocument* mParentDocument;
 
--- a/dom/html/HTMLMetaElement.cpp
+++ b/dom/html/HTMLMetaElement.cpp
@@ -72,17 +72,31 @@ HTMLMetaElement::BindToTree(nsIDocument*
                                                  aBindingParent,
                                                  aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
   if (aDocument &&
       AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, nsGkAtoms::viewport, eIgnoreCase)) {
     nsAutoString content;
     rv = GetContent(content);
     NS_ENSURE_SUCCESS(rv, rv);
-    nsContentUtils::ProcessViewportInfo(aDocument, content);  
+    nsContentUtils::ProcessViewportInfo(aDocument, content);
+  }
+  if (aDocument &&
+      AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, nsGkAtoms::referrer, eIgnoreCase)) {
+    nsAutoString content;
+    rv = GetContent(content);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // Referrer Policy spec requires a <meta name="referrer" tag to be in the
+    // <head> element.
+    Element* headElt = aDocument->GetHeadElement();
+    if (headElt && nsContentUtils::ContentIsDescendantOf(this, headElt)) {
+      content = nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>(content);
+      aDocument->SetHeaderData(nsGkAtoms::referrer, content);
+    }
   }
   CreateAndDispatchEvent(aDocument, NS_LITERAL_STRING("DOMMetaAdded"));
   return rv;
 }
 
 void
 HTMLMetaElement::UnbindFromTree(bool aDeep, bool aNullParent)
 {
new file mode 100644
--- /dev/null
+++ b/netwerk/base/public/ReferrerPolicy.h
@@ -0,0 +1,61 @@
+/* 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 ReferrerPolicy_h__
+#define ReferrerPolicy_h__
+
+#include "nsStringGlue.h"
+#include "nsIHttpChannel.h"
+
+namespace mozilla { namespace net {
+
+enum ReferrerPolicy {
+  /* spec tokens: never no-referrer */
+  RP_No_Referrer                 = nsIHttpChannel::REFERRER_POLICY_NO_REFERRER,
+
+  /* spec tokens: origin */
+  RP_Origin                      = nsIHttpChannel::REFERRER_POLICY_ORIGIN,
+
+  /* spec tokens: default no-referrer-when-downgrade */
+  RP_No_Referrer_When_Downgrade  = nsIHttpChannel::REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE,
+  RP_Default                     = nsIHttpChannel::REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE,
+
+  /* spec tokens: origin-when-crossorigin */
+  RP_Origin_When_Crossorigin     = nsIHttpChannel::REFERRER_POLICY_ORIGIN_WHEN_XORIGIN,
+
+  /* spec tokens: always unsafe-url */
+  RP_Unsafe_URL                  = nsIHttpChannel::REFERRER_POLICY_UNSAFE_URL
+};
+
+inline ReferrerPolicy
+ReferrerPolicyFromString(const nsAString& content)
+{
+  // This is implemented step by step as described in the Referrer Policy
+  // specification, section 6.4 "Determine token's Policy".
+  if (content.LowerCaseEqualsLiteral("never") ||
+      content.LowerCaseEqualsLiteral("no-referrer")) {
+    return RP_No_Referrer;
+  }
+  if (content.LowerCaseEqualsLiteral("origin")) {
+    return RP_Origin;
+  }
+  if (content.LowerCaseEqualsLiteral("default") ||
+      content.LowerCaseEqualsLiteral("no-referrer-when-downgrade")) {
+    return RP_No_Referrer_When_Downgrade;
+  }
+  if (content.LowerCaseEqualsLiteral("origin-when-crossorigin")) {
+    return RP_Origin_When_Crossorigin;
+  }
+  if (content.LowerCaseEqualsLiteral("always") ||
+      content.LowerCaseEqualsLiteral("unsafe-url")) {
+    return RP_Unsafe_URL;
+  }
+  // Spec says if none of the previous match, use No_Referrer.
+  return RP_No_Referrer;
+
+}
+
+} } //namespace mozilla::net
+
+#endif
--- a/netwerk/base/public/moz.build
+++ b/netwerk/base/public/moz.build
@@ -139,15 +139,19 @@ EXPORTS += [
     'nsASocketHandler.h',
     'nsAsyncRedirectVerifyHelper.h',
     'nsNetUtil.h',
     'nsReadLine.h',
     'nsStreamListenerWrapper.h',
     'nsURIHashKey.h',
 ]
 
+EXPORTS.mozilla.net += [
+    'ReferrerPolicy.h',
+]
+
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
     EXPORTS += [
         'NetStatistics.h',
     ]
 
 FAIL_ON_WARNINGS = True
 
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -71,16 +71,17 @@ HttpBaseChannel::HttpBaseChannel()
   , mResponseTimeoutEnabled(true)
   , mAllRedirectsSameOrigin(true)
   , mAllRedirectsPassTimingAllowCheck(true)
   , mForceNoIntercept(false)
   , mSuspendCount(0)
   , mProxyResolveFlags(0)
   , mContentDispositionHint(UINT32_MAX)
   , mHttpHandler(gHttpHandler)
+  , mReferrerPolicy(REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE)
   , mRedirectCount(0)
   , mForcePending(false)
 {
   LOG(("Creating HttpBaseChannel @%x\n", this));
 
   // Subfields of unions cannot be targeted in an initializer list
   mSelfAddr.raw.family = PR_AF_UNSPEC;
   mPeerAddr.raw.family = PR_AF_UNSPEC;
@@ -894,24 +895,47 @@ HttpBaseChannel::GetReferrer(nsIURI **re
   *referrer = mReferrer;
   NS_IF_ADDREF(*referrer);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpBaseChannel::SetReferrer(nsIURI *referrer)
 {
+  return SetReferrerWithPolicy(referrer, REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE);
+}
+
+NS_IMETHODIMP
+HttpBaseChannel::GetReferrerPolicy(uint32_t *referrerPolicy)
+{
+  NS_ENSURE_ARG_POINTER(referrerPolicy);
+  *referrerPolicy = mReferrerPolicy;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+HttpBaseChannel::SetReferrerWithPolicy(nsIURI *referrer,
+                                       uint32_t referrerPolicy)
+{
   ENSURE_CALLED_BEFORE_CONNECT();
 
   // clear existing referrer, if any
   mReferrer = nullptr;
   mRequestHead.ClearHeader(nsHttp::Referer);
-
-  if (!referrer)
-      return NS_OK;
+  mReferrerPolicy = REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE;
+
+  if (!referrer) {
+    return NS_OK;
+  }
+
+  // Don't send referrer at all when the meta referrer setting is "no-referrer"
+  if (referrerPolicy == REFERRER_POLICY_NO_REFERRER) {
+    mReferrerPolicy = REFERRER_POLICY_NO_REFERRER;
+    return NS_OK;
+  }
 
   // 0: never send referer
   // 1: send referer for direct user action
   // 2: always send referer
   uint32_t userReferrerLevel = gHttpHandler->ReferrerLevel();
 
   // false: use real referrer
   // true: spoof with URI of the current request
@@ -924,22 +948,24 @@ HttpBaseChannel::SetReferrer(nsIURI *ref
 
   // 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)
+  if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) {
     referrerLevel = 1; // user action
-  else
+  } else {
     referrerLevel = 2; // inline content
-  if (userReferrerLevel < referrerLevel)
+  }
+  if (userReferrerLevel < referrerLevel) {
     return NS_OK;
+  }
 
   nsCOMPtr<nsIURI> referrerGrip;
   nsresult rv;
   bool match;
 
   //
   // Strip off "wyciwyg://123/" from wyciwyg referrers.
   //
@@ -987,56 +1013,80 @@ HttpBaseChannel::SetReferrer(nsIURI *ref
     nullptr
   };
   match = false;
   const char *const *scheme = referrerWhiteList;
   for (; *scheme && !match; ++scheme) {
     rv = referrer->SchemeIs(*scheme, &match);
     if (NS_FAILED(rv)) return rv;
   }
-  if (!match)
-    return NS_OK; // kick out....
+  if (!match) 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;
-    if (!match)
-      return NS_OK;
-
-    if (!gHttpHandler->SendSecureXSiteReferrer()) {
-      nsAutoCString referrerHost;
-      nsAutoCString host;
-
-      rv = referrer->GetAsciiHost(referrerHost);
-      if (NS_FAILED(rv)) return rv;
-
-      rv = mURI->GetAsciiHost(host);
-      if (NS_FAILED(rv)) return rv;
-
-      // GetAsciiHost returns lowercase hostname.
-      if (!referrerHost.Equals(host))
-        return NS_OK;
+
+    // It's ok to send referrer for https-to-http scenarios if the referrer
+    // policy is "unsafe-url" or "origin".
+    if (referrerPolicy != REFERRER_POLICY_UNSAFE_URL &&
+        referrerPolicy != REFERRER_POLICY_ORIGIN) {
+
+      // in other referrer policies, https->http is not allowed...
+      if (!match) return NS_OK;
+
+      // ...and https->https is possibly only allowed if the hosts match.
+      if (!gHttpHandler->SendSecureXSiteReferrer()) {
+        nsAutoCString referrerHost;
+        nsAutoCString host;
+
+        rv = referrer->GetAsciiHost(referrerHost);
+        if (NS_FAILED(rv)) return rv;
+
+        rv = mURI->GetAsciiHost(host);
+        if (NS_FAILED(rv)) return rv;
+
+        // GetAsciiHost returns lowercase hostname.
+        if (!referrerHost.Equals(host))
+          return NS_OK;
+      }
     }
   }
 
+  // for cross-origin-based referrer changes (not just host-based), figure out
+  // if the referrer is being sent cross-origin.
+  nsCOMPtr<nsIURI> loadingURI;
+  bool isCrossOrigin = true;
+  if (mLoadInfo) {
+    mLoadInfo->LoadingPrincipal()->GetURI(getter_AddRefs(loadingURI));
+  }
+  if (loadingURI) {
+    nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+    rv = ssm->CheckSameOriginURI(loadingURI, mURI, false);
+    isCrossOrigin = NS_FAILED(rv);
+  } else {
+    NS_WARNING("no loading principal available via loadInfo, assumming load is cross-origin");
+  }
+
   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
   //
   // Use CloneIgnoringRef to strip away any fragment per RFC 2616 section 14.36
+  // and Referrer Policy section 6.3.5.
   rv = referrer->CloneIgnoringRef(getter_AddRefs(clone));
   if (NS_FAILED(rv)) return rv;
 
   nsAutoCString currentHost;
   nsAutoCString referrerHost;
 
   rv = mURI->GetAsciiHost(currentHost);
   if (NS_FAILED(rv)) return rv;
@@ -1072,21 +1122,32 @@ HttpBaseChannel::SetReferrer(nsIURI *ref
     nsCOMPtr<nsIURI> mURIclone;
     rv = mURI->CloneIgnoringRef(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.
   rv = clone->SetUserPass(EmptyCString());
   if (NS_FAILED(rv)) return rv;
 
   nsAutoCString spec;
 
+  // site-specified referrer trimming may affect the trim level
+  // "unsafe-url" behaves like "origin" (send referrer in the same situations) but
+  // "unsafe-url" sends the whole referrer and origin removes the path.
+  // "origin-when-cross-origin" trims the referrer only when the request is
+  // cross-origin.
+  if (referrerPolicy == REFERRER_POLICY_ORIGIN ||
+      (isCrossOrigin && referrerPolicy == REFERRER_POLICY_ORIGIN_WHEN_XORIGIN)) {
+    userReferrerTrimmingPolicy = 2;
+  }
+
   // check how much referer to send
   switch (userReferrerTrimmingPolicy) {
 
   case 1: {
     // scheme+host+port+path
     nsAutoCString prepath, path;
     rv = clone->GetPrePath(prepath);
     if (NS_FAILED(rv)) return rv;
@@ -1112,18 +1173,21 @@ HttpBaseChannel::SetReferrer(nsIURI *ref
   default:
     // full URI
     rv = clone->GetAsciiSpec(spec);
     if (NS_FAILED(rv)) return rv;
     break;
   }
 
   // finally, remember the referrer URI and set the Referer header.
+  rv = SetRequestHeader(NS_LITERAL_CSTRING("Referer"), spec, false);
+  if (NS_FAILED(rv)) return rv;
+
   mReferrer = clone;
-  mRequestHead.SetHeader(nsHttp::Referer, spec);
+  mReferrerPolicy = referrerPolicy;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpBaseChannel::GetRequestHeader(const nsACString& aHeader,
                                   nsACString& aValue)
 {
   // XXX might be better to search the header list directly instead of
@@ -2066,17 +2130,17 @@ HttpBaseChannel::SetupReplacementChannel
     // request method gets set on the channel, regardless of whether or not
     // we set the upload stream above. This means SetRequestMethod() will
     // be called twice if ExplicitSetUploadStream() gets called above.
 
     httpChannel->SetRequestMethod(mRequestHead.Method());
   }
   // convey the referrer if one was used for this channel to the next one
   if (mReferrer)
-    httpChannel->SetReferrer(mReferrer);
+    httpChannel->SetReferrerWithPolicy(mReferrer, mReferrerPolicy);
   // convey the mAllowPipelining and mAllowSTS flags
   httpChannel->SetAllowPipelining(mAllowPipelining);
   httpChannel->SetAllowSTS(mAllowSTS);
   // convey the new redirection limit
   httpChannel->SetRedirectionLimit(mRedirectionLimit - 1);
 
   // convey the Accept header value
   {
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -128,16 +128,18 @@ public:
                                        nsIStreamListener **aNewNextListener,
                                        nsISupports *aCtxt);
 
   // HttpBaseChannel::nsIHttpChannel
   NS_IMETHOD GetRequestMethod(nsACString& aMethod);
   NS_IMETHOD SetRequestMethod(const nsACString& aMethod);
   NS_IMETHOD GetReferrer(nsIURI **referrer);
   NS_IMETHOD SetReferrer(nsIURI *referrer);
+  NS_IMETHOD GetReferrerPolicy(uint32_t *referrerPolicy);
+  NS_IMETHOD SetReferrerWithPolicy(nsIURI *referrer, uint32_t referrerPolicy);
   NS_IMETHOD GetRequestHeader(const nsACString& aHeader, nsACString& aValue);
   NS_IMETHOD SetRequestHeader(const nsACString& aHeader,
                               const nsACString& aValue, bool aMerge);
   NS_IMETHOD VisitRequestHeaders(nsIHttpHeaderVisitor *visitor);
   NS_IMETHOD GetResponseHeader(const nsACString &header, nsACString &value);
   NS_IMETHOD SetResponseHeader(const nsACString& header,
                                const nsACString& value, bool merge);
   NS_IMETHOD VisitResponseHeaders(nsIHttpHeaderVisitor *visitor);
@@ -378,16 +380,18 @@ protected:
   uint32_t                          mProxyResolveFlags;
   nsCOMPtr<nsIURI>                  mProxyURI;
 
   uint32_t                          mContentDispositionHint;
   nsAutoPtr<nsString>               mContentDispositionFilename;
 
   nsRefPtr<nsHttpHandler>           mHttpHandler;  // keep gHttpHandler alive
 
+  uint32_t                          mReferrerPolicy;
+
   // Performance tracking
   // The initiator type (for this resource) - how was the resource referenced in
   // the HTML file.
   nsString                          mInitiatorType;
   // Number of redirects that has occurred.
   int16_t                           mRedirectCount;
   // A time value equal to the starting time of the fetch that initiates the
   // redirect.
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -269,16 +269,17 @@ HttpChannelParent::DoAsyncOpen(  const U
     mChannel->ResumeAt(startPos, entityID);
 
   if (originalUri)
     mChannel->SetOriginalURI(originalUri);
   if (docUri)
     mChannel->SetDocumentURI(docUri);
   if (referrerUri)
     mChannel->SetReferrerInternal(referrerUri);
+    //TODO set referrer policy too (in patch 5)
   if (apiRedirectToUri)
     mChannel->RedirectTo(apiRedirectToUri);
   if (topWindowUri)
     mChannel->SetTopWindowURI(topWindowUri);
   if (loadFlags != nsIRequest::LOAD_NORMAL)
     mChannel->SetLoadFlags(loadFlags);
 
   for (uint32_t i = 0; i < requestHeaders.Length(); i++) {
--- a/netwerk/protocol/http/nsIHttpChannel.idl
+++ b/netwerk/protocol/http/nsIHttpChannel.idl
@@ -9,17 +9,17 @@ interface nsIHttpHeaderVisitor;
 
 /**
  * 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.
  */
-[scriptable, uuid(1bc753ad-5b88-454d-b4c0-4fd34cce6d96)]
+[scriptable, uuid(82083578-fb78-4f9a-953c-cecbae500697)]
 interface nsIHttpChannel : nsIChannel
 {
     /**************************************************************************
      * REQUEST CONFIGURATION
      *
      * Modifying request parameters after asyncOpen has been called is an error.
      */
 
@@ -51,16 +51,43 @@ interface nsIHttpChannel : nsIChannel
      * 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.
      */
     attribute nsIURI referrer;
 
     /**
+     * Referrer policies. See ReferrerPolicy.h for more details.
+     */
+
+    /*   default state, doesn't send referrer from https->http          */
+    const unsigned long REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE = 0;
+    /*   sends no referrer                                              */
+    const unsigned long REFERRER_POLICY_NO_REFERRER                = 1;
+    /*   only sends the origin of the referring URL                     */
+    const unsigned long REFERRER_POLICY_ORIGIN                     = 2;
+    /*   same as default, but reduced to ORIGIN when cross-origin.      */
+    const unsigned long REFERRER_POLICY_ORIGIN_WHEN_XORIGIN        = 3;
+    /*   always sends the referrer, even on downgrade.                  */
+    const unsigned long REFERRER_POLICY_UNSAFE_URL                 = 4;
+
+    /**
+     * 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).
+     */
+    readonly attribute unsigned long referrerPolicy;
+
+    /**
+     * Set the HTTP referrer URI with a referrer policy.
+     */
+    void setReferrerWithPolicy(in nsIURI referrer, in unsigned long referrerPolicy);
+
+    /**
      * Get the value of a particular request header.
      *
      * @param aHeader
      *        The case-insensitive name of the request header to query (e.g.,
      *        "Cache-Control").
      *
      * @return the value of the request header.
      * @throws NS_ERROR_NOT_AVAILABLE if the header is not set.
--- a/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
+++ b/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
@@ -603,16 +603,31 @@ nsViewSourceChannel::GetReferrer(nsIURI 
 NS_IMETHODIMP
 nsViewSourceChannel::SetReferrer(nsIURI * aReferrer)
 {
     return !mHttpChannel ? NS_ERROR_NULL_POINTER :
         mHttpChannel->SetReferrer(aReferrer);
 }
 
 NS_IMETHODIMP
+nsViewSourceChannel::GetReferrerPolicy(uint32_t *aReferrerPolicy)
+{
+    return !mHttpChannel ? NS_ERROR_NULL_POINTER :
+        mHttpChannel->GetReferrerPolicy(aReferrerPolicy);
+}
+
+NS_IMETHODIMP
+nsViewSourceChannel::SetReferrerWithPolicy(nsIURI * aReferrer,
+                                           uint32_t aReferrerPolicy)
+{
+    return !mHttpChannel ? NS_ERROR_NULL_POINTER :
+        mHttpChannel->SetReferrerWithPolicy(aReferrer, aReferrerPolicy);
+}
+
+NS_IMETHODIMP
 nsViewSourceChannel::GetRequestHeader(const nsACString & aHeader,
                                       nsACString & aValue)
 {
     return !mHttpChannel ? NS_ERROR_NULL_POINTER :
         mHttpChannel->GetRequestHeader(aHeader, aValue);
 }
 
 NS_IMETHODIMP
--- a/parser/html/nsHtml5SpeculativeLoad.cpp
+++ b/parser/html/nsHtml5SpeculativeLoad.cpp
@@ -22,16 +22,19 @@ nsHtml5SpeculativeLoad::~nsHtml5Speculat
 
 void
 nsHtml5SpeculativeLoad::Perform(nsHtml5TreeOpExecutor* aExecutor)
 {
   switch (mOpCode) {
     case eSpeculativeLoadBase:
       aExecutor->SetSpeculationBase(mUrl);
       break;
+    case eSpeculativeLoadMetaReferrer:
+      aExecutor->SetSpeculationReferrerPolicy(mMetaReferrerPolicy);
+      break;
     case eSpeculativeLoadImage:
       aExecutor->PreloadImage(mUrl, mCrossOrigin);
       break;
     case eSpeculativeLoadScript:
       aExecutor->PreloadScript(mUrl, mCharset, mTypeOrCharsetSource,
                                mCrossOrigin, false);
       break;
     case eSpeculativeLoadScriptFromHead:
--- a/parser/html/nsHtml5SpeculativeLoad.h
+++ b/parser/html/nsHtml5SpeculativeLoad.h
@@ -1,24 +1,26 @@
 /* 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 nsHtml5SpeculativeLoad_h
 #define nsHtml5SpeculativeLoad_h
 
 #include "nsString.h"
+#include "nsContentUtils.h"
 
 class nsHtml5TreeOpExecutor;
 
 enum eHtml5SpeculativeLoad {
 #ifdef DEBUG
   eSpeculativeLoadUninitialized,
 #endif
   eSpeculativeLoadBase,
+  eSpeculativeLoadMetaReferrer,
   eSpeculativeLoadImage,
   eSpeculativeLoadScript,
   eSpeculativeLoadScriptFromHead,
   eSpeculativeLoadStyle,
   eSpeculativeLoadManifest,
   eSpeculativeLoadSetDocumentCharset
 };
 
@@ -30,16 +32,24 @@ class nsHtml5SpeculativeLoad {
     inline void InitBase(const nsAString& aUrl)
     {
       NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized,
                       "Trying to reinitialize a speculative load!");
       mOpCode = eSpeculativeLoadBase;
       mUrl.Assign(aUrl);
     }
 
+    inline void InitMetaReferrerPolicy(const nsAString& aReferrerPolicy) {
+      NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized,
+                      "Trying to reinitialize a speculative load!");
+      mOpCode = eSpeculativeLoadMetaReferrer;
+      mMetaReferrerPolicy.Assign(
+        nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>(aReferrerPolicy));
+    }
+
     inline void InitImage(const nsAString& aUrl,
                           const nsAString& aCrossOrigin)
     {
       NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized,
                       "Trying to reinitialize a speculative load!");
       mOpCode = eSpeculativeLoadImage;
       mUrl.Assign(aUrl);
       mCrossOrigin.Assign(aCrossOrigin);
@@ -111,16 +121,17 @@ class nsHtml5SpeculativeLoad {
       mTypeOrCharsetSource.Assign((char16_t)aCharsetSource);
     }
 
     void Perform(nsHtml5TreeOpExecutor* aExecutor);
     
   private:
     eHtml5SpeculativeLoad mOpCode;
     nsString mUrl;
+    nsString mMetaReferrerPolicy;
     /**
      * If mOpCode is eSpeculativeLoadStyle or eSpeculativeLoadScript[FromHead]
      * then this is the value of the "charset" attribute. For
      * eSpeculativeLoadSetDocumentCharset it is the charset that the
      * document's charset is being set to. Otherwise it's empty.
      */
     nsString mCharset;
     /**
--- a/parser/html/nsHtml5TreeBuilderCppSupplement.h
+++ b/parser/html/nsHtml5TreeBuilderCppSupplement.h
@@ -184,16 +184,25 @@ nsHtml5TreeBuilder::createElement(int32_
             mSpeculativeLoadQueue.AppendElement()->InitManifest(EmptyString());
           }
         } else if (nsHtml5Atoms::base == aName) {
           nsString* url =
               aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF);
           if (url) {
             mSpeculativeLoadQueue.AppendElement()->InitBase(*url);
           }
+        } else if (nsHtml5Atoms::meta == aName) {
+          if (nsHtml5Portability::lowerCaseLiteralEqualsIgnoreAsciiCaseString(
+                      "referrer",
+                      aAttributes->getValue(nsHtml5AttributeName::ATTR_NAME))) {
+            nsString* referrerPolicy = aAttributes->getValue(nsHtml5AttributeName::ATTR_CONTENT);
+            if (referrerPolicy) {
+              mSpeculativeLoadQueue.AppendElement()->InitMetaReferrerPolicy(*referrerPolicy);
+            }
+          }
         }
         break;
       case kNameSpaceID_SVG:
         if (nsHtml5Atoms::image == aName) {
           nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF);
           if (url) {
             mSpeculativeLoadQueue.AppendElement()->InitImage(*url, NullString());
           }
--- a/parser/html/nsHtml5TreeOpExecutor.cpp
+++ b/parser/html/nsHtml5TreeOpExecutor.cpp
@@ -58,16 +58,18 @@ class nsHtml5ExecutorReflusher : public 
 };
 
 static mozilla::LinkedList<nsHtml5TreeOpExecutor>* gBackgroundFlushList = nullptr;
 static nsITimer* gFlushTimer = nullptr;
 
 nsHtml5TreeOpExecutor::nsHtml5TreeOpExecutor()
   : nsHtml5DocumentBuilder(false)
   , mPreloadedURLs(23)  // Mean # of preloadable resources per page on dmoz
+  , mSpeculationReferrerPolicyWasSet(false)
+  , mSpeculationReferrerPolicy(mozilla::net::RP_Default)
 {
   // zeroing operator new for everything else
 }
 
 nsHtml5TreeOpExecutor::~nsHtml5TreeOpExecutor()
 {
   if (gBackgroundFlushList && isInList()) {
     mOpQueue.Clear();
@@ -947,16 +949,37 @@ nsHtml5TreeOpExecutor::SetSpeculationBas
     return;
   }
   const nsCString& charset = mDocument->GetDocumentCharacterSet();
   DebugOnly<nsresult> rv = NS_NewURI(getter_AddRefs(mSpeculationBaseURI), aURL,
                                      charset.get(), mDocument->GetDocumentURI());
   NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to create a URI");
 }
 
+void
+nsHtml5TreeOpExecutor::SetSpeculationReferrerPolicy(const nsAString& aReferrerPolicy)
+{
+  ReferrerPolicy policy = mozilla::net::ReferrerPolicyFromString(aReferrerPolicy);
+
+  if (mSpeculationReferrerPolicyWasSet &&
+      policy != mSpeculationReferrerPolicy) {
+    // According to the Referrer Policy spec, if there's already been a policy
+    // set and another attempt is made to set a _different_ policy, the result
+    // is a "No Referrer" policy.
+    mSpeculationReferrerPolicy = mozilla::net::RP_No_Referrer;
+  }
+  else {
+    // Record "speculated" referrer policy locally and thread through the
+    // speculation phase.  The actual referrer policy will be set by
+    // HTMLMetaElement::BindToTree().
+    mSpeculationReferrerPolicyWasSet = true;
+    mSpeculationReferrerPolicy = policy;
+  }
+}
+
 #ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
 uint32_t nsHtml5TreeOpExecutor::sAppendBatchMaxSize = 0;
 uint32_t nsHtml5TreeOpExecutor::sAppendBatchSlotsExamined = 0;
 uint32_t nsHtml5TreeOpExecutor::sAppendBatchExaminations = 0;
 uint32_t nsHtml5TreeOpExecutor::sLongestTimeOffTheEventLoop = 0;
 uint32_t nsHtml5TreeOpExecutor::sTimesFlushLoopInterrupted = 0;
 #endif
 bool nsHtml5TreeOpExecutor::sExternalViewSource = false;
--- a/parser/html/nsHtml5TreeOpExecutor.h
+++ b/parser/html/nsHtml5TreeOpExecutor.h
@@ -17,30 +17,32 @@
 #include "nsIParser.h"
 #include "nsAHtml5TreeOpSink.h"
 #include "nsHtml5TreeOpStage.h"
 #include "nsIURI.h"
 #include "nsTHashtable.h"
 #include "nsHashKeys.h"
 #include "mozilla/LinkedList.h"
 #include "nsHtml5DocumentBuilder.h"
+#include "mozilla/net/ReferrerPolicy.h"
 
 class nsHtml5Parser;
 class nsHtml5TreeBuilder;
 class nsHtml5Tokenizer;
 class nsHtml5StreamParser;
 class nsIContent;
 class nsIDocument;
 
 class nsHtml5TreeOpExecutor MOZ_FINAL : public nsHtml5DocumentBuilder,
                                         public nsIContentSink,
                                         public nsAHtml5TreeOpSink,
                                         public mozilla::LinkedListElement<nsHtml5TreeOpExecutor>
 {
   friend class nsHtml5FlushLoopGuard;
+  typedef mozilla::net::ReferrerPolicy ReferrerPolicy;
 
   public:
     NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
     NS_DECL_ISUPPORTS_INHERITED
 
   private:
     static bool        sExternalViewSource;
 #ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
@@ -62,16 +64,22 @@ class nsHtml5TreeOpExecutor MOZ_FINAL : 
     
     /**
      * URLs already preloaded/preloading.
      */
     nsTHashtable<nsCStringHashKey> mPreloadedURLs;
 
     nsCOMPtr<nsIURI> mSpeculationBaseURI;
 
+    /**
+     * Need to keep track of whether the referrer policy was already set.
+     */
+    bool             mSpeculationReferrerPolicyWasSet;
+    ReferrerPolicy   mSpeculationReferrerPolicy;
+
     nsCOMPtr<nsIURI> mViewSourceBaseURI;
 
     /**
      * Whether the parser has started
      */
     bool                          mStarted;
 
     nsHtml5TreeOpStage            mStage;
@@ -252,16 +260,18 @@ class nsHtml5TreeOpExecutor MOZ_FINAL : 
                        bool aScriptFromHead);
 
     void PreloadStyle(const nsAString& aURL, const nsAString& aCharset,
 		      const nsAString& aCrossOrigin);
 
     void PreloadImage(const nsAString& aURL, const nsAString& aCrossOrigin);
 
     void SetSpeculationBase(const nsAString& aURL);
+
+    void SetSpeculationReferrerPolicy(const nsAString& aReferrerPolicy);
     
     void AddBase(const nsAString& aURL);
 
     static void InitializeStatics();
 
   private:
     nsHtml5Parser* GetParser();