Bug 1319111 - Expose 'result principal URI' on LoadInfo as a source for NS_GetFinalChannelURI (removes some use of LOAD_REPLACE flag). r=bz, r=mikedeboer
authorHonza Bambas <honzab.moz@firemni.cz>
Tue, 30 May 2017 18:07:59 +0200
changeset 364683 6d909485099a6c99427c1ea944f820e20af96ee5
parent 364682 3794aebfdef8582e9b2bb17d8e89df7e205250ca
child 364684 2d2ee0638f59a24c5e6ea43be55cc41776c9c51b
push id91605
push userryanvm@gmail.com
push dateMon, 19 Jun 2017 18:42:01 +0000
treeherdermozilla-inbound@6d909485099a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, mikedeboer
bugs1319111
milestone56.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 1319111 - Expose 'result principal URI' on LoadInfo as a source for NS_GetFinalChannelURI (removes some use of LOAD_REPLACE flag). r=bz, r=mikedeboer
browser/components/about/AboutRedirector.cpp
caps/tests/mochitest/file_bug1367586-followon.html
caps/tests/mochitest/file_bug1367586-redirect.sjs
caps/tests/mochitest/file_bug1367586-target.html
caps/tests/mochitest/mochitest.ini
caps/tests/mochitest/test_bug1367586.html
chrome/nsChromeProtocolHandler.cpp
devtools/client/framework/about-devtools-toolbox.js
docshell/base/nsAboutRedirector.cpp
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/nsISHEntry.idl
docshell/shistory/nsSHEntry.cpp
docshell/shistory/nsSHEntry.h
ipc/glue/BackgroundUtils.cpp
modules/libjar/nsJARChannel.cpp
netwerk/base/LoadInfo.cpp
netwerk/base/LoadInfo.h
netwerk/base/nsILoadInfo.idl
netwerk/base/nsNetUtil.cpp
netwerk/base/nsNetUtil.h
netwerk/ipc/NeckoChannelParams.ipdlh
netwerk/protocol/file/nsFileChannel.cpp
netwerk/protocol/file/nsFileChannel.h
netwerk/protocol/file/nsFileProtocolHandler.cpp
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/http/HttpBaseChannel.h
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/res/ExtensionProtocolHandler.h
netwerk/protocol/res/SubstitutingProtocolHandler.cpp
netwerk/streamconv/converters/nsMultiMixedConv.cpp
netwerk/test/unit/test_file_protocol.js
toolkit/components/mozprotocol/mozProtocolHandler.js
toolkit/modules/sessionstore/SessionHistory.jsm
--- a/browser/components/about/AboutRedirector.cpp
+++ b/browser/components/about/AboutRedirector.cpp
@@ -127,16 +127,18 @@ GetAboutModuleName(nsIURI *aURI)
 }
 
 NS_IMETHODIMP
 AboutRedirector::NewChannel(nsIURI* aURI,
                             nsILoadInfo* aLoadInfo,
                             nsIChannel** result)
 {
   NS_ENSURE_ARG_POINTER(aURI);
+  NS_ENSURE_ARG_POINTER(aLoadInfo);
+
   NS_ASSERTION(result, "must not be null");
 
   nsAutoCString path = GetAboutModuleName(aURI);
 
   nsresult rv;
   nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -167,36 +169,32 @@ AboutRedirector::NewChannel(nsIURI* aURI
       }
 
       nsCOMPtr<nsIChannel> tempChannel;
       nsCOMPtr<nsIURI> tempURI;
       rv = NS_NewURI(getter_AddRefs(tempURI), url);
       NS_ENSURE_SUCCESS(rv, rv);
 
       // If tempURI links to an external URI (i.e. something other than
-      // chrome:// or resource://) then set the LOAD_REPLACE flag on the
-      // channel which forces the channel owner to reflect the displayed
+      // chrome:// or resource://) then set the result principal URI on the
+      // load info which forces the channel prncipal to reflect the displayed
       // URL rather then being the systemPrincipal.
       bool isUIResource = false;
       rv = NS_URIChainHasFlags(tempURI, nsIProtocolHandler::URI_IS_UI_RESOURCE,
                                &isUIResource);
       NS_ENSURE_SUCCESS(rv, rv);
 
-      nsLoadFlags loadFlags = isUIResource
-                    ? static_cast<nsLoadFlags>(nsIChannel::LOAD_NORMAL)
-                    : static_cast<nsLoadFlags>(nsIChannel::LOAD_REPLACE);
-
       rv = NS_NewChannelInternal(getter_AddRefs(tempChannel),
                                  tempURI,
-                                 aLoadInfo,
-                                 nullptr, // aLoadGroup
-                                 nullptr, // aCallbacks
-                                 loadFlags);
+                                 aLoadInfo);
       NS_ENSURE_SUCCESS(rv, rv);
 
+      if (!isUIResource) {
+        aLoadInfo->SetResultPrincipalURI(tempURI);
+      }
       tempChannel->SetOriginalURI(aURI);
 
       NS_ADDREF(*result = tempChannel);
       return rv;
     }
   }
 
   return NS_ERROR_ILLEGAL_VALUE;
new file mode 100644
--- /dev/null
+++ b/caps/tests/mochitest/file_bug1367586-followon.html
@@ -0,0 +1,1 @@
+<body>Follow-on navigation content</body>
new file mode 100644
--- /dev/null
+++ b/caps/tests/mochitest/file_bug1367586-redirect.sjs
@@ -0,0 +1,5 @@
+function handleRequest(aRequest, aResponse) {
+ aResponse.setStatusLine(aRequest.httpVersion, 302, "Moved");
+ aResponse.setHeader("Location", "http://mochi.test:8888/tests/caps/tests/mochitest/file_bug1367586-target.html");
+ aResponse.write("To be redirected to target");
+}
new file mode 100644
--- /dev/null
+++ b/caps/tests/mochitest/file_bug1367586-target.html
@@ -0,0 +1,6 @@
+<head><script>
+window.addEventListener("pageshow", function(event) {
+    parent.ok(!event.persisted, "Should not load from bfcache");
+});
+</script></head>
+<body>Redirect target content</body>
--- a/caps/tests/mochitest/mochitest.ini
+++ b/caps/tests/mochitest/mochitest.ini
@@ -1,12 +1,16 @@
 [DEFAULT]
 support-files =
+  file_bug1367586-followon.html
+  file_bug1367586-redirect.sjs
+  file_bug1367586-target.html
   file_data.txt
   file_disableScript.html
   !/js/xpconnect/tests/mochitest/file_empty.html
 
 [test_bug246699.html]
 [test_bug292789.html]
 [test_bug423375.html]
 [test_bug470804.html]
+[test_bug1367586.html]
 [test_disallowInheritPrincipal.html]
 [test_extensionURL.html]
new file mode 100644
--- /dev/null
+++ b/caps/tests/mochitest/test_bug1367586.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1367586
+-->
+<head>
+  <title>Test for Bug 1367586</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe id="load-frame"></iframe>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+var frm = document.getElementById("load-frame");
+var step = 0;
+
+window.addEventListener("load", () => {
+  frm.contentWindow.location = "http://mochi.test:8888/tests/caps/tests/mochitest/file_bug1367586-redirect.sjs";
+  frm.addEventListener("load", function() {
+    ++step;
+    SimpleTest.executeSoon((function(_step, _frm) {
+      switch (_step) {
+      case 1:
+        is(_frm.contentWindow.location.href, "http://mochi.test:8888/tests/caps/tests/mochitest/file_bug1367586-target.html",
+           "Redirected to the expected target in step 1");
+        _frm.contentWindow.location = "http://mochi.test:8888/tests/caps/tests/mochitest/file_bug1367586-followon.html";
+        break;
+      case 2:
+        is(_frm.contentWindow.location.href, "http://mochi.test:8888/tests/caps/tests/mochitest/file_bug1367586-followon.html",
+           "Navigated to the expected URL in step 2");
+        _frm.contentWindow.history.back();
+        break;
+      case 3:
+        is(_frm.contentWindow.location.href, "http://mochi.test:8888/tests/caps/tests/mochitest/file_bug1367586-target.html",
+           "Seeing the correct URL when navigating back in step 3");
+        SimpleTest.finish();
+        break;
+      }
+    }).bind(window, step, frm));
+  });
+});
+
+</script>
+</pre>
+</body>
+</html>
--- a/chrome/nsChromeProtocolHandler.cpp
+++ b/chrome/nsChromeProtocolHandler.cpp
@@ -100,16 +100,18 @@ nsChromeProtocolHandler::NewURI(const ns
 NS_IMETHODIMP
 nsChromeProtocolHandler::NewChannel2(nsIURI* aURI,
                                      nsILoadInfo* aLoadInfo,
                                      nsIChannel** aResult)
 {
     nsresult rv;
 
     NS_ENSURE_ARG_POINTER(aURI);
+    NS_ENSURE_ARG_POINTER(aLoadInfo);
+
     NS_PRECONDITION(aResult, "Null out param");
 
 #ifdef DEBUG
     // Check that the uri we got is already canonified
     nsresult debug_rv;
     nsCOMPtr<nsIURI> debugClone;
     debug_rv = aURI->Clone(getter_AddRefs(debugClone));
     if (NS_SUCCEEDED(debug_rv)) {
@@ -140,16 +142,22 @@ nsChromeProtocolHandler::NewChannel2(nsI
     if (NS_FAILED(rv)) {
 #ifdef DEBUG
         printf("Couldn't convert chrome URL: %s\n",
                aURI->GetSpecOrDefault().get());
 #endif
         return rv;
     }
 
+    // We don't want to allow the inner protocol handler modify the result principal URI
+    // since we want either |aURI| or anything pre-set by upper layers to prevail.
+    nsCOMPtr<nsIURI> savedResultPrincipalURI;
+    rv = aLoadInfo->GetResultPrincipalURI(getter_AddRefs(savedResultPrincipalURI));
+    NS_ENSURE_SUCCESS(rv, rv);
+
     rv = NS_NewChannelInternal(getter_AddRefs(result),
                                resolvedURI,
                                aLoadInfo);
     NS_ENSURE_SUCCESS(rv, rv);
 
 #ifdef DEBUG
     nsCOMPtr<nsIFileChannel> fileChan(do_QueryInterface(result));
     if (fileChan) {
@@ -163,19 +171,18 @@ nsChromeProtocolHandler::NewChannel2(nsI
             file->GetNativePath(path);
             printf("Chrome file doesn't exist: %s\n", path.get());
         }
     }
 #endif
 
     // Make sure that the channel remembers where it was
     // originally loaded from.
-    nsLoadFlags loadFlags = 0;
-    result->GetLoadFlags(&loadFlags);
-    result->SetLoadFlags(loadFlags & ~nsIChannel::LOAD_REPLACE);
+    rv = aLoadInfo->SetResultPrincipalURI(savedResultPrincipalURI);
+    NS_ENSURE_SUCCESS(rv, rv);
     rv = result->SetOriginalURI(aURI);
     if (NS_FAILED(rv)) return rv;
 
     // Get a system principal for content files and set the owner
     // property of the result
     nsCOMPtr<nsIURL> url = do_QueryInterface(aURI);
     nsAutoCString path;
     rv = url->GetPath(path);
--- a/devtools/client/framework/about-devtools-toolbox.js
+++ b/devtools/client/framework/about-devtools-toolbox.js
@@ -21,16 +21,17 @@ AboutURL.prototype = {
   classID: components.ID("11342911-3135-45a8-8d71-737a2b0ad469"),
   contractID: "@mozilla.org/network/protocol/about;1?what=devtools-toolbox",
 
   QueryInterface: XPCOMUtils.generateQI([nsIAboutModule]),
 
   newChannel: function (aURI, aLoadInfo) {
     let chan = Services.io.newChannelFromURIWithLoadInfo(this.uri, aLoadInfo);
     chan.owner = Services.scriptSecurityManager.getSystemPrincipal();
+    chan.originalURI = aURI;
     return chan;
   },
 
   getURIFlags: function (aURI) {
     return nsIAboutModule.ALLOW_SCRIPT || nsIAboutModule.ENABLE_INDEXED_DB;
   }
 };
 
--- a/docshell/base/nsAboutRedirector.cpp
+++ b/docshell/base/nsAboutRedirector.cpp
@@ -147,54 +147,52 @@ static const RedirEntry kRedirMap[] = {
 static const int kRedirTotal = mozilla::ArrayLength(kRedirMap);
 
 NS_IMETHODIMP
 nsAboutRedirector::NewChannel(nsIURI* aURI,
                               nsILoadInfo* aLoadInfo,
                               nsIChannel** aResult)
 {
   NS_ENSURE_ARG_POINTER(aURI);
+  NS_ENSURE_ARG_POINTER(aLoadInfo);
   NS_ASSERTION(aResult, "must not be null");
 
   nsAutoCString path;
   nsresult rv = NS_GetAboutModuleName(aURI, path);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   for (int i = 0; i < kRedirTotal; i++) {
     if (!strcmp(path.get(), kRedirMap[i].id)) {
       nsCOMPtr<nsIChannel> tempChannel;
       nsCOMPtr<nsIURI> tempURI;
       rv = NS_NewURI(getter_AddRefs(tempURI), kRedirMap[i].url);
       NS_ENSURE_SUCCESS(rv, rv);
 
+      rv = NS_NewChannelInternal(getter_AddRefs(tempChannel),
+                                 tempURI,
+                                 aLoadInfo);
+      NS_ENSURE_SUCCESS(rv, rv);
+
       // If tempURI links to an external URI (i.e. something other than
-      // chrome:// or resource://) then set the LOAD_REPLACE flag on the
-      // channel which forces the channel owner to reflect the displayed
+      // chrome:// or resource://) then set result principal URI on the
+      // load info which forces the channel principal to reflect the displayed
       // URL rather then being the systemPrincipal.
       bool isUIResource = false;
       rv = NS_URIChainHasFlags(tempURI, nsIProtocolHandler::URI_IS_UI_RESOURCE,
                                &isUIResource);
       NS_ENSURE_SUCCESS(rv, rv);
 
       bool isAboutBlank = NS_IsAboutBlank(tempURI);
 
-      nsLoadFlags loadFlags = isUIResource || isAboutBlank
-                    ? static_cast<nsLoadFlags>(nsIChannel::LOAD_NORMAL)
-                    : static_cast<nsLoadFlags>(nsIChannel::LOAD_REPLACE);
-
-      rv = NS_NewChannelInternal(getter_AddRefs(tempChannel),
-                                 tempURI,
-                                 aLoadInfo,
-                                 nullptr, // aLoadGroup
-                                 nullptr, // aCallbacks
-                                 loadFlags);
-      NS_ENSURE_SUCCESS(rv, rv);
+      if (!isUIResource && !isAboutBlank) {
+        aLoadInfo->SetResultPrincipalURI(tempURI);
+      }
 
       tempChannel->SetOriginalURI(aURI);
 
       tempChannel.forget(aResult);
       return rv;
     }
   }
 
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -1267,16 +1267,17 @@ nsDocShell::LoadURI(nsIURI* aURI,
   // firing beforeunload, so we do need to check if *beforeunload* is currently
   // firing, so we call IsNavigationAllowed rather than just IsPrintingOrPP.
   if (!IsNavigationAllowed(true, false)) {
     return NS_OK; // JS may not handle returning of an error code
   }
 
   nsCOMPtr<nsIURI> referrer;
   nsCOMPtr<nsIURI> originalURI;
+  Maybe<nsCOMPtr<nsIURI>> resultPrincipalURI;
   bool loadReplace = false;
   nsCOMPtr<nsIInputStream> postStream;
   nsCOMPtr<nsIInputStream> headersStream;
   nsCOMPtr<nsIPrincipal> triggeringPrincipal;
   bool inheritPrincipal = false;
   bool principalIsExplicit = false;
   bool sendReferrer = true;
   uint32_t referrerPolicy = mozilla::net::RP_Unset;
@@ -1295,16 +1296,17 @@ nsDocShell::LoadURI(nsIURI* aURI,
       mItemType == typeContent && !NS_IsAboutBlank(aURI)) {
     StartupTimeline::RecordOnce(StartupTimeline::FIRST_LOAD_URI);
   }
 
   // Extract the info from the DocShellLoadInfo struct...
   if (aLoadInfo) {
     aLoadInfo->GetReferrer(getter_AddRefs(referrer));
     aLoadInfo->GetOriginalURI(getter_AddRefs(originalURI));
+    GetMaybeResultPrincipalURI(aLoadInfo, resultPrincipalURI);
     aLoadInfo->GetLoadReplace(&loadReplace);
     nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal;
     aLoadInfo->GetLoadType(&lt);
     // Get the appropriate loadType from nsIDocShellLoadInfo type
     loadType = ConvertDocShellLoadInfoToLoadType(lt);
 
     aLoadInfo->GetTriggeringPrincipal(getter_AddRefs(triggeringPrincipal));
     aLoadInfo->GetInheritPrincipal(&inheritPrincipal);
@@ -1565,16 +1567,17 @@ nsDocShell::LoadURI(nsIURI* aURI,
   }
 
   if (isSrcdoc) {
     flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
   }
 
   return InternalLoad(aURI,
                       originalURI,
+                      resultPrincipalURI,
                       loadReplace,
                       referrer,
                       referrerPolicy,
                       triggeringPrincipal,
                       principalToInherit,
                       flags,
                       target,
                       nullptr,      // No type hint
@@ -5413,17 +5416,17 @@ nsDocShell::LoadErrorPage(nsIURI* aURI, 
   // end of the URL, so append it last.
   errorPageUrl.AppendLiteral("&d=");
   errorPageUrl.AppendASCII(escapedDescription.get());
 
   nsCOMPtr<nsIURI> errorPageURI;
   nsresult rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  return InternalLoad(errorPageURI, nullptr, false, nullptr,
+  return InternalLoad(errorPageURI, nullptr, Nothing(), false, nullptr,
                       mozilla::net::RP_Unset,
                       nsContentUtils::GetSystemPrincipal(), nullptr,
                       INTERNAL_LOAD_FLAGS_NONE, EmptyString(),
                       nullptr, NullString(), nullptr, nullptr, LOAD_ERROR_PAGE,
                       nullptr, true, NullString(), this, nullptr, false,
                       nullptr, nullptr);
 }
 
@@ -5468,16 +5471,17 @@ nsDocShell::Reload(uint32_t aReloadFlags
       return NS_OK;
     }
 
     // Do not inherit owner from document
     uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
     nsAutoString srcdoc;
     nsCOMPtr<nsIURI> baseURI;
     nsCOMPtr<nsIURI> originalURI;
+    nsCOMPtr<nsIURI> resultPrincipalURI;
     bool loadReplace = false;
 
     nsIPrincipal* triggeringPrincipal = doc->NodePrincipal();
     nsAutoString contentTypeHint;
     doc->GetContentType(contentTypeHint);
 
     if (doc->IsSrcdocDocument()) {
       doc->GetSrcdocData(srcdoc);
@@ -5488,27 +5492,37 @@ nsDocShell::Reload(uint32_t aReloadFlags
     if (chan) {
       uint32_t loadFlags;
       chan->GetLoadFlags(&loadFlags);
       loadReplace = loadFlags & nsIChannel::LOAD_REPLACE;
       nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(chan));
       if (httpChan) {
         httpChan->GetOriginalURI(getter_AddRefs(originalURI));
       }
+
+      nsCOMPtr<nsILoadInfo> loadInfo = chan->GetLoadInfo();
+      if (loadInfo) {
+        loadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
+      }
     }
 
     MOZ_ASSERT(triggeringPrincipal, "Need a valid triggeringPrincipal");
 
     // 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(Move(resultPrincipalURI));
     rv = InternalLoad(currentURI,
                       originalURI,
+                      emplacedResultPrincipalURI,
                       loadReplace,
                       referrerURI,
                       referrerPolicy,
                       triggeringPrincipal,
                       triggeringPrincipal,
                       flags,
                       EmptyString(),   // No window target
                       NS_LossyConvertUTF16toASCII(contentTypeHint).get(),
@@ -9617,30 +9631,34 @@ nsDocShell::CopyFavicon(nsIURI* aOldURI,
                          : nsIFaviconService::FAVICON_LOAD_NON_PRIVATE, nullptr);
   }
 #endif
 }
 
 class InternalLoadEvent : public Runnable
 {
 public:
-  InternalLoadEvent(nsDocShell* aDocShell, nsIURI* aURI,
-                    nsIURI* aOriginalURI, bool aLoadReplace,
+  InternalLoadEvent(nsDocShell* aDocShell,
+                    nsIURI* aURI,
+                    nsIURI* aOriginalURI,
+                    Maybe<nsCOMPtr<nsIURI>> const& aResultPrincipalURI,
+                    bool aLoadReplace,
                     nsIURI* aReferrer, uint32_t aReferrerPolicy,
                     nsIPrincipal* aTriggeringPrincipal,
                     nsIPrincipal* aPrincipalToInherit, uint32_t aFlags,
                     const char* aTypeHint, nsIInputStream* aPostData,
                     nsIInputStream* aHeadersData, uint32_t aLoadType,
                     nsISHEntry* aSHEntry, bool aFirstParty,
                     const nsAString& aSrcdoc, nsIDocShell* aSourceDocShell,
                     nsIURI* aBaseURI, bool aCheckForPrerender)
     : mSrcdoc(aSrcdoc)
     , mDocShell(aDocShell)
     , mURI(aURI)
     , mOriginalURI(aOriginalURI)
+    , mResultPrincipalURI(aResultPrincipalURI)
     , mLoadReplace(aLoadReplace)
     , mReferrer(aReferrer)
     , mReferrerPolicy(aReferrerPolicy)
     , mTriggeringPrincipal(aTriggeringPrincipal)
     , mPrincipalToInherit(aPrincipalToInherit)
     , mPostData(aPostData)
     , mHeadersData(aHeadersData)
     , mSHEntry(aSHEntry)
@@ -9655,17 +9673,17 @@ public:
     if (aTypeHint) {
       mTypeHint = aTypeHint;
     }
   }
 
   NS_IMETHOD
   Run() override
   {
-    return mDocShell->InternalLoad(mURI, mOriginalURI,
+    return mDocShell->InternalLoad(mURI, mOriginalURI, mResultPrincipalURI,
                                    mLoadReplace,
                                    mReferrer,
                                    mReferrerPolicy,
                                    mTriggeringPrincipal, mPrincipalToInherit,
                                    mFlags, EmptyString(), mTypeHint.get(),
                                    NullString(), mPostData, mHeadersData,
                                    mLoadType, mSHEntry, mFirstParty,
                                    mSrcdoc, mSourceDocShell, mBaseURI,
@@ -9676,16 +9694,17 @@ private:
   // Use IDL strings so .get() returns null by default
   nsXPIDLString mWindowTarget;
   nsXPIDLCString mTypeHint;
   nsString mSrcdoc;
 
   RefPtr<nsDocShell> mDocShell;
   nsCOMPtr<nsIURI> mURI;
   nsCOMPtr<nsIURI> mOriginalURI;
+  Maybe<nsCOMPtr<nsIURI>> mResultPrincipalURI;
   bool mLoadReplace;
   nsCOMPtr<nsIURI> mReferrer;
   uint32_t mReferrerPolicy;
   nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
   nsCOMPtr<nsIPrincipal> mPrincipalToInherit;
   nsCOMPtr<nsIInputStream> mPostData;
   nsCOMPtr<nsIInputStream> mHeadersData;
   nsCOMPtr<nsISHEntry> mSHEntry;
@@ -9722,16 +9741,17 @@ nsDocShell::CreatePrincipalFromReferrer(
   prin.forget(aResult);
 
   return *aResult ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsDocShell::InternalLoad(nsIURI* aURI,
                          nsIURI* aOriginalURI,
+                         Maybe<nsCOMPtr<nsIURI>> const& aResultPrincipalURI,
                          bool aLoadReplace,
                          nsIURI* aReferrer,
                          uint32_t aReferrerPolicy,
                          nsIPrincipal* aTriggeringPrincipal,
                          nsIPrincipal* aPrincipalToInherit,
                          uint32_t aFlags,
                          const nsAString& aWindowTarget,
                          const char* aTypeHint,
@@ -10057,16 +10077,17 @@ nsDocShell::InternalLoad(nsIURI* aURI,
 
         // Set up our loadinfo so it will do the load as much like we would have
         // as possible.
         loadInfo->SetReferrer(aReferrer);
         loadInfo->SetReferrerPolicy(aReferrerPolicy);
         loadInfo->SetSendReferrer(!(aFlags &
                                     INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER));
         loadInfo->SetOriginalURI(aOriginalURI);
+        SetMaybeResultPrincipalURI(loadInfo, aResultPrincipalURI);
         loadInfo->SetLoadReplace(aLoadReplace);
         loadInfo->SetTriggeringPrincipal(aTriggeringPrincipal);
         loadInfo->SetInheritPrincipal(
           aFlags & INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL);
         // Explicit principal because we do not want any guesses as to what the
         // principal to inherit is: it should be aTriggeringPrincipal.
         loadInfo->SetPrincipalIsExplicit(true);
         loadInfo->SetLoadType(ConvertLoadTypeToDocShellLoadInfo(LOAD_LINK));
@@ -10104,16 +10125,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,
                                         aOriginalURI,
+                                        aResultPrincipalURI,
                                         aLoadReplace,
                                         aReferrer,
                                         aReferrerPolicy,
                                         aTriggeringPrincipal,
                                         principalToInherit,
                                         aFlags,
                                         EmptyString(),   // No window target
                                         aTypeHint,
@@ -10200,18 +10222,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, aOriginalURI, aLoadReplace,
-                              aReferrer, aReferrerPolicy,
+        new InternalLoadEvent(this, aURI, aOriginalURI, aResultPrincipalURI,
+                              aLoadReplace, aReferrer, aReferrerPolicy,
                               aTriggeringPrincipal, principalToInherit,
                               aFlags, aTypeHint, aPostData, aHeadersData,
                               aLoadType, aSHEntry, aFirstParty, aSrcdoc,
                               aSourceDocShell, aBaseURI, false);
       return DispatchToTabGroup("nsDocShell::InternalLoadEvent",
                                 TaskCategory::Other, ev.forget());
     }
 
@@ -10584,18 +10606,18 @@ nsDocShell::InternalLoad(nsIURI* aURI,
                                        aTriggeringPrincipal, &shouldLoad);
     if (NS_SUCCEEDED(rv) && !shouldLoad) {
       return NS_OK;
     }
   }
 
   if (browserChrome3 && aCheckForPrerender) {
     nsCOMPtr<nsIRunnable> ev =
-      new InternalLoadEvent(this, aURI, aOriginalURI, aLoadReplace,
-                            aReferrer, aReferrerPolicy,
+      new InternalLoadEvent(this, aURI, aOriginalURI, aResultPrincipalURI,
+                            aLoadReplace, aReferrer, aReferrerPolicy,
                             aTriggeringPrincipal, principalToInherit,
                             aFlags, aTypeHint, aPostData, aHeadersData,
                             aLoadType, aSHEntry, aFirstParty, aSrcdoc,
                             aSourceDocShell, aBaseURI, false);
     // We don't need any success handler since in that case
     // OnPartialSHistoryDeactive would be called, and it would ensure
     // docshell loads about:blank.
     bool shouldSwitch = false;
@@ -10734,17 +10756,17 @@ nsDocShell::InternalLoad(nsIURI* aURI,
   attrs.SetFirstPartyDomain(isTopLevelDoc, aURI);
 
   net::PredictorLearn(aURI, nullptr,
                       nsINetworkPredictor::LEARN_LOAD_TOPLEVEL, attrs);
   net::PredictorPredict(aURI, nullptr,
                         nsINetworkPredictor::PREDICT_LOAD, attrs, nullptr);
 
   nsCOMPtr<nsIRequest> req;
-  rv = DoURILoad(aURI, aOriginalURI, aLoadReplace, aReferrer,
+  rv = DoURILoad(aURI, aOriginalURI, aResultPrincipalURI, aLoadReplace, aReferrer,
                  !(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER),
                  aReferrerPolicy,
                  aTriggeringPrincipal, principalToInherit, 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,
@@ -10813,16 +10835,17 @@ nsDocShell::GetInheritedPrincipal(bool a
   }
 
   return nullptr;
 }
 
 nsresult
 nsDocShell::DoURILoad(nsIURI* aURI,
                       nsIURI* aOriginalURI,
+                      Maybe<nsCOMPtr<nsIURI>> const& aResultPrincipalURI,
                       bool aLoadReplace,
                       nsIURI* aReferrerURI,
                       bool aSendReferrer,
                       uint32_t aReferrerPolicy,
                       nsIPrincipal* aTriggeringPrincipal,
                       nsIPrincipal* aPrincipalToInherit,
                       const char* aTypeHint,
                       const nsAString& aFileName,
@@ -11128,26 +11151,38 @@ nsDocShell::DoURILoad(nsIURI* aURI,
   // Make sure to give the caller a channel if we managed to create one
   // This is important for correct error page/session history interaction
   if (aRequest) {
     NS_ADDREF(*aRequest = channel);
   }
 
   if (aOriginalURI) {
     channel->SetOriginalURI(aOriginalURI);
+    // The LOAD_REPLACE flag and its handling here will be removed as part
+    // of bug 1319110.  For now preserve its restoration here to not break
+    // any code expecting it being set specially on redirected channels.
+    // If the flag has originally been set to change result of
+    // NS_GetFinalChannelURI it won't have any effect and also won't cause
+    // any harm.
     if (aLoadReplace) {
       uint32_t loadFlags;
       channel->GetLoadFlags(&loadFlags);
       NS_ENSURE_SUCCESS(rv, rv);
       channel->SetLoadFlags(loadFlags | nsIChannel::LOAD_REPLACE);
     }
   } else {
     channel->SetOriginalURI(aURI);
   }
 
+  if (aResultPrincipalURI) {
+    // Unconditionally override, we want the replay to be equal to what has
+    // been captured.
+    loadInfo->SetResultPrincipalURI(aResultPrincipalURI.ref());
+  }
+
   if (aTypeHint && *aTypeHint) {
     channel->SetContentType(nsDependentCString(aTypeHint));
     mContentTypeHint = aTypeHint;
   } else {
     mContentTypeHint.Truncate();
   }
 
   if (!aFileName.IsVoid()) {
@@ -12358,16 +12393,17 @@ nsDocShell::AddToSessionHistory(nsIURI* 
     if (!entry) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
   }
 
   // Get the post data & referrer
   nsCOMPtr<nsIInputStream> inputStream;
   nsCOMPtr<nsIURI> originalURI;
+  nsCOMPtr<nsIURI> resultPrincipalURI;
   bool loadReplace = false;
   nsCOMPtr<nsIURI> referrerURI;
   uint32_t referrerPolicy = mozilla::net::RP_Unset;
   nsCOMPtr<nsISupports> cacheKey;
   nsCOMPtr<nsIPrincipal> triggeringPrincipal = aTriggeringPrincipal;
   nsCOMPtr<nsIPrincipal> principalToInherit = aPrincipalToInherit;
   bool expired = false;
   bool discardLayoutState = false;
@@ -12405,16 +12441,18 @@ nsDocShell::AddToSessionHistory(nsIURI* 
     }
 
     nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
     if (loadInfo) {
       if (!triggeringPrincipal) {
         triggeringPrincipal = loadInfo->TriggeringPrincipal();
       }
 
+      loadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
+
       // For now keep storing just the principal in the SHEntry.
       if (!principalToInherit) {
         if (loadInfo->GetLoadingSandboxed()) {
           if (loadInfo->LoadingPrincipal()) {
             principalToInherit = NullPrincipal::CreateWithInheritedAttributes(
             loadInfo->LoadingPrincipal());
           } else {
             // get the OriginAttributes
@@ -12437,16 +12475,17 @@ nsDocShell::AddToSessionHistory(nsIURI* 
                 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);
   nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(aChannel);
   if (inStrmChan) {
     bool isSrcdocChannel;
     inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
     if (isSrcdocChannel) {
@@ -12550,29 +12589,32 @@ nsresult
 nsDocShell::LoadHistoryEntry(nsISHEntry* aEntry, uint32_t aLoadType)
 {
   if (!IsNavigationAllowed()) {
     return NS_OK;
   }
 
   nsCOMPtr<nsIURI> uri;
   nsCOMPtr<nsIURI> originalURI;
+  nsCOMPtr<nsIURI> resultPrincipalURI;
   bool loadReplace = false;
   nsCOMPtr<nsIInputStream> postData;
   nsCOMPtr<nsIURI> referrerURI;
   uint32_t referrerPolicy;
   nsAutoCString contentType;
   nsCOMPtr<nsIPrincipal> triggeringPrincipal;
   nsCOMPtr<nsIPrincipal> principalToInherit;
 
   NS_ENSURE_TRUE(aEntry, NS_ERROR_FAILURE);
 
   NS_ENSURE_SUCCESS(aEntry->GetURI(getter_AddRefs(uri)), NS_ERROR_FAILURE);
   NS_ENSURE_SUCCESS(aEntry->GetOriginalURI(getter_AddRefs(originalURI)),
                     NS_ERROR_FAILURE);
+  NS_ENSURE_SUCCESS(aEntry->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI)),
+                    NS_ERROR_FAILURE);
   NS_ENSURE_SUCCESS(aEntry->GetLoadReplace(&loadReplace),
                     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);
@@ -12650,18 +12692,21 @@ nsDocShell::LoadHistoryEntry(nsISHEntry*
   if (!triggeringPrincipal) {
     triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
   }
 
   // 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(Move(resultPrincipalURI));
   rv = InternalLoad(uri,
                     originalURI,
+                    emplacedResultPrincipalURI,
                     loadReplace,
                     referrerURI,
                     referrerPolicy,
                     triggeringPrincipal,
                     principalToInherit,
                     flags,
                     EmptyString(),      // No window target
                     contentType.get(),  // Type hint
@@ -14204,16 +14249,17 @@ nsDocShell::OnLinkClickSync(nsIContent* 
   // if the triggeringPrincipal is not passed explicitly, then we
   // fall back to using doc->NodePrincipal() as the triggeringPrincipal.
   nsCOMPtr<nsIPrincipal> triggeringPrincipal =
     aTriggeringPrincipal ? aTriggeringPrincipal
                          : aContent->NodePrincipal();
 
   nsresult rv = InternalLoad(clonedURI,                 // New URI
                              nullptr,                   // Original URI
+                             Nothing(),                 // Let the protocol handler assign it
                              false,                     // LoadReplace
                              referer,                   // Referer URI
                              refererPolicy,             // Referer policy
                              triggeringPrincipal,
                              aContent->NodePrincipal(),
                              flags,
                              target,                    // Window target
                              NS_LossyConvertUTF16toASCII(typeHint).get(),
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -366,16 +366,17 @@ protected:
   // get a principal based on the URI being loaded.
   // 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.
   // aOriginalURI will be set as the originalURI on the channel that does the
   // load. If aOriginalURI is null, aURI will be set as the originalURI.
   // If aLoadReplace is true, LOAD_REPLACE flag will be set to the nsIChannel.
   nsresult DoURILoad(nsIURI* aURI,
                      nsIURI* aOriginalURI,
+                     mozilla::Maybe<nsCOMPtr<nsIURI>> const& aResultPrincipalURI,
                      bool aLoadReplace,
                      nsIURI* aReferrer,
                      bool aSendReferrer,
                      uint32_t aReferrerPolicy,
                      nsIPrincipal* aTriggeringPrincipal,
                      nsIPrincipal* aPrincipalToInherit,
                      const char* aTypeHint,
                      const nsAString& aFileName,
--- a/docshell/base/nsDocShellLoadInfo.cpp
+++ b/docshell/base/nsDocShellLoadInfo.cpp
@@ -5,19 +5,71 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsDocShellLoadInfo.h"
 #include "nsISHEntry.h"
 #include "nsIInputStream.h"
 #include "nsIURI.h"
 #include "nsIDocShell.h"
 #include "mozilla/net/ReferrerPolicy.h"
+#include "mozilla/Unused.h"
+
+namespace mozilla {
+
+void
+GetMaybeResultPrincipalURI(nsIDocShellLoadInfo* aLoadInfo, Maybe<nsCOMPtr<nsIURI>>& aRPURI)
+{
+  if (!aLoadInfo) {
+    return;
+  }
+
+  nsresult rv;
+
+  bool isSome;
+  rv = aLoadInfo->GetResultPrincipalURIIsSome(&isSome);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  aRPURI.reset();
+
+  if (!isSome) {
+    return;
+  }
+
+  nsCOMPtr<nsIURI> uri;
+  rv = aLoadInfo->GetResultPrincipalURI(getter_AddRefs(uri));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  aRPURI.emplace(Move(uri));
+}
+
+void
+SetMaybeResultPrincipalURI(nsIDocShellLoadInfo* aLoadInfo, Maybe<nsCOMPtr<nsIURI>> const& aRPURI)
+{
+  if (!aLoadInfo) {
+    return;
+  }
+
+  nsresult rv;
+
+  rv = aLoadInfo->SetResultPrincipalURI(aRPURI.refOr(nullptr));
+  Unused << NS_WARN_IF(NS_FAILED(rv));
+
+  rv = aLoadInfo->SetResultPrincipalURIIsSome(aRPURI.isSome());
+  Unused << NS_WARN_IF(NS_FAILED(rv));
+}
+
+} // mozilla
 
 nsDocShellLoadInfo::nsDocShellLoadInfo()
-  : mLoadReplace(false)
+  : mResultPrincipalURIIsSome(false)
+  , mLoadReplace(false)
   , mInheritPrincipal(false)
   , mPrincipalIsExplicit(false)
   , mSendReferrer(true)
   , mReferrerPolicy(mozilla::net::RP_Unset)
   , mLoadType(nsIDocShellLoadInfo::loadNormal)
   , mIsSrcdocLoad(false)
 {
 }
@@ -64,16 +116,47 @@ nsDocShellLoadInfo::GetOriginalURI(nsIUR
 NS_IMETHODIMP
 nsDocShellLoadInfo::SetOriginalURI(nsIURI* aOriginalURI)
 {
   mOriginalURI = aOriginalURI;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDocShellLoadInfo::GetResultPrincipalURI(nsIURI** aResultPrincipalURI)
+{
+  NS_ENSURE_ARG_POINTER(aResultPrincipalURI);
+
+  *aResultPrincipalURI = mResultPrincipalURI;
+  NS_IF_ADDREF(*aResultPrincipalURI);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::SetResultPrincipalURI(nsIURI* aResultPrincipalURI)
+{
+  mResultPrincipalURI = aResultPrincipalURI;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::GetResultPrincipalURIIsSome(bool* aIsSome)
+{
+  *aIsSome = mResultPrincipalURIIsSome;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::SetResultPrincipalURIIsSome(bool aIsSome)
+{
+  mResultPrincipalURIIsSome = aIsSome;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDocShellLoadInfo::GetLoadReplace(bool* aLoadReplace)
 {
   *aLoadReplace = mLoadReplace;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShellLoadInfo::SetLoadReplace(bool aLoadReplace)
--- a/docshell/base/nsDocShellLoadInfo.h
+++ b/docshell/base/nsDocShellLoadInfo.h
@@ -28,17 +28,19 @@ public:
   NS_DECL_NSIDOCSHELLLOADINFO
 
 protected:
   virtual ~nsDocShellLoadInfo();
 
 protected:
   nsCOMPtr<nsIURI> mReferrer;
   nsCOMPtr<nsIURI> mOriginalURI;
+  nsCOMPtr<nsIURI> mResultPrincipalURI;
   nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
+  bool mResultPrincipalURIIsSome;
   bool mLoadReplace;
   bool mInheritPrincipal;
   bool mPrincipalIsExplicit;
   bool mSendReferrer;
   nsDocShellInfoReferrerPolicy mReferrerPolicy;
   nsDocShellInfoLoadType mLoadType;
   nsCOMPtr<nsISHEntry> mSHEntry;
   nsString mTarget;
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -5,26 +5,28 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "domstubs.idl"
 #include "nsIDocShellTreeItem.idl"
 #include "nsIRequest.idl"
 
 %{ C++
 #include "js/TypeDecls.h"
+#include "mozilla/Maybe.h"
 class nsPresContext;
 class nsIPresShell;
 %}
 
 /**
  * The nsIDocShell interface.
  */
 
 [ptr] native nsPresContext(nsPresContext);
 [ptr] native nsIPresShell(nsIPresShell);
+[ref] native MaybeURI(mozilla::Maybe<nsCOMPtr<nsIURI>>);
 
 interface nsIURI;
 interface nsIChannel;
 interface nsIContentViewer;
 interface nsIDOMEventTarget;
 interface nsIDocShellLoadInfo;
 interface nsIEditor;
 interface nsIEditingSession;
@@ -122,16 +124,20 @@ 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 aOriginalURI         - The URI to set as the originalURI on the channel
    *                               that does the load. If null, aURI will be set as
    *                               the originalURI.
+   * @param aResultPrincipalURI  - The URI to be set to loadInfo.resultPrincipalURI
+   *                               When Nothing, there will be no change
+   *                               When Some, the principal URI will overwrite even
+   *                               with a null value.
    * @param aLoadReplace         - If set LOAD_REPLACE flag will be set on the
    *                               channel. aOriginalURI is null, this argument is
    *                               ignored.
    * @param aReferrer            - Referring URI
    * @param aReferrerPolicy      - Referrer policy
    * @param aTriggeringPrincipal - A non-null principal that initiated that load.
    *                               Please note that this is the principal that is
    *                               used for security checks. If the argument aURI
@@ -169,16 +175,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 aOriginalURI,
+                              [const] in MaybeURI aResultPrincipalURI,
                               in boolean aLoadReplace,
                               in nsIURI aReferrer,
                               in unsigned long aReferrerPolicy,
                               in nsIPrincipal aTriggeringPrincipal,
                               in nsIPrincipal aPrincipalToInherit,
                               in uint32_t aFlags,
                               in AString aWindowTarget,
                               in string aTypeHint,
--- a/docshell/base/nsIDocShellLoadInfo.idl
+++ b/docshell/base/nsIDocShellLoadInfo.idl
@@ -27,16 +27,26 @@ interface nsIDocShellLoadInfo : nsISuppo
     attribute nsIURI referrer;
 
     /**
      * The originalURI to be passed to nsIDocShell.internalLoad. May be null.
      */
     attribute nsIURI originalURI;
 
     /**
+     * Result principal URL from nsILoadInfo, may be null.  Valid only if
+     * the "IsSome" part is true (has the same meaning as isSome()
+     * on mozilla::Maybe.)
+     *
+     * In C++ please use Get/SetMaybeResultPrincipalURI helper functions.
+     */
+    attribute nsIURI resultPrincipalURI;
+    attribute boolean resultPrincipalURIIsSome;
+
+    /**
      * loadReplace flag to be passed to nsIDocShell.internalLoad.
      */
     attribute boolean loadReplace;
 
     /** The principal of the load, that is, the entity responsible for 
      *  causing the load to occur. In most cases the referrer and
      *  the triggeringPrincipal's URI will be identical.
      */
@@ -118,8 +128,27 @@ interface nsIDocShellLoadInfo : nsISuppo
     attribute nsIDocShell sourceDocShell;
 
     /**
      * Used for srcdoc loads to give view-source knowledge of the load's base
      * URI as this information isn't embedded in the load's URI.
      */
     attribute nsIURI baseURI;
 };
+
+%{C++
+
+#include "mozilla/Maybe.h"
+
+namespace mozilla {
+
+/**
+ * Helper function allowing convenient work with mozilla::Maybe in C++, hiding
+ * resultPrincipalURI and resultPrincipalURIIsSome attributes from the consumer.
+ */
+void
+GetMaybeResultPrincipalURI(nsIDocShellLoadInfo* aLoadInfo, Maybe<nsCOMPtr<nsIURI>>& aRPURI);
+void
+SetMaybeResultPrincipalURI(nsIDocShellLoadInfo* aLoadInfo, Maybe<nsCOMPtr<nsIURI>> const& aRPURI);
+
+} // mozilla
+
+%}
--- a/docshell/shistory/nsISHEntry.idl
+++ b/docshell/shistory/nsISHEntry.idl
@@ -45,16 +45,22 @@ interface nsISHEntry : nsISupports
     /**
      * A readonly property that returns the original URI of the current entry.
      * If an entry is the result of a redirect this attribute holds original
      * URI. The object returned is of type nsIURI
      */
     attribute nsIURI originalURI;
 
     /**
+     * URL as stored from nsILoadInfo.resultPrincipalURI.  See nsILoadInfo
+     * for more details.
+     */
+    attribute nsIURI resultPrincipalURI;
+
+    /**
      *  This flag remembers whether channel has LOAD_REPLACE set.
      */
     attribute boolean loadReplace;
 
     /**
      * A readonly property that returns the title
      * of the current entry.  The object returned
      * is a encoded string
--- a/docshell/shistory/nsSHEntry.cpp
+++ b/docshell/shistory/nsSHEntry.cpp
@@ -133,16 +133,31 @@ nsSHEntry::GetOriginalURI(nsIURI** aOrig
 NS_IMETHODIMP
 nsSHEntry::SetOriginalURI(nsIURI* aOriginalURI)
 {
   mOriginalURI = aOriginalURI;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsSHEntry::GetResultPrincipalURI(nsIURI** aResultPrincipalURI)
+{
+  *aResultPrincipalURI = mResultPrincipalURI;
+  NS_IF_ADDREF(*aResultPrincipalURI);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::SetResultPrincipalURI(nsIURI* aResultPrincipalURI)
+{
+  mResultPrincipalURI = aResultPrincipalURI;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsSHEntry::GetLoadReplace(bool* aLoadReplace)
 {
   *aLoadReplace = mLoadReplace;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSHEntry::SetLoadReplace(bool aLoadReplace)
--- a/docshell/shistory/nsSHEntry.h
+++ b/docshell/shistory/nsSHEntry.h
@@ -42,16 +42,17 @@ private:
 
   // 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;
   nsString mTitle;
   nsCOMPtr<nsIInputStream> mPostData;
   uint32_t mLoadType;
   uint32_t mID;
   int32_t mScrollPositionX;
   int32_t mScrollPositionY;
--- a/ipc/glue/BackgroundUtils.cpp
+++ b/ipc/glue/BackgroundUtils.cpp
@@ -18,16 +18,17 @@
 #include "nsNetUtil.h"
 #include "mozilla/LoadInfo.h"
 #include "ContentPrincipal.h"
 #include "NullPrincipal.h"
 #include "nsContentUtils.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "mozilla/nsRedirectHistoryEntry.h"
+#include "URIUtils.h"
 
 namespace mozilla {
 namespace net {
 class OptionalLoadInfoArgs;
 }
 
 using mozilla::BasePrincipal;
 using namespace mozilla::net;
@@ -341,16 +342,23 @@ LoadInfoToLoadInfoArgs(nsILoadInfo *aLoa
         getter_AddRefs(sandboxedLoadingPrincipal));
     NS_ENSURE_SUCCESS(rv, rv);
     rv = PrincipalToPrincipalInfo(sandboxedLoadingPrincipal,
                                   &sandboxedLoadingPrincipalInfoTemp);
     NS_ENSURE_SUCCESS(rv, rv);
     sandboxedLoadingPrincipalInfo = sandboxedLoadingPrincipalInfoTemp;
   }
 
+  OptionalURIParams optionalResultPrincipalURI = mozilla::void_t();
+  nsCOMPtr<nsIURI> resultPrincipalURI;
+  Unused << aLoadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
+  if (resultPrincipalURI) {
+    SerializeURI(resultPrincipalURI, optionalResultPrincipalURI);
+  }
+
   nsTArray<RedirectHistoryEntryInfo> redirectChainIncludingInternalRedirects;
   for (const nsCOMPtr<nsIRedirectHistoryEntry>& redirectEntry :
        aLoadInfo->RedirectChainIncludingInternalRedirects()) {
     RedirectHistoryEntryInfo* entry = redirectChainIncludingInternalRedirects.AppendElement();
     rv = RHEntryToRHEntryInfo(redirectEntry, entry);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
@@ -363,16 +371,17 @@ LoadInfoToLoadInfoArgs(nsILoadInfo *aLoa
   }
 
   *aOptionalLoadInfoArgs =
     LoadInfoArgs(
       loadingPrincipalInfo,
       triggeringPrincipalInfo,
       principalToInheritInfo,
       sandboxedLoadingPrincipalInfo,
+      optionalResultPrincipalURI,
       aLoadInfo->GetSecurityFlags(),
       aLoadInfo->InternalContentPolicyType(),
       static_cast<uint32_t>(aLoadInfo->GetTainting()),
       aLoadInfo->GetUpgradeInsecureRequests(),
       aLoadInfo->GetVerifySignedContent(),
       aLoadInfo->GetEnforceSRI(),
       aLoadInfo->GetForceInheritPrincipalDropped(),
       aLoadInfo->GetInnerWindowID(),
@@ -429,16 +438,22 @@ LoadInfoArgsToLoadInfo(const OptionalLoa
 
   nsCOMPtr<nsIPrincipal> sandboxedLoadingPrincipal;
   if (loadInfoArgs.sandboxedLoadingPrincipalInfo().type() != OptionalPrincipalInfo::Tvoid_t) {
     sandboxedLoadingPrincipal =
       PrincipalInfoToPrincipal(loadInfoArgs.sandboxedLoadingPrincipalInfo(), &rv);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
+  nsCOMPtr<nsIURI> resultPrincipalURI;
+  if (loadInfoArgs.resultPrincipalURI().type() != OptionalURIParams::Tvoid_t) {
+    resultPrincipalURI = DeserializeURI(loadInfoArgs.resultPrincipalURI());
+    NS_ENSURE_TRUE(resultPrincipalURI, NS_ERROR_UNEXPECTED);
+  }
+
   RedirectHistoryArray redirectChainIncludingInternalRedirects;
   for (const RedirectHistoryEntryInfo& entryInfo :
       loadInfoArgs.redirectChainIncludingInternalRedirects()) {
     nsCOMPtr<nsIRedirectHistoryEntry> redirectHistoryEntry =
       RHEntryInfoToRHEntry(entryInfo);
     NS_ENSURE_SUCCESS(rv, rv);
     redirectChainIncludingInternalRedirects.AppendElement(redirectHistoryEntry.forget());
   }
@@ -451,16 +466,17 @@ LoadInfoArgsToLoadInfo(const OptionalLoa
     redirectChain.AppendElement(redirectHistoryEntry.forget());
   }
 
   nsCOMPtr<nsILoadInfo> loadInfo =
     new mozilla::LoadInfo(loadingPrincipal,
                           triggeringPrincipal,
                           principalToInherit,
                           sandboxedLoadingPrincipal,
+                          resultPrincipalURI,
                           loadInfoArgs.securityFlags(),
                           loadInfoArgs.contentPolicyType(),
                           static_cast<LoadTainting>(loadInfoArgs.tainting()),
                           loadInfoArgs.upgradeInsecureRequests(),
                           loadInfoArgs.verifySignedContent(),
                           loadInfoArgs.enforceSRI(),
                           loadInfoArgs.forceInheritPrincipalDropped(),
                           loadInfoArgs.innerWindowID(),
--- a/modules/libjar/nsJARChannel.cpp
+++ b/modules/libjar/nsJARChannel.cpp
@@ -915,18 +915,18 @@ nsJARChannel::OnDownloadComplete(MemoryD
 {
     nsresult rv;
 
     nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
     if (channel) {
         uint32_t loadFlags;
         channel->GetLoadFlags(&loadFlags);
         if (loadFlags & LOAD_REPLACE) {
-            mLoadFlags |= LOAD_REPLACE;
-
+            // Update our URI to reflect any redirects that happen during
+            // the HTTP request.
             if (!mOriginalURI) {
                 SetOriginalURI(mJarURI);
             }
 
             nsCOMPtr<nsIURI> innerURI;
             rv = channel->GetURI(getter_AddRefs(innerURI));
             if (NS_SUCCEEDED(rv)) {
                 nsCOMPtr<nsIJARURI> newURI;
@@ -938,16 +938,19 @@ nsJARChannel::OnDownloadComplete(MemoryD
             }
             if (NS_SUCCEEDED(status)) {
                 status = rv;
             }
         }
     }
 
     if (NS_SUCCEEDED(status) && channel) {
+        // In case the load info object has changed during a redirect,
+        // grab it from the target channel.
+        channel->GetLoadInfo(getter_AddRefs(mLoadInfo));
         // Grab the security info from our base channel
         channel->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
 
         nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
         if (httpChannel) {
             // We only want to run scripts if the server really intended to
             // send us a JAR file.  Check the server-supplied content type for
             // a JAR type.
--- a/netwerk/base/LoadInfo.cpp
+++ b/netwerk/base/LoadInfo.cpp
@@ -269,16 +269,17 @@ LoadInfo::LoadInfo(nsPIDOMWindowOuter* a
 #endif
 }
 
 LoadInfo::LoadInfo(const LoadInfo& rhs)
   : mLoadingPrincipal(rhs.mLoadingPrincipal)
   , mTriggeringPrincipal(rhs.mTriggeringPrincipal)
   , mPrincipalToInherit(rhs.mPrincipalToInherit)
   , mSandboxedLoadingPrincipal(rhs.mSandboxedLoadingPrincipal)
+  , mResultPrincipalURI(rhs.mResultPrincipalURI)
   , mLoadingContext(rhs.mLoadingContext)
   , mSecurityFlags(rhs.mSecurityFlags)
   , mInternalContentPolicyType(rhs.mInternalContentPolicyType)
   , mTainting(rhs.mTainting)
   , mUpgradeInsecureRequests(rhs.mUpgradeInsecureRequests)
   , mVerifySignedContent(rhs.mVerifySignedContent)
   , mEnforceSRI(rhs.mEnforceSRI)
   , mForceInheritPrincipalDropped(rhs.mForceInheritPrincipalDropped)
@@ -302,16 +303,17 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
   , mIsHSTSPrimingUpgrade(rhs.mIsHSTSPrimingUpgrade)
 {
 }
 
 LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
                    nsIPrincipal* aTriggeringPrincipal,
                    nsIPrincipal* aPrincipalToInherit,
                    nsIPrincipal* aSandboxedLoadingPrincipal,
+                   nsIURI* aResultPrincipalURI,
                    nsSecurityFlags aSecurityFlags,
                    nsContentPolicyType aContentPolicyType,
                    LoadTainting aTainting,
                    bool aUpgradeInsecureRequests,
                    bool aVerifySignedContent,
                    bool aEnforceSRI,
                    bool aForceInheritPrincipalDropped,
                    uint64_t aInnerWindowID,
@@ -329,16 +331,17 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadin
                    bool aIsPreflight,
                    bool aForceHSTSPriming,
                    bool aMixedContentWouldBlock,
                    bool aIsHSTSPriming,
                    bool aIsHSTSPrimingUpgrade)
   : mLoadingPrincipal(aLoadingPrincipal)
   , mTriggeringPrincipal(aTriggeringPrincipal)
   , mPrincipalToInherit(aPrincipalToInherit)
+  , mResultPrincipalURI(aResultPrincipalURI)
   , mSecurityFlags(aSecurityFlags)
   , mInternalContentPolicyType(aContentPolicyType)
   , mTainting(aTainting)
   , mUpgradeInsecureRequests(aUpgradeInsecureRequests)
   , mVerifySignedContent(aVerifySignedContent)
   , mEnforceSRI(aEnforceSRI)
   , mForceInheritPrincipalDropped(aForceInheritPrincipalDropped)
   , mInnerWindowID(aInnerWindowID)
@@ -1004,10 +1007,24 @@ LoadInfo::SynthesizeServiceWorkerTaintin
 NS_IMETHODIMP
 LoadInfo::GetIsTopLevelLoad(bool *aResult)
 {
   *aResult = mFrameOuterWindowID ? mFrameOuterWindowID == mOuterWindowID
                                  : mParentOuterWindowID == mOuterWindowID;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+LoadInfo::GetResultPrincipalURI(nsIURI **aURI)
+{
+  NS_IF_ADDREF(*aURI = mResultPrincipalURI);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LoadInfo::SetResultPrincipalURI(nsIURI *aURI)
+{
+  mResultPrincipalURI = aURI;
+  return NS_OK;
+}
+
 } // namespace net
 } // namespace mozilla
--- a/netwerk/base/LoadInfo.h
+++ b/netwerk/base/LoadInfo.h
@@ -89,16 +89,17 @@ private:
   // private constructor that is only allowed to be called from within
   // HttpChannelParent and FTPChannelParent declared as friends undeneath.
   // In e10s we can not serialize nsINode, hence we store the innerWindowID.
   // Please note that aRedirectChain uses swapElements.
   LoadInfo(nsIPrincipal* aLoadingPrincipal,
            nsIPrincipal* aTriggeringPrincipal,
            nsIPrincipal* aPrincipalToInherit,
            nsIPrincipal* aSandboxedLoadingPrincipal,
+           nsIURI* aResultPrincipalURI,
            nsSecurityFlags aSecurityFlags,
            nsContentPolicyType aContentPolicyType,
            LoadTainting aTainting,
            bool aUpgradeInsecureRequests,
            bool aVerifySignedContent,
            bool aEnforceSRI,
            bool aForceInheritPrincipalDropped,
            uint64_t aInnerWindowID,
@@ -138,16 +139,17 @@ private:
   void SetIncludeCookiesSecFlag();
   friend class mozilla::dom::XMLHttpRequestMainThread;
 
   // if you add a member, please also update the copy constructor
   nsCOMPtr<nsIPrincipal>           mLoadingPrincipal;
   nsCOMPtr<nsIPrincipal>           mTriggeringPrincipal;
   nsCOMPtr<nsIPrincipal>           mPrincipalToInherit;
   nsCOMPtr<nsIPrincipal>           mSandboxedLoadingPrincipal;
+  nsCOMPtr<nsIURI>                 mResultPrincipalURI;
   nsWeakPtr                        mLoadingContext;
   nsSecurityFlags                  mSecurityFlags;
   nsContentPolicyType              mInternalContentPolicyType;
   LoadTainting                     mTainting;
   bool                             mUpgradeInsecureRequests;
   bool                             mVerifySignedContent;
   bool                             mEnforceSRI;
   bool                             mForceInheritPrincipalDropped;
--- a/netwerk/base/nsILoadInfo.idl
+++ b/netwerk/base/nsILoadInfo.idl
@@ -6,16 +6,17 @@
 
 #include "nsISupports.idl"
 #include "nsIContentPolicy.idl"
 
 interface nsIDOMDocument;
 interface nsINode;
 interface nsIPrincipal;
 interface nsIRedirectHistoryEntry;
+interface nsIURI;
 %{C++
 #include "nsTArray.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/LoadTainting.h"
 
 class nsCString;
 %}
 
@@ -748,15 +749,23 @@ interface nsILoadInfo : nsISupports
 
   /**
    * Returns true if this load is for top level document.
    * Note that the load for a sub-frame's document will return false here.
    */
   [infallible] readonly attribute boolean isTopLevelLoad;
 
   /**
+   * If this is non-null, this property represents two things: (1) the
+   * URI to be used for the principal if the channel with this loadinfo
+   * gets a principal based on URI and (2) the URI to use for a document
+   * created from the channel with this loadinfo.
+   */
+  attribute nsIURI resultPrincipalURI;
+
+  /**
    * Returns the null principal of the resulting resource if the SEC_SANDBOXED
    * flag is set.  Otherwise returns null.  This is used by
    * GetChannelResultPrincipal() to ensure that the same null principal object
    * is returned every time.
    */
   [noscript] readonly attribute nsIPrincipal sandboxedLoadingPrincipal;
 };
--- a/netwerk/base/nsNetUtil.cpp
+++ b/netwerk/base/nsNetUtil.cpp
@@ -179,21 +179,27 @@ NS_NewChannelInternal(nsIChannel        
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (aCallbacks) {
     rv = channel->SetNotificationCallbacks(aCallbacks);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
+#ifdef DEBUG
+  nsLoadFlags channelLoadFlags = 0;
+  channel->GetLoadFlags(&channelLoadFlags);
+  // Will be removed when we remove LOAD_REPLACE altogether
+  // This check is trying to catch protocol handlers that still
+  // try to set the LOAD_REPLACE flag.
+  MOZ_DIAGNOSTIC_ASSERT(!(channelLoadFlags & nsIChannel::LOAD_REPLACE));
+#endif
+
   if (aLoadFlags != nsIRequest::LOAD_NORMAL) {
-    // Retain the LOAD_REPLACE load flag if set.
-    nsLoadFlags normalLoadFlags = 0;
-    channel->GetLoadFlags(&normalLoadFlags);
-    rv = channel->SetLoadFlags(aLoadFlags | (normalLoadFlags & nsIChannel::LOAD_REPLACE));
+    rv = channel->SetLoadFlags(aLoadFlags);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   channel.forget(outChannel);
   return NS_OK;
 }
 
 nsresult
@@ -258,21 +264,27 @@ NS_NewChannelInternal(nsIChannel        
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (aCallbacks) {
     rv = channel->SetNotificationCallbacks(aCallbacks);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
+#ifdef DEBUG
+  nsLoadFlags channelLoadFlags = 0;
+  channel->GetLoadFlags(&channelLoadFlags);
+  // Will be removed when we remove LOAD_REPLACE altogether
+  // This check is trying to catch protocol handlers that still
+  // try to set the LOAD_REPLACE flag.
+  MOZ_DIAGNOSTIC_ASSERT(!(channelLoadFlags & nsIChannel::LOAD_REPLACE));
+#endif
+
   if (aLoadFlags != nsIRequest::LOAD_NORMAL) {
-    // Retain the LOAD_REPLACE load flag if set.
-    nsLoadFlags normalLoadFlags = 0;
-    channel->GetLoadFlags(&normalLoadFlags);
-    rv = channel->SetLoadFlags(aLoadFlags | (normalLoadFlags & nsIChannel::LOAD_REPLACE));
+    rv = channel->SetLoadFlags(aLoadFlags);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   channel.forget(outChannel);
   return NS_OK;
 }
 
 nsresult /*NS_NewChannelWithNodeAndTriggeringPrincipal */
@@ -1881,22 +1893,25 @@ NS_GetInnermostURI(nsIURI *aURI)
 
     return uri.forget();
 }
 
 nsresult
 NS_GetFinalChannelURI(nsIChannel *channel, nsIURI **uri)
 {
     *uri = nullptr;
-    nsLoadFlags loadFlags = 0;
-    nsresult rv = channel->GetLoadFlags(&loadFlags);
-    NS_ENSURE_SUCCESS(rv, rv);
 
-    if (loadFlags & nsIChannel::LOAD_REPLACE) {
-        return channel->GetURI(uri);
+    nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
+    if (loadInfo) {
+        nsCOMPtr<nsIURI> resultPrincipalURI;
+        loadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
+        if (resultPrincipalURI) {
+            resultPrincipalURI.forget(uri);
+            return NS_OK;
+        }
     }
 
     return channel->GetOriginalURI(uri);
 }
 
 nsresult
 NS_URIChainHasFlags(nsIURI   *uri,
                     uint32_t  flags,
--- a/netwerk/base/nsNetUtil.h
+++ b/netwerk/base/nsNetUtil.h
@@ -776,21 +776,20 @@ nsresult NS_URIChainHasFlags(nsIURI   *u
 
 /**
  * Helper function for getting the innermost URI for a given URI.  The return
  * value could be just the object passed in if it's not a nested URI.
  */
 already_AddRefed<nsIURI> NS_GetInnermostURI(nsIURI *aURI);
 
 /**
- * Get the "final" URI for a channel.  This is either the same as GetURI or
- * GetOriginalURI, depending on whether this channel has
- * nsIChanel::LOAD_REPLACE set.  For channels without that flag set, the final
- * URI is the original URI, while for ones with the flag the final URI is the
- * channel URI.
+ * Get the "final" URI for a channel.  This is either channel's load info
+ * resultPrincipalURI, if set, or GetOriginalURI.  In most cases (but not all) load
+ * info resultPrincipalURI, if set, corresponds to URI of the channel if it's required
+ * to represent the actual principal for the channel.
  */
 nsresult NS_GetFinalChannelURI(nsIChannel *channel, nsIURI **uri);
 
 // NS_SecurityHashURI must return the same hash value for any two URIs that
 // compare equal according to NS_SecurityCompareURIs.  Unfortunately, in the
 // case of files, it's not clear we can do anything better than returning
 // the schemeHash, so hashing files degenerates to storing them in a list.
 uint32_t NS_SecurityHashURI(nsIURI *aURI);
--- a/netwerk/ipc/NeckoChannelParams.ipdlh
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -36,16 +36,17 @@ struct RedirectHistoryEntryInfo
 };
 
 struct LoadInfoArgs
 {
   OptionalPrincipalInfo       requestingPrincipalInfo;
   PrincipalInfo               triggeringPrincipalInfo;
   OptionalPrincipalInfo       principalToInheritInfo;
   OptionalPrincipalInfo       sandboxedLoadingPrincipalInfo;
+  OptionalURIParams           resultPrincipalURI;
   uint32_t                    securityFlags;
   uint32_t                    contentPolicyType;
   uint32_t                    tainting;
   bool                        upgradeInsecureRequests;
   bool                        verifySignedContent;
   bool                        enforceSRI;
   bool                        forceInheritPrincipalDropped;
   uint64_t                    innerWindowID;
--- a/netwerk/protocol/file/nsFileChannel.cpp
+++ b/netwerk/protocol/file/nsFileChannel.cpp
@@ -246,61 +246,74 @@ nsFileUploadContentStream::OnCopyComplet
   nsresult status = mCopyEvent->Status();
 
   CloseWithStatus(NS_FAILED(status) ? status : NS_BASE_STREAM_CLOSED);
 }
 
 //-----------------------------------------------------------------------------
 
 nsFileChannel::nsFileChannel(nsIURI *uri) 
+  : mFileURI(uri)
 {
+}
+
+nsresult
+nsFileChannel::Init()
+{
+  NS_ENSURE_STATE(mLoadInfo);
+
+  nsresult rv;
+
+  rv = nsBaseChannel::Init();
+  NS_ENSURE_SUCCESS(rv, rv);
+
   // If we have a link file, we should resolve its target right away.
   // This is to protect against a same origin attack where the same link file
   // can point to different resources right after the first resource is loaded.
   nsCOMPtr<nsIFile> file;
   nsCOMPtr <nsIURI> targetURI;
 #ifdef XP_WIN
   nsAutoString fileTarget;
 #else
   nsAutoCString fileTarget;
 #endif
   nsCOMPtr<nsIFile> resolvedFile;
   bool symLink;
-  nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(uri);
-  if (fileURL && 
+  nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mFileURI);
+  if (fileURL &&
       NS_SUCCEEDED(fileURL->GetFile(getter_AddRefs(file))) &&
-      NS_SUCCEEDED(file->IsSymlink(&symLink)) && 
+      NS_SUCCEEDED(file->IsSymlink(&symLink)) &&
       symLink &&
 #ifdef XP_WIN
       NS_SUCCEEDED(file->GetTarget(fileTarget)) &&
-      NS_SUCCEEDED(NS_NewLocalFile(fileTarget, PR_TRUE, 
+      NS_SUCCEEDED(NS_NewLocalFile(fileTarget, true,
                                    getter_AddRefs(resolvedFile))) &&
 #else
       NS_SUCCEEDED(file->GetNativeTarget(fileTarget)) &&
-      NS_SUCCEEDED(NS_NewNativeLocalFile(fileTarget, PR_TRUE, 
+      NS_SUCCEEDED(NS_NewNativeLocalFile(fileTarget, true,
                                          getter_AddRefs(resolvedFile))) &&
 #endif
-      NS_SUCCEEDED(NS_NewFileURI(getter_AddRefs(targetURI), 
-                   resolvedFile, nullptr))) {
+      NS_SUCCEEDED(NS_NewFileURI(getter_AddRefs(targetURI),
+                                 resolvedFile, nullptr))) {
     // Make an effort to match up the query strings.
-    nsCOMPtr<nsIURL> origURL = do_QueryInterface(uri);
+    nsCOMPtr<nsIURL> origURL = do_QueryInterface(mFileURI);
     nsCOMPtr<nsIURL> targetURL = do_QueryInterface(targetURI);
     nsAutoCString queryString;
     if (origURL && targetURL && NS_SUCCEEDED(origURL->GetQuery(queryString))) {
       targetURL->SetQuery(queryString);
     }
 
     SetURI(targetURI);
-    SetOriginalURI(uri);
-    nsLoadFlags loadFlags = 0;
-    GetLoadFlags(&loadFlags);
-    SetLoadFlags(loadFlags | nsIChannel::LOAD_REPLACE);
+    SetOriginalURI(mFileURI);
+    mLoadInfo->SetResultPrincipalURI(targetURI);
   } else {
-    SetURI(uri);
+    SetURI(mFileURI);
   }
+
+  return NS_OK;
 }
 
 nsFileChannel::~nsFileChannel()
 {
 }
 
 nsresult
 nsFileChannel::MakeFileInputStream(nsIFile *file,
--- a/netwerk/protocol/file/nsFileChannel.h
+++ b/netwerk/protocol/file/nsFileChannel.h
@@ -17,16 +17,18 @@ class nsFileChannel : public nsBaseChann
 {
 public: 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIFILECHANNEL
   NS_DECL_NSIUPLOADCHANNEL
 
   explicit nsFileChannel(nsIURI *uri);
 
+  nsresult Init();
+
 protected:
   ~nsFileChannel();
 
   // Called to construct a blocking file input stream for the given file.  This
   // method also returns a best guess at the content-type for the data stream.
   // NOTE: If the channel has a type hint set, contentType will be left
   // untouched. The caller should not use it in that case.
   MOZ_MUST_USE nsresult MakeFileInputStream(nsIFile *file,
@@ -35,11 +37,12 @@ protected:
 
   virtual MOZ_MUST_USE nsresult OpenContentStream(bool async,
                                                   nsIInputStream **result,
                                                   nsIChannel** channel) override;
 
 private:
   nsCOMPtr<nsIInputStream> mUploadStream;
   int64_t mUploadLength;
+  nsCOMPtr<nsIURI> mFileURI;
 };
 
 #endif // !nsFileChannel_h__
--- a/netwerk/protocol/file/nsFileProtocolHandler.cpp
+++ b/netwerk/protocol/file/nsFileProtocolHandler.cpp
@@ -185,34 +185,38 @@ nsFileProtocolHandler::NewURI(const nsAC
     return CallQueryInterface(url, result);
 }
 
 NS_IMETHODIMP
 nsFileProtocolHandler::NewChannel2(nsIURI* uri,
                                    nsILoadInfo* aLoadInfo,
                                    nsIChannel** result)
 {
+    nsresult rv;
+
     nsFileChannel *chan;
     if (IsNeckoChild()) {
         chan = new mozilla::net::FileChannelChild(uri);
     } else {
         chan = new nsFileChannel(uri);
     }
     if (!chan)
         return NS_ERROR_OUT_OF_MEMORY;
     NS_ADDREF(chan);
 
-    nsresult rv = chan->Init();
+    // set the loadInfo on the new channel ; must do this
+    // before calling Init() on it, since it needs the load
+    // info be already set.
+    rv = chan->SetLoadInfo(aLoadInfo);
     if (NS_FAILED(rv)) {
         NS_RELEASE(chan);
         return rv;
     }
 
-    // set the loadInfo on the new channel
-    rv = chan->SetLoadInfo(aLoadInfo);
+    rv = chan->Init();
     if (NS_FAILED(rv)) {
         NS_RELEASE(chan);
         return rv;
     }
 
     *result = chan;
     return NS_OK;
 }
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -3016,16 +3016,80 @@ void HttpBaseChannel::AssertPrivateBrows
 
   OriginAttributes docShellAttrs;
   loadContext->GetOriginAttributes(docShellAttrs);
   MOZ_ASSERT(mLoadInfo->GetOriginAttributes().mPrivateBrowsingId == docShellAttrs.mPrivateBrowsingId,
              "PrivateBrowsingId values are not the same between LoadInfo and LoadContext.");
 }
 #endif
 
+already_AddRefed<nsILoadInfo>
+HttpBaseChannel::CloneLoadInfoForRedirect(nsIURI * newURI, uint32_t redirectFlags)
+{
+  // make a copy of the loadinfo, append to the redirectchain
+  // this will be set on the newly created channel for the redirect target.
+  if (!mLoadInfo) {
+    return nullptr;
+  }
+
+  nsCOMPtr<nsILoadInfo> newLoadInfo =
+    static_cast<mozilla::LoadInfo*>(mLoadInfo.get())->Clone();
+
+  nsContentPolicyType contentPolicyType = mLoadInfo->GetExternalContentPolicyType();
+  if (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT ||
+      contentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT) {
+    nsCOMPtr<nsIPrincipal> nullPrincipalToInherit = NullPrincipal::Create();
+    newLoadInfo->SetPrincipalToInherit(nullPrincipalToInherit);
+  }
+
+  // re-compute the origin attributes of the loadInfo if it's top-level load.
+  bool isTopLevelDoc =
+    newLoadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_DOCUMENT;
+
+  if (isTopLevelDoc) {
+    nsCOMPtr<nsILoadContext> loadContext;
+    NS_QueryNotificationCallbacks(this, loadContext);
+    OriginAttributes docShellAttrs;
+    if (loadContext) {
+      loadContext->GetOriginAttributes(docShellAttrs);
+    }
+
+    OriginAttributes attrs = newLoadInfo->GetOriginAttributes();
+
+    MOZ_ASSERT(docShellAttrs.mUserContextId == attrs.mUserContextId,
+                "docshell and necko should have the same userContextId attribute.");
+    MOZ_ASSERT(docShellAttrs.mInIsolatedMozBrowser == attrs.mInIsolatedMozBrowser,
+                "docshell and necko should have the same inIsolatedMozBrowser attribute.");
+    MOZ_ASSERT(docShellAttrs.mPrivateBrowsingId == attrs.mPrivateBrowsingId,
+                "docshell and necko should have the same privateBrowsingId attribute.");
+
+    attrs = docShellAttrs;
+    attrs.SetFirstPartyDomain(true, newURI);
+    newLoadInfo->SetOriginAttributes(attrs);
+  }
+
+  // Leave empty, we want a 'clean ground' when creating the new channel.
+  // This will be ensured to be either set by the protocol handler or set
+  // to the redirect target URI properly after the channel creation.
+  newLoadInfo->SetResultPrincipalURI(nullptr);
+
+  bool isInternalRedirect =
+    (redirectFlags & (nsIChannelEventSink::REDIRECT_INTERNAL |
+                      nsIChannelEventSink::REDIRECT_STS_UPGRADE));
+
+  nsCString remoteAddress;
+  Unused << GetRemoteAddress(remoteAddress);
+  nsCOMPtr<nsIRedirectHistoryEntry> entry =
+    new nsRedirectHistoryEntry(GetURIPrincipal(), mReferrer, remoteAddress);
+
+  newLoadInfo->AppendRedirectHistoryEntry(entry, isInternalRedirect);
+
+  return newLoadInfo.forget();
+}
+
 //-----------------------------------------------------------------------------
 // nsHttpChannel::nsITraceableChannel
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 HttpBaseChannel::SetNewListener(nsIStreamListener *aListener, nsIStreamListener **_retval)
 {
   LOG(("HttpBaseChannel::SetNewListener [this=%p, mListener=%p, newListener=%p]",
@@ -3165,29 +3229,52 @@ HttpBaseChannel::ShouldRewriteRedirectTo
 }
 
 nsresult
 HttpBaseChannel::SetupReplacementChannel(nsIURI       *newURI,
                                          nsIChannel   *newChannel,
                                          bool          preserveMethod,
                                          uint32_t      redirectFlags)
 {
+  nsresult rv;
+
   LOG(("HttpBaseChannel::SetupReplacementChannel "
      "[this=%p newChannel=%p preserveMethod=%d]",
      this, newChannel, preserveMethod));
 
+  // Ensure the channel's loadInfo's result principal URI so that it's
+  // either non-null or updated to the redirect target URI.
+  // We must do this because in case the loadInfo's result principal URI
+  // is null, it would be taken from OriginalURI of the channel.  But we
+  // overwrite it with the whole redirect chain first URI before opening
+  // the target channel, hence the information would be lost.
+  // If the protocol handler that created the channel wants to use
+  // the originalURI of the channel as the principal URI, this fulfills
+  // that request - newURI is the original URI of the channel.
+  nsCOMPtr<nsILoadInfo> newLoadInfo = newChannel->GetLoadInfo();
+  if (newLoadInfo) {
+    nsCOMPtr<nsIURI> resultPrincipalURI;
+    rv = newLoadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (!resultPrincipalURI) {
+      rv = newLoadInfo->SetResultPrincipalURI(newURI);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+  }
+
   uint32_t newLoadFlags = mLoadFlags | LOAD_REPLACE;
   // if the original channel was using SSL and this channel is not using
   // SSL, then no need to inhibit persistent caching.  however, if the
   // original channel was not using SSL and has INHIBIT_PERSISTENT_CACHING
   // set, then allow the flag to apply to the redirected channel as well.
   // since we force set INHIBIT_PERSISTENT_CACHING on all HTTPS channels,
   // we only need to check if the original channel was using SSL.
   bool usingSSL = false;
-  nsresult rv = mURI->SchemeIs("https", &usingSSL);
+  rv = mURI->SchemeIs("https", &usingSSL);
   if (NS_SUCCEEDED(rv) && usingSSL)
     newLoadFlags &= ~INHIBIT_PERSISTENT_CACHING;
 
   // Do not pass along LOAD_CHECK_OFFLINE_CACHE
   newLoadFlags &= ~nsICachingChannel::LOAD_CHECK_OFFLINE_CACHE;
 
   newChannel->SetLoadGroup(mLoadGroup);
   newChannel->SetNotificationCallbacks(mCallbacks);
@@ -3202,72 +3289,16 @@ HttpBaseChannel::SetupReplacementChannel
   if (mPrivateBrowsingOverriden) {
     nsCOMPtr<nsIPrivateBrowsingChannel> newPBChannel =
       do_QueryInterface(newChannel);
     if (newPBChannel) {
       newPBChannel->SetPrivate(mPrivateBrowsing);
     }
   }
 
-  // make a copy of the loadinfo, append to the redirectchain
-  // and set it on the new channel
-  if (mLoadInfo) {
-    nsCOMPtr<nsILoadInfo> newLoadInfo =
-      static_cast<mozilla::LoadInfo*>(mLoadInfo.get())->Clone();
-
-    nsContentPolicyType contentPolicyType = mLoadInfo->GetExternalContentPolicyType();
-    if (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT ||
-        contentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT) {
-      nsCOMPtr<nsIPrincipal> nullPrincipalToInherit = NullPrincipal::Create();
-      newLoadInfo->SetPrincipalToInherit(nullPrincipalToInherit);
-    }
-
-    // re-compute the origin attributes of the loadInfo if it's top-level load.
-    bool isTopLevelDoc =
-      newLoadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_DOCUMENT;
-
-    if (isTopLevelDoc) {
-      nsCOMPtr<nsILoadContext> loadContext;
-      NS_QueryNotificationCallbacks(this, loadContext);
-      OriginAttributes docShellAttrs;
-      if (loadContext) {
-        loadContext->GetOriginAttributes(docShellAttrs);
-      }
-
-      OriginAttributes attrs = newLoadInfo->GetOriginAttributes();
-
-      MOZ_ASSERT(docShellAttrs.mUserContextId == attrs.mUserContextId,
-                "docshell and necko should have the same userContextId attribute.");
-      MOZ_ASSERT(docShellAttrs.mInIsolatedMozBrowser == attrs.mInIsolatedMozBrowser,
-                "docshell and necko should have the same inIsolatedMozBrowser attribute.");
-      MOZ_ASSERT(docShellAttrs.mPrivateBrowsingId == attrs.mPrivateBrowsingId,
-                 "docshell and necko should have the same privateBrowsingId attribute.");
-
-      attrs = docShellAttrs;
-      attrs.SetFirstPartyDomain(true, newURI);
-      newLoadInfo->SetOriginAttributes(attrs);
-    }
-
-    bool isInternalRedirect =
-      (redirectFlags & (nsIChannelEventSink::REDIRECT_INTERNAL |
-                        nsIChannelEventSink::REDIRECT_STS_UPGRADE));
-    nsCString remoteAddress;
-    Unused << GetRemoteAddress(remoteAddress);
-    nsCOMPtr<nsIRedirectHistoryEntry> entry =
-      new nsRedirectHistoryEntry(GetURIPrincipal(), mReferrer, remoteAddress);
-
-    newLoadInfo->AppendRedirectHistoryEntry(entry, isInternalRedirect);
-    newChannel->SetLoadInfo(newLoadInfo);
-  }
-  else {
-    // the newChannel was created with a dummy loadInfo, we should clear
-    // it in case the original channel does not have a loadInfo
-    newChannel->SetLoadInfo(nullptr);
-  }
-
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel);
   if (!httpChannel)
     return NS_OK; // no other options to set
 
   // Preserve the CORS preflight information.
   nsCOMPtr<nsIHttpChannelInternal> httpInternal = do_QueryInterface(newChannel);
   if (httpInternal) {
     httpInternal->SetLastRedirectFlags(redirectFlags);
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -427,16 +427,19 @@ protected:
   // the new mUploadStream.
   void EnsureUploadStreamIsCloneableComplete(nsresult aStatus);
 
 #ifdef DEBUG
   // Check if mPrivateBrowsingId matches between LoadInfo and LoadContext.
   void AssertPrivateBrowsingId();
 #endif
 
+  // Called before we create the redirect target channel.
+  already_AddRefed<nsILoadInfo> CloneLoadInfoForRedirect(nsIURI *newURI, uint32_t redirectFlags);
+
   friend class PrivateBrowsingChannel<HttpBaseChannel>;
   friend class InterceptFailedOnStop;
 
 protected:
   // this section is for main-thread-only object
   // all the references need to be proxy released on main thread.
   nsCOMPtr<nsIURI> mURI;
   nsCOMPtr<nsIURI> mOriginalURI;
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -1488,19 +1488,20 @@ HttpChannelChild::SetupRedirect(nsIURI* 
   LOG(("HttpChannelChild::SetupRedirect [this=%p]\n", this));
 
   nsresult rv;
   nsCOMPtr<nsIIOService> ioService;
   rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIChannel> newChannel;
+  nsCOMPtr<nsILoadInfo> redirectLoadInfo = CloneLoadInfoForRedirect(uri, redirectFlags);
   rv = NS_NewChannelInternal(getter_AddRefs(newChannel),
                              uri,
-                             mLoadInfo,
+                             redirectLoadInfo,
                              nullptr, // aLoadGroup
                              nullptr, // aCallbacks
                              nsIRequest::LOAD_NORMAL,
                              ioService);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // We won't get OnStartRequest, set cookies here.
   mResponseHead = new nsHttpResponseHead(*responseHead);
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -2779,24 +2779,26 @@ nsHttpChannel::HandleAsyncAPIRedirect()
 
 nsresult
 nsHttpChannel::StartRedirectChannelToURI(nsIURI *upgradedURI, uint32_t flags)
 {
     nsresult rv = NS_OK;
     LOG(("nsHttpChannel::StartRedirectChannelToURI()\n"));
 
     nsCOMPtr<nsIChannel> newChannel;
+    nsCOMPtr<nsILoadInfo> redirectLoadInfo = CloneLoadInfoForRedirect(upgradedURI,
+                                                                      flags);
 
     nsCOMPtr<nsIIOService> ioService;
     rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = NS_NewChannelInternal(getter_AddRefs(newChannel),
                                upgradedURI,
-                               mLoadInfo,
+                               redirectLoadInfo,
                                nullptr, // aLoadGroup
                                nullptr, // aCallbacks
                                nsIRequest::LOAD_NORMAL,
                                ioService);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = SetupReplacementChannel(upgradedURI, newChannel, true, flags);
     NS_ENSURE_SUCCESS(rv, rv);
@@ -5670,32 +5672,33 @@ nsHttpChannel::ContinueProcessRedirectio
         rv = PromptTempRedirect();
         if (NS_FAILED(rv)) return rv;
     }
 
     nsCOMPtr<nsIIOService> ioService;
     rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
     if (NS_FAILED(rv)) return rv;
 
+    uint32_t redirectFlags;
+    if (nsHttp::IsPermanentRedirect(mRedirectType))
+        redirectFlags = nsIChannelEventSink::REDIRECT_PERMANENT;
+    else
+        redirectFlags = nsIChannelEventSink::REDIRECT_TEMPORARY;
+
     nsCOMPtr<nsIChannel> newChannel;
+    nsCOMPtr<nsILoadInfo> redirectLoadInfo = CloneLoadInfoForRedirect(mRedirectURI, redirectFlags);
     rv = NS_NewChannelInternal(getter_AddRefs(newChannel),
                                mRedirectURI,
-                               mLoadInfo,
+                               redirectLoadInfo,
                                nullptr, // aLoadGroup
                                nullptr, // aCallbacks
                                nsIRequest::LOAD_NORMAL,
                                ioService);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    uint32_t redirectFlags;
-    if (nsHttp::IsPermanentRedirect(mRedirectType))
-        redirectFlags = nsIChannelEventSink::REDIRECT_PERMANENT;
-    else
-        redirectFlags = nsIChannelEventSink::REDIRECT_TEMPORARY;
-
     rv = SetupReplacementChannel(mRedirectURI, newChannel,
                                  !rewriteToGET, redirectFlags);
     if (NS_FAILED(rv)) return rv;
 
     // verify that this is a legal redirect
     mRedirectChannel = newChannel;
 
     PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirection);
--- a/netwerk/protocol/res/ExtensionProtocolHandler.h
+++ b/netwerk/protocol/res/ExtensionProtocolHandler.h
@@ -28,16 +28,20 @@ public:
 protected:
   ~ExtensionProtocolHandler() {}
 
   MOZ_MUST_USE bool ResolveSpecialCases(const nsACString& aHost,
                                         const nsACString& aPath,
                                         const nsACString& aPathname,
                                         nsACString& aResult) override;
 
+  // |result| is an inout param.  On entry to this function, *result
+  // is expected to be non-null and already addrefed.  This function
+  // may release the object stored in *result on entry and write
+  // a new pointer to an already addrefed channel to *result.
   virtual MOZ_MUST_USE nsresult SubstituteChannel(nsIURI* uri,
                                                   nsILoadInfo* aLoadInfo,
                                                   nsIChannel** result) override;
 };
 
 } // namespace net
 } // namespace mozilla
 
--- a/netwerk/protocol/res/SubstitutingProtocolHandler.cpp
+++ b/netwerk/protocol/res/SubstitutingProtocolHandler.cpp
@@ -241,30 +241,38 @@ SubstitutingProtocolHandler::NewURI(cons
 }
 
 nsresult
 SubstitutingProtocolHandler::NewChannel2(nsIURI* uri,
                                          nsILoadInfo* aLoadInfo,
                                          nsIChannel** result)
 {
   NS_ENSURE_ARG_POINTER(uri);
+  NS_ENSURE_ARG_POINTER(aLoadInfo);
+
   nsAutoCString spec;
   nsresult rv = ResolveURI(uri, spec);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIURI> newURI;
   rv = NS_NewURI(getter_AddRefs(newURI), spec);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  // We don't want to allow the inner protocol handler to modify the result
+  // principal URI since we want either |uri| or anything pre-set by upper
+  // layers to prevail.
+  nsCOMPtr<nsIURI> savedResultPrincipalURI;
+  rv = aLoadInfo->GetResultPrincipalURI(getter_AddRefs(savedResultPrincipalURI));
+  NS_ENSURE_SUCCESS(rv, rv);
+
   rv = NS_NewChannelInternal(result, newURI, aLoadInfo);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsLoadFlags loadFlags = 0;
-  (*result)->GetLoadFlags(&loadFlags);
-  (*result)->SetLoadFlags(loadFlags & ~nsIChannel::LOAD_REPLACE);
+  rv = aLoadInfo->SetResultPrincipalURI(savedResultPrincipalURI);
+  NS_ENSURE_SUCCESS(rv, rv);
   rv = (*result)->SetOriginalURI(uri);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return SubstituteChannel(uri, aLoadInfo, result);
 }
 
 nsresult
 SubstitutingProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result)
--- a/netwerk/streamconv/converters/nsMultiMixedConv.cpp
+++ b/netwerk/streamconv/converters/nsMultiMixedConv.cpp
@@ -872,16 +872,20 @@ nsMultiMixedConv::SendStart()
     rv = mPartChannel->SetContentType(mContentType);
     if (NS_FAILED(rv)) return rv;
 
     rv = mPartChannel->SetContentLength(mContentLength);
     if (NS_FAILED(rv)) return rv;
 
     mPartChannel->SetContentDisposition(mContentDisposition);
 
+    // Each part of a multipart/replace response can be used
+    // for the top level document.  We must inform upper layers
+    // about this by setting the LOAD_REPLACE flag so that certain
+    // state assertions are evaluated as positive.
     nsLoadFlags loadFlags = 0;
     mPartChannel->GetLoadFlags(&loadFlags);
     loadFlags |= nsIChannel::LOAD_REPLACE;
     mPartChannel->SetLoadFlags(loadFlags);
 
     nsCOMPtr<nsILoadGroup> loadGroup;
     (void)mPartChannel->GetLoadGroup(getter_AddRefs(loadGroup));
 
--- a/netwerk/test/unit/test_file_protocol.js
+++ b/netwerk/test/unit/test_file_protocol.js
@@ -227,19 +227,16 @@ function test_upload_file() {
 
 function test_load_replace() {
   // lnk files should resolve to their targets
   if (mozinfo.os == "win") {
     dump("*** test_load_replace\n");
     file = do_get_file("data/system_root.lnk", false);
     var chan = new_file_channel(file);
 
-    // The LOAD_REPLACE flag should be set
-    do_check_eq(chan.loadFlags & chan.LOAD_REPLACE, chan.LOAD_REPLACE);
-
     // The original URI path should differ from the URI path
     do_check_neq(chan.URI.path, chan.originalURI.path);
 
     // The original URI path should be the same as the lnk file path
     var ios = Cc["@mozilla.org/network/io-service;1"].
               getService(Ci.nsIIOService);
     do_check_eq(chan.originalURI.path, ios.newFileURI(file).path);
   }
--- a/toolkit/components/mozprotocol/mozProtocolHandler.js
+++ b/toolkit/components/mozprotocol/mozProtocolHandler.js
@@ -26,18 +26,18 @@ mozProtocolHandler.prototype = {
     } else {
       uri.spec = spec;
     }
     return uri;
   },
 
   newChannel2(uri, loadInfo) {
     let realURL = NetUtil.newURI(this.urlToLoad);
-    let channel = Services.io.newChannelFromURIWithLoadInfo(realURL, loadInfo)
-    channel.loadFlags |= Ci.nsIChannel.LOAD_REPLACE;
+    let channel = Services.io.newChannelFromURIWithLoadInfo(realURL, loadInfo);
+    loadInfo.resultPrincipalURI = realURL;
     return channel;
   },
 
   newChannel(uri) {
     return this.newChannel2(uri, null);
   },
 
   classID: Components.ID("{47a45e5f-691e-4799-8686-14f8d3fc0f8c}"),
--- a/toolkit/modules/sessionstore/SessionHistory.jsm
+++ b/toolkit/modules/sessionstore/SessionHistory.jsm
@@ -170,18 +170,39 @@ var SessionHistoryInternal = {
       entry.referrer = shEntry.referrerURI.spec;
       entry.referrerPolicy = shEntry.referrerPolicy;
     }
 
     if (shEntry.originalURI) {
       entry.originalURI = shEntry.originalURI.spec;
     }
 
+    if (shEntry.resultPrincipalURI) {
+      entry.resultPrincipalURI = shEntry.resultPrincipalURI.spec;
+
+      // For downgrade compatibility we store the loadReplace property as it
+      // would be stored before result principal URI introduction so that
+      // the old code can still create URL based principals for channels
+      // correctly.  When resultPrincipalURI is non-null and not equal to
+      // channel's orignalURI in the new code, it's equal to setting
+      // LOAD_REPLACE in the old code.  Note that we only do 'the best we can'
+      // here to derivate the 'old' loadReplace flag value.
+      entry.loadReplace = entry.resultPrincipalURI != entry.originalURI;
+    } else {
+      // We want to store the property to let the backward compatibility code,
+      // when reading the stored session, work. When this property is undefined
+      // that code will derive the result principal URI from the load replace
+      // flag.
+      entry.resultPrincipalURI = null;
+    }
+
     if (shEntry.loadReplace) {
-      entry.loadReplace = shEntry.loadReplace;
+      // Storing under a new property name, since it has changed its meaning
+      // with the result principal URI introduction.
+      entry.loadReplace2 = shEntry.loadReplace;
     }
 
     if (shEntry.srcdocData)
       entry.srcdocData = shEntry.srcdocData;
 
     if (shEntry.isSrcdocEntry)
       entry.isSrcdocEntry = shEntry.isSrcdocEntry;
 
@@ -363,18 +384,27 @@ var SessionHistoryInternal = {
       shEntry.contentType = entry.contentType;
     if (entry.referrer) {
       shEntry.referrerURI = Utils.makeURI(entry.referrer);
       shEntry.referrerPolicy = entry.referrerPolicy;
     }
     if (entry.originalURI) {
       shEntry.originalURI = Utils.makeURI(entry.originalURI);
     }
-    if (entry.loadReplace) {
-      shEntry.loadReplace = entry.loadReplace;
+    if (typeof entry.resultPrincipalURI === "undefined" && entry.loadReplace) {
+      // This is backward compatibility code for stored sessions saved prior to
+      // introduction of the resultPrincipalURI property.  The equivalent of this
+      // property non-null value used to be the URL while the LOAD_REPLACE flag
+      // was set.
+      shEntry.resultPrincipalURI = shEntry.URI;
+    } else if (entry.resultPrincipalURI) {
+      shEntry.resultPrincipalURI = Utils.makeURI(entry.resultPrincipalURI);
+    }
+    if (entry.loadReplace2) {
+      shEntry.loadReplace = entry.loadReplace2;
     }
     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"].