Bug 1469916, r=ckerschb,jkt
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Fri, 22 Jun 2018 15:41:39 +0100
changeset 424918 9a2b02fe351bd65f6850a5c80b91dd0eec4a878a
parent 424884 5d23b6e0b74d74eee52d7af5fa47573ef8afe979
child 424919 7e6eabfa350b1c28cc988ed468cbb7d78cb13b01
push id34227
push userrgurzau@mozilla.com
push dateWed, 04 Jul 2018 09:59:03 +0000
treeherdermozilla-central@cc3401e78e8b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersckerschb, jkt
bugs1469916
milestone63.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 1469916, r=ckerschb,jkt
browser/base/content/nsContextMenu.js
browser/base/content/pageinfo/pageInfo.js
browser/base/content/utilityOverlay.js
browser/components/shell/nsMacShellService.cpp
devtools/shared/gcli/commands/screenshot.js
devtools/startup/devtools-startup.js
dom/base/nsContentAreaDragDrop.cpp
dom/base/nsContentAreaDragDrop.h
dom/tests/browser/browser.ini
dom/tests/browser/browser_persist_cookies.js
dom/tests/browser/mimeme.sjs
dom/tests/browser/set-samesite-cookies-and-redirect.sjs
dom/webbrowserpersist/PWebBrowserPersistDocument.ipdl
dom/webbrowserpersist/WebBrowserPersistDocumentChild.cpp
dom/webbrowserpersist/WebBrowserPersistLocalDocument.cpp
dom/webbrowserpersist/WebBrowserPersistRemoteDocument.cpp
dom/webbrowserpersist/WebBrowserPersistRemoteDocument.h
dom/webbrowserpersist/nsIWebBrowserPersist.idl
dom/webbrowserpersist/nsIWebBrowserPersistDocument.idl
dom/webbrowserpersist/nsWebBrowserPersist.cpp
dom/webbrowserpersist/nsWebBrowserPersist.h
toolkit/components/browser/nsWebBrowser.cpp
toolkit/components/downloads/test/unit/head.js
toolkit/components/viewsource/content/viewSourceUtils.js
toolkit/content/contentAreaUtils.js
toolkit/content/tests/browser/browser_saveImageURL.js
toolkit/mozapps/extensions/LightweightThemeManager.jsm
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -990,22 +990,32 @@ nsContextMenu.prototype = {
     }
     if (!name)
       name = "snapshot.jpg";
 
     mm.sendAsyncMessage("ContextMenu:SaveVideoFrameAsImage", {}, {
       target: this.target,
     });
 
+    // Cache this because we fetch the data async
+    let {documentURIObject} = gContextMenuContentData;
+
     let onMessage = (message) => {
       mm.removeMessageListener("ContextMenu:SaveVideoFrameAsImage:Result", onMessage);
+      // FIXME can we switch this to a blob URL?
       let dataURL = message.data.dataURL;
-      saveImageURL(dataURL, name, "SaveImageTitle", true, false,
-                   document.documentURIObject, null, null, null,
-                   isPrivate);
+      saveImageURL(dataURL, name, "SaveImageTitle",
+                   true, // bypass cache
+                   false, // don't skip prompt for where to save
+                   documentURIObject, // referrer
+                   null, // document
+                   null, // content type
+                   null, // content disposition
+                   isPrivate,
+                   this.principal);
     };
     mm.addMessageListener("ContextMenu:SaveVideoFrameAsImage:Result", onMessage);
   },
 
   leaveDOMFullScreen() {
     document.exitFullscreen();
   },
 
@@ -1233,23 +1243,25 @@ nsContextMenu.prototype = {
     let isContentWindowPrivate = this.isRemote ? this.ownerDoc.isPrivate : undefined;
     let referrerURI = gContextMenuContentData.documentURIObject;
     let isPrivate = PrivateBrowsingUtils.isBrowserPrivate(this.browser);
     if (this.onCanvas) {
       // Bypass cache, since it's a data: URL.
       this._canvasToBlobURL(this.target).then(function(blobURL) {
         saveImageURL(blobURL, "canvas.png", "SaveImageTitle",
                      true, false, referrerURI, null, null, null,
-                     isPrivate);
+                     isPrivate,
+                     document.nodePrincipal /* system, because blob: */);
       }, Cu.reportError);
     } else if (this.onImage) {
       urlSecurityCheck(this.mediaURL, this.principal);
       saveImageURL(this.mediaURL, null, "SaveImageTitle", false,
                    false, referrerURI, null, gContextMenuContentData.contentType,
-                   gContextMenuContentData.contentDisposition, isPrivate);
+                   gContextMenuContentData.contentDisposition, isPrivate,
+                   this.principal);
     } else if (this.onVideo || this.onAudio) {
       var dialogTitle = this.onVideo ? "SaveVideoTitle" : "SaveAudioTitle";
       this.saveHelper(this.mediaURL, null, dialogTitle, false, doc, referrerURI,
                       this.frameOuterWindowID, "", isContentWindowPrivate);
     }
   },
 
   // Backwards-compatibility wrapper
--- a/browser/base/content/pageinfo/pageInfo.js
+++ b/browser/base/content/pageinfo/pageInfo.js
@@ -684,17 +684,18 @@ function saveMedia() {
               null, gDocInfo.isContentWindowPrivate);
     }
   } else {
     selectSaveFolder(function(aDirectory) {
       if (aDirectory) {
         var saveAnImage = function(aURIString, aChosenData, aBaseURI) {
           uniqueFile(aChosenData.file);
           internalSave(aURIString, null, null, null, null, false, "SaveImageTitle",
-                       aChosenData, aBaseURI, null, false, null, gDocInfo.isContentWindowPrivate);
+                       aChosenData, aBaseURI, null, false, null,
+                       gDocInfo.isContentWindowPrivate, gDocInfo.principal);
         };
 
         for (var i = 0; i < rowArray.length; i++) {
           let v = rowArray[i];
           let dir = aDirectory.clone();
           let item = gImageView.data[v][COL_IMAGE_NODE];
           let uriString = gImageView.data[v][COL_IMAGE_ADDRESS];
           let uri = makeURI(uriString);
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -300,17 +300,18 @@ function openLinkIn(url, where, params) 
       params.forceAboutBlankViewerInCurrent;
   var aResolveOnNewTabCreated = params.resolveOnNewTabCreated;
 
   if (where == "save") {
     // TODO(1073187): propagate referrerPolicy.
 
     // ContentClick.jsm passes isContentWindowPrivate for saveURL instead of passing a CPOW initiatingDoc
     if ("isContentWindowPrivate" in params) {
-      saveURL(url, null, null, true, true, aNoReferrer ? null : aReferrerURI, null, params.isContentWindowPrivate);
+      saveURL(url, null, null, true, true, aNoReferrer ? null : aReferrerURI,
+              null, params.isContentWindowPrivate, aPrincipal);
     } else {
       if (!aInitiatingDoc) {
         Cu.reportError("openUILink/openLinkIn was called with " +
           "where == 'save' but without initiatingDoc.  See bug 814264.");
         return;
       }
       saveURL(url, null, null, true, true, aNoReferrer ? null : aReferrerURI, aInitiatingDoc);
     }
--- a/browser/components/shell/nsMacShellService.cpp
+++ b/browser/components/shell/nsMacShellService.cpp
@@ -148,17 +148,17 @@ nsMacShellService::SetDesktopBackground(
 
   nsCOMPtr<nsILoadContext> loadContext;
   nsCOMPtr<nsISupports> container = aElement->OwnerDoc()->GetContainer();
   nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(container);
   if (docShell) {
     loadContext = do_QueryInterface(docShell);
   }
 
-  return wbp->SaveURI(imageURI, 0,
+  return wbp->SaveURI(imageURI, aElement->NodePrincipal(), 0,
                       docURI, aElement->OwnerDoc()->GetReferrerPolicy(),
                       nullptr, nullptr,
                       mBackgroundFile, loadContext);
 }
 
 NS_IMETHODIMP
 nsMacShellService::OnProgressChange(nsIWebProgress* aWebProgress,
                                     nsIRequest* aRequest,
--- a/devtools/shared/gcli/commands/screenshot.js
+++ b/devtools/shared/gcli/commands/screenshot.js
@@ -549,17 +549,16 @@ var saveToFile = Task.async(function* (c
   // Create download and track its progress.
   // This is adapted from saveURL in contentAreaUtils.js, but simplified greatly
   // and modified to allow saving to arbitrary paths on disk.  Using these
   // objects as opposed to just writing with OS.File allows us to tie into the
   // download manager to record a download entry and to get visual feedback from
   // the downloads toolbar button when the save is done.
   const nsIWBP = Ci.nsIWebBrowserPersist;
   const flags = nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
-                nsIWBP.PERSIST_FLAGS_FORCE_ALLOW_COOKIES |
                 nsIWBP.PERSIST_FLAGS_BYPASS_CACHE |
                 nsIWBP.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
   const isPrivate =
     PrivateBrowsingUtils.isContentWindowPrivate(document.defaultView);
   const persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
                   .createInstance(Ci.nsIWebBrowserPersist);
   persist.persistFlags = flags;
   const tr = Cc["@mozilla.org/transfer;1"].createInstance(Ci.nsITransfer);
@@ -568,17 +567,19 @@ var saveToFile = Task.async(function* (c
           "",
           null,
           null,
           null,
           persist,
           isPrivate);
   const listener = new DownloadListener(window, tr);
   persist.progressListener = listener;
+  const principal = Services.scriptSecurityManager.getSystemPrincipal();
   persist.savePrivacyAwareURI(sourceURI,
+                              principal,
                               0,
                               document.documentURIObject,
                               Ci.nsIHttpChannel.REFERRER_POLICY_UNSET,
                               null,
                               null,
                               targetFileURI,
                               isPrivate);
 
--- a/devtools/startup/devtools-startup.js
+++ b/devtools/startup/devtools-startup.js
@@ -35,16 +35,18 @@ const { XPCOMUtils } = ChromeUtils.impor
 ChromeUtils.defineModuleGetter(this, "Services",
                                "resource://gre/modules/Services.jsm");
 ChromeUtils.defineModuleGetter(this, "AppConstants",
                                "resource://gre/modules/AppConstants.jsm");
 ChromeUtils.defineModuleGetter(this, "CustomizableUI",
                                "resource:///modules/CustomizableUI.jsm");
 ChromeUtils.defineModuleGetter(this, "CustomizableWidgets",
                                "resource:///modules/CustomizableWidgets.jsm");
+ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
+                               "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 // We don't want to spend time initializing the full loader here so we create
 // our own lazy require.
 XPCOMUtils.defineLazyGetter(this, "Telemetry", function() {
   const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
   // eslint-disable-next-line no-shadow
   const Telemetry = require("devtools/client/shared/telemetry");
 
@@ -943,26 +945,42 @@ const JsonView = {
    */
   onSave: function(message) {
     const chrome = Services.wm.getMostRecentWindow("navigator:browser");
     const browser = chrome.gBrowser.selectedBrowser;
     if (message.data === null) {
       // Save original contents
       chrome.saveBrowser(browser);
     } else {
+      if (!message.data.startsWith("blob:null") || !browser.contentPrincipal.isNullPrincipal) {
+        Cu.reportError("Got invalid request to save JSON data");
+        return;
+      }
       // The following code emulates saveBrowser, but:
       // - Uses the given blob URL containing the custom contents to save.
       // - Obtains the file name from the URL of the document, not the blob.
+      // - avoids passing the document and explicitly passes system principal.
+      //   We have a blob created by a null principal to save, and the null
+      //   principal is from the child. Null principals don't survive crossing
+      //   over IPC, so there's no other principal that'll work.
       const persistable = browser.frameLoader;
       persistable.startPersistence(0, {
         onDocumentReady(doc) {
           const uri = chrome.makeURI(doc.documentURI, doc.characterSet);
           const filename = chrome.getDefaultFileName(undefined, uri, doc, null);
-          chrome.internalSave(message.data, doc, filename, null, doc.contentType,
-            false, null, null, null, doc, false, null, undefined);
+          chrome.internalSave(message.data, null, filename, null, doc.contentType,
+            false /* bypass cache */,
+            null, /* filepicker title key */
+            null, /* file chosen */
+            null, /* referrer */
+            null, /* initiating document */
+            false, /* don't skip prompt for a location */
+            null, /* cache key */
+            PrivateBrowsingUtils.isBrowserPrivate(browser), /* private browsing ? */
+            Services.scriptSecurityManager.getSystemPrincipal());
         },
         onError(status) {
           throw new Error("JSON Viewer's onSave failed in startPersistence");
         }
       });
     }
   }
 };
--- a/dom/base/nsContentAreaDragDrop.cpp
+++ b/dom/base/nsContentAreaDragDrop.cpp
@@ -136,16 +136,17 @@ nsContentAreaDragDrop::GetDragData(nsPID
 
 NS_IMPL_ISUPPORTS(nsContentAreaDragDropDataProvider, nsIFlavorDataProvider)
 
 // SaveURIToFile
 // used on platforms where it's possible to drag items (e.g. images)
 // into the file system
 nsresult
 nsContentAreaDragDropDataProvider::SaveURIToFile(nsIURI* inSourceURI,
+                                                 nsIPrincipal* inTriggeringPrincipal,
                                                  nsIFile* inDestFile,
                                                  bool isPrivate)
 {
   nsCOMPtr<nsIURL> sourceURL = do_QueryInterface(inSourceURI);
   if (!sourceURL) {
     return NS_ERROR_NO_INTERFACE;
   }
 
@@ -157,17 +158,18 @@ nsContentAreaDragDropDataProvider::SaveU
   nsCOMPtr<nsIWebBrowserPersist> persist =
     do_CreateInstance("@mozilla.org/embedding/browser/nsWebBrowserPersist;1",
                       &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   persist->SetPersistFlags(nsIWebBrowserPersist::PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION);
 
   // referrer policy can be anything since the referrer is nullptr
-  return persist->SavePrivacyAwareURI(inSourceURI, 0, nullptr,
+  return persist->SavePrivacyAwareURI(inSourceURI,
+                                      inTriggeringPrincipal, 0, nullptr,
                                       mozilla::net::RP_Unset,
                                       nullptr, nullptr,
                                       inDestFile, isPrivate);
 }
 
 /*
  * Check if the provided filename extension is valid for the MIME type and
  * return the MIME type's primary extension.
@@ -337,17 +339,19 @@ nsContentAreaDragDropDataProvider::GetFl
     rv = destDirectory->Clone(getter_AddRefs(file));
     NS_ENSURE_SUCCESS(rv, rv);
 
     file->Append(targetFilename);
 
     bool isPrivate;
     aTransferable->GetIsPrivateData(&isPrivate);
 
-    rv = SaveURIToFile(sourceURI, file, isPrivate);
+    nsCOMPtr<nsIPrincipal> principal;
+    aTransferable->GetRequestingPrincipal(getter_AddRefs(principal));
+    rv = SaveURIToFile(sourceURI, principal, file, isPrivate);
     // send back an nsIFile
     if (NS_SUCCEEDED(rv)) {
       CallQueryInterface(file, aData);
       *aDataLen = sizeof(nsIFile*);
     }
   }
 
   return rv;
--- a/dom/base/nsContentAreaDragDrop.h
+++ b/dom/base/nsContentAreaDragDrop.h
@@ -72,14 +72,15 @@ class nsContentAreaDragDropDataProvider 
 {
   virtual ~nsContentAreaDragDropDataProvider() {}
 
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIFLAVORDATAPROVIDER
 
   nsresult SaveURIToFile(nsIURI* inSourceURI,
+                         nsIPrincipal* inTriggeringPrincipal,
                          nsIFile* inDestFile, bool isPrivate);
 };
 
 
 #endif /* nsContentAreaDragDrop_h__ */
 
--- a/dom/tests/browser/browser.ini
+++ b/dom/tests/browser/browser.ini
@@ -52,16 +52,20 @@ support-files =
 run-if = e10s
 [browser_largeAllocation_win32.js]
 skip-if = !e10s || os != "win" || processor != "x86" # Large-Allocation requires e10s
 [browser_largeAllocation_non_win32.js]
 skip-if = !e10s || (os == "win" && processor == "x86") || (verify && debug && (os == 'linux')) # Large-Allocation requires e10s
 [browser_localStorage_e10s.js]
 skip-if = !e10s || verify # This is a test of e10s functionality.
 [browser_localStorage_privatestorageevent.js]
+[browser_persist_cookies.js]
+support-files =
+  set-samesite-cookies-and-redirect.sjs
+  mimeme.sjs
 [browser_test_focus_after_modal_state.js]
 skip-if = verify
 support-files =
   focus_after_prompt.html
 [browser_test_new_window_from_content.js]
 tags = openwindow
 skip-if = toolkit == 'android'  || (os == "linux" && debug) # see bug 1261495 for Linux debug time outs
 support-files =
new file mode 100644
--- /dev/null
+++ b/dom/tests/browser/browser_persist_cookies.js
@@ -0,0 +1,94 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_PATH = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://example.org");
+const TEST_PATH2 = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://example.com");
+
+var MockFilePicker = SpecialPowers.MockFilePicker;
+MockFilePicker.init(window);
+
+registerCleanupFunction(async function() {
+  info("Running the cleanup code");
+  MockFilePicker.cleanup();
+  Services.obs.removeObserver(checkRequest, "http-on-modify-request");
+  if (gTestDir && gTestDir.exists()) {
+    // On Windows, sometimes nsIFile.remove() throws, probably because we're
+    // still writing to the directory we're trying to remove, despite
+    // waiting for the download to complete. Just retry a bit later...
+    let succeeded = false;
+    while (!succeeded) {
+      try {
+        gTestDir.remove(true);
+        succeeded = true;
+      } catch (ex) {
+        await new Promise(requestAnimationFrame);
+      }
+    }
+  }
+});
+
+let gTestDir = null;
+
+
+function checkRequest(subject) {
+  let httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);
+  let spec = httpChannel.URI.spec;
+  // Ignore initial requests for page that sets cookies and its favicon, which may not have
+  // cookies.
+  if (httpChannel.URI.host == "example.org" && !spec.endsWith("favicon.ico") && !spec.includes("redirect.sjs")) {
+    let cookie = httpChannel.getRequestHeader("cookie");
+    is(cookie.trim(), "normalCookie=true", "Should have correct cookie in request for " + spec);
+  }
+}
+
+function createTemporarySaveDirectory() {
+  var saveDir = Services.dirsvc.get("TmpD", Ci.nsIFile);
+  saveDir.append("testsavedir");
+  if (!saveDir.exists()) {
+    info("create testsavedir!");
+    saveDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
+  }
+  info("return from createTempSaveDir: " + saveDir.path);
+  return saveDir;
+}
+
+add_task(async function() {
+  await BrowserTestUtils.withNewTab("about:blank", async function(browser) {
+    Services.obs.addObserver(checkRequest, "http-on-modify-request");
+    BrowserTestUtils.loadURI(browser, TEST_PATH + "set-samesite-cookies-and-redirect.sjs");
+    // Test that the original document load doesn't send same-site cookies.
+    await BrowserTestUtils.browserLoaded(browser, true, TEST_PATH2 + "set-samesite-cookies-and-redirect.sjs");
+    // Now check the saved page.
+    // Create the folder the link will be saved into.
+    gTestDir = createTemporarySaveDirectory();
+    let destFile = gTestDir.clone();
+
+    MockFilePicker.displayDirectory = gTestDir;
+    let fileName;
+    MockFilePicker.showCallback = function(fp) {
+      info("showCallback");
+      fileName = fp.defaultString;
+      info("fileName: " + fileName);
+      destFile.append(fileName);
+      info("path: " + destFile.path);
+      MockFilePicker.setFiles([destFile]);
+      MockFilePicker.filterIndex = 0; // kSaveAsType_Complete
+      info("done showCallback");
+    };
+    saveBrowser(browser);
+    await new Promise(async (resolve) => {
+      let dls = await Downloads.getList(Downloads.PUBLIC);
+      dls.addView({
+        onDownloadChanged(download) {
+          if (download.succeeded) {
+            dls.removeView(this);
+            dls.removeFinished();
+            resolve();
+          }
+        }
+      });
+    });
+  });
+});
new file mode 100644
--- /dev/null
+++ b/dom/tests/browser/mimeme.sjs
@@ -0,0 +1,26 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+function handleRequest(request, response) {
+  let mimeType = request.queryString.match(/type=([a-z]*)/)[1];
+  switch (mimeType) {
+    case "css":
+      response.setHeader("Content-Type", "text/css");
+      response.write("#hi {color: red}");
+      break;
+    case "js":
+      response.setHeader("Content-Type", "application/javascript");
+      response.write("var foo;");
+      break;
+    case "png":
+      response.setHeader("Content-Type", "image/png");
+      response.write("");
+      break;
+    case "html":
+      response.setHeader("Content-Type", "text/html");
+      response.write("<body>I am a subframe</body>");
+      break;
+  }
+}
new file mode 100644
--- /dev/null
+++ b/dom/tests/browser/set-samesite-cookies-and-redirect.sjs
@@ -0,0 +1,33 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+function handleRequest(request, response) {
+  // Set cookies and redirect for .org:
+  if (request.host.endsWith(".org")) {
+    response.setHeader("Set-Cookie", "normalCookie=true; path=/;", true);
+    response.setHeader("Set-Cookie", "laxHeader=true; path=/; SameSite=Lax", true);
+    response.setHeader("Set-Cookie", "strictHeader=true; path=/; SameSite=Strict", true);
+    response.write(`
+      <head>
+        <meta http-equiv='set-cookie' content='laxMeta=true; path=/; SameSite=Lax'>
+        <meta http-equiv='set-cookie' content='strictMeta=true; path=/; SameSite=Strict'>
+      </head>
+      <body>
+        <script>
+        document.cookie = 'laxScript=true; path=/; SameSite=Lax';
+        document.cookie = 'strictScript=true; path=/; SameSite=Strict';
+        location.href = location.href.replace(/\.org/, ".com");
+        </script>
+      </body>`);
+  } else {
+    let baseURI = "https://example.org/" + request.path.replace(/[a-z-]*\.sjs/, "mimeme.sjs?type=");
+    response.write(`
+      <link rel="stylesheet" type="text/css" href="${baseURI}css">
+      <iframe src="${baseURI}html"></iframe>
+      <script src="${baseURI}js"></script>
+      <img src="${baseURI}png">
+    `);
+  }
+}
--- a/dom/webbrowserpersist/PWebBrowserPersistDocument.ipdl
+++ b/dom/webbrowserpersist/PWebBrowserPersistDocument.ipdl
@@ -5,16 +5,17 @@
 
 include protocol PContent;
 include protocol PWebBrowserPersistResources;
 include protocol PWebBrowserPersistSerialize;
 include protocol PFileDescriptorSet;
 include protocol PChildToParentStream; //FIXME: bug #792908
 include protocol PParentToChildStream; //FIXME: bug #792908
 
+include PBackgroundSharedTypes;
 include IPCStream;
 
 namespace mozilla {
 
 // nsIWebBrowserPersistDocument has attributes which can be read
 // synchronously.  To avoid using sync IPC for them, the actor sends
 // this structure from the child to the parent before the parent actor
 // is exposed to XPCOM.
@@ -24,16 +25,17 @@ struct WebBrowserPersistDocumentAttrs {
   nsCString baseURI;
   nsCString contentType;
   nsCString characterSet;
   nsString title;
   nsString referrer;
   nsString contentDisposition;
   uint32_t cacheKey;
   uint32_t persistFlags;
+  PrincipalInfo principal;
 };
 
 // IPDL doesn't have tuples, so this gives the pair of strings from
 // nsIWebBrowserPersistURIMap::getURIMapping a name.
 struct WebBrowserPersistURIMapEntry {
   nsCString mapFrom;
   nsCString mapTo;
 };
--- a/dom/webbrowserpersist/WebBrowserPersistDocumentChild.cpp
+++ b/dom/webbrowserpersist/WebBrowserPersistDocumentChild.cpp
@@ -36,16 +36,17 @@ void
 WebBrowserPersistDocumentChild::Start(nsIWebBrowserPersistDocument* aDocument)
 {
     MOZ_ASSERT(!mDocument);
     if (!aDocument) {
         SendInitFailure(NS_ERROR_FAILURE);
         return;
     }
 
+    nsCOMPtr<nsIPrincipal> principal;
     WebBrowserPersistDocumentAttrs attrs;
     nsCOMPtr<nsIInputStream> postDataStream;
 #define ENSURE(e) do {           \
         nsresult rv = (e);       \
         if (NS_FAILED(rv)) {     \
             SendInitFailure(rv); \
             return;              \
         }                        \
@@ -55,16 +56,20 @@ WebBrowserPersistDocumentChild::Start(ns
     ENSURE(aDocument->GetBaseURI(attrs.baseURI()));
     ENSURE(aDocument->GetContentType(attrs.contentType()));
     ENSURE(aDocument->GetCharacterSet(attrs.characterSet()));
     ENSURE(aDocument->GetTitle(attrs.title()));
     ENSURE(aDocument->GetReferrer(attrs.referrer()));
     ENSURE(aDocument->GetContentDisposition(attrs.contentDisposition()));
     ENSURE(aDocument->GetCacheKey(&(attrs.cacheKey())));
     ENSURE(aDocument->GetPersistFlags(&(attrs.persistFlags())));
+
+    ENSURE(aDocument->GetPrincipal(getter_AddRefs(principal)));
+    ENSURE(ipc::PrincipalToPrincipalInfo(principal, &(attrs.principal())));
+
     ENSURE(aDocument->GetPostData(getter_AddRefs(postDataStream)));
 #undef ENSURE
 
     mozilla::ipc::AutoIPCStream autoStream;
     autoStream.Serialize(postDataStream,
                          static_cast<mozilla::dom::ContentChild*>(Manager()));
 
     mDocument = aDocument;
--- a/dom/webbrowserpersist/WebBrowserPersistLocalDocument.cpp
+++ b/dom/webbrowserpersist/WebBrowserPersistLocalDocument.cpp
@@ -179,16 +179,24 @@ WebBrowserPersistLocalDocument::GetPostD
     nsCOMPtr<nsISHEntry> history = GetHistory();
     if (!history) {
         *aStream = nullptr;
         return NS_OK;
     }
     return history->GetPostData(aStream);
 }
 
+NS_IMETHODIMP
+WebBrowserPersistLocalDocument::GetPrincipal(nsIPrincipal** aPrincipal)
+{
+  nsCOMPtr<nsIPrincipal> nodePrincipal = mDocument->NodePrincipal();
+  nodePrincipal.forget(aPrincipal);
+  return NS_OK;
+}
+
 already_AddRefed<nsISHEntry>
 WebBrowserPersistLocalDocument::GetHistory()
 {
     nsCOMPtr<nsPIDOMWindowOuter> window = mDocument->GetDefaultView();
     if (NS_WARN_IF(!window)) {
         return nullptr;
     }
     nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(window);
--- a/dom/webbrowserpersist/WebBrowserPersistRemoteDocument.cpp
+++ b/dom/webbrowserpersist/WebBrowserPersistRemoteDocument.cpp
@@ -4,30 +4,35 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebBrowserPersistRemoteDocument.h"
 #include "WebBrowserPersistDocumentParent.h"
 #include "WebBrowserPersistResourcesParent.h"
 #include "WebBrowserPersistSerializeParent.h"
 #include "mozilla/Unused.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+
+#include "nsIPrincipal.h"
 
 namespace mozilla {
 
 NS_IMPL_ISUPPORTS(WebBrowserPersistRemoteDocument,
                   nsIWebBrowserPersistDocument)
 
 WebBrowserPersistRemoteDocument
 ::WebBrowserPersistRemoteDocument(WebBrowserPersistDocumentParent* aActor,
                                   const Attrs& aAttrs,
                                   nsIInputStream* aPostData)
 : mActor(aActor)
 , mAttrs(aAttrs)
 , mPostData(aPostData)
 {
+  nsresult rv;
+  mPrincipal = ipc::PrincipalInfoToPrincipal(mAttrs.principal(), &rv);
 }
 
 WebBrowserPersistRemoteDocument::~WebBrowserPersistRemoteDocument()
 {
     if (mActor) {
         Unused << WebBrowserPersistDocumentParent::Send__delete__(mActor);
         // That will call mActor->ActorDestroy, which calls this->ActorDestroy
         // (whether or not the IPC send succeeds).
@@ -128,16 +133,24 @@ NS_IMETHODIMP
 WebBrowserPersistRemoteDocument::GetPostData(nsIInputStream** aStream)
 {
     nsCOMPtr<nsIInputStream> stream = mPostData;
     stream.forget(aStream);
     return NS_OK;
 }
 
 NS_IMETHODIMP
+WebBrowserPersistRemoteDocument::GetPrincipal(nsIPrincipal** aPrincipal)
+{
+    nsCOMPtr<nsIPrincipal> nodePrincipal = mPrincipal;
+    nodePrincipal.forget(aPrincipal);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
 WebBrowserPersistRemoteDocument::ReadResources(nsIWebBrowserPersistResourceVisitor* aVisitor)
 {
     if (!mActor) {
         return NS_ERROR_FAILURE;
     }
     RefPtr<WebBrowserPersistResourcesParent> subActor =
         new WebBrowserPersistResourcesParent(this, aVisitor);
     return mActor->SendPWebBrowserPersistResourcesConstructor(
--- a/dom/webbrowserpersist/WebBrowserPersistRemoteDocument.h
+++ b/dom/webbrowserpersist/WebBrowserPersistRemoteDocument.h
@@ -8,16 +8,18 @@
 #define WebBrowserPersistRemoteDocument_h__
 
 #include "mozilla/Maybe.h"
 #include "mozilla/PWebBrowserPersistDocumentParent.h"
 #include "nsCOMPtr.h"
 #include "nsIWebBrowserPersistDocument.h"
 #include "nsIInputStream.h"
 
+class nsIPrincipal;
+
 // This class is the XPCOM half of the glue between the
 // nsIWebBrowserPersistDocument interface and a remote document; it is
 // created by WebBrowserPersistDocumentParent when (and if) it
 // receives the information needed to populate the interface's
 // properties.
 //
 // This object has a normal refcounted lifetime.  The corresponding
 // IPC actor holds a weak reference to this class; when the last
@@ -35,16 +37,17 @@ public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIWEBBROWSERPERSISTDOCUMENT
 
 private:
     using Attrs = WebBrowserPersistDocumentAttrs;
     WebBrowserPersistDocumentParent* mActor;
     Attrs mAttrs;
     nsCOMPtr<nsIInputStream> mPostData;
+    nsCOMPtr<nsIPrincipal> mPrincipal;
 
     friend class WebBrowserPersistDocumentParent;
     WebBrowserPersistRemoteDocument(WebBrowserPersistDocumentParent* aActor,
                                     const Attrs& aAttrs,
                                     nsIInputStream* aPostData);
     ~WebBrowserPersistRemoteDocument();
 
     void ActorDestroy(void);
--- a/dom/webbrowserpersist/nsIWebBrowserPersist.idl
+++ b/dom/webbrowserpersist/nsIWebBrowserPersist.idl
@@ -7,16 +7,17 @@
 #include "nsICancelable.idl"
 
 interface nsIURI;
 interface nsIInputStream;
 interface nsIWebProgressListener;
 interface nsIFile;
 interface nsIChannel;
 interface nsILoadContext;
+interface nsIPrincipal;
 
 /**
  * Interface for persisting DOM documents and URIs to local or remote storage.
  */
 [scriptable, uuid(8cd752a4-60b1-42c3-a819-65c7a1138a28)]
 interface nsIWebBrowserPersist : nsICancelable
 {
   /** No special persistence behaviour. */
@@ -61,22 +62,16 @@ interface nsIWebBrowserPersist : nsICanc
   const unsigned long PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION = 16384;
   /**
    * Append the downloaded data to the target file.
    * This can only be used when persisting to a local file.
    */
   const unsigned long PERSIST_FLAGS_APPEND_TO_FILE = 32768;
 
   /**
-   * Force relevant cookies to be sent with this load even if normally they
-   * wouldn't be.
-   */
-  const unsigned long PERSIST_FLAGS_FORCE_ALLOW_COOKIES = 65536;
-
-  /**
    * Flags governing how data is fetched and saved from the network.
    * It is best to set this value explicitly unless you are prepared
    * to accept the default values.
    */
   attribute unsigned long persistFlags;
 
   /** Persister is ready to save data */
   const unsigned long PERSIST_STATE_READY = 1;
@@ -111,16 +106,18 @@ interface nsIWebBrowserPersist : nsICanc
   attribute nsIWebProgressListener progressListener;
 
   /**
    * Save the specified URI to file.
    *
    * @param aURI       URI to save to file. Some implementations of this interface
    *                   may also support <CODE>nullptr</CODE> to imply the currently
    *                   loaded URI.
+   * @param aTriggeringPrincipal
+   *                   The triggering principal for the URI we're saving.
    * @param aCacheKey  The necko cache key integer.
    * @param aReferrer  The referrer URI to pass with an HTTP request or
    *                   <CODE>nullptr</CODE>.
    * @param aReferrerPolicy  The referrer policy for when and what to send via
    *                   HTTP Referer header.  Ignored if aReferrer is
    *                   <CODE>nullptr</CODE>.  Taken from REFERRER_POLICY
    *                   constants in nsIHttpChannel.
    * @param aPostData  Post data to pass with an HTTP request or
@@ -137,29 +134,31 @@ interface nsIWebBrowserPersist : nsICanc
    *                   window or document)
    *
    * @see nsIFile
    * @see nsIURI
    * @see nsIInputStream
    *
    * @throws NS_ERROR_INVALID_ARG One or more arguments was invalid.
    */
-  void saveURI(in nsIURI aURI, in unsigned long aCacheKey,
+  void saveURI(in nsIURI aURI, in nsIPrincipal aTriggeringPrincipal,
+      in unsigned long aCacheKey,
       in nsIURI aReferrer, in unsigned long aReferrerPolicy,
       in nsIInputStream aPostData,
       in string aExtraHeaders, in nsISupports aFile,
       in nsILoadContext aPrivacyContext);
 
   /**
    * @param aIsPrivate Treat the save operation as private (ie. with
    *                   regards to networking operations and persistence
    *                   of intermediate data, etc.)
    * @see saveURI for all other parameter descriptions
    */
-  void savePrivacyAwareURI(in nsIURI aURI, in unsigned long aCacheKey,
+  void savePrivacyAwareURI(in nsIURI aURI,
+      in nsIPrincipal aTriggeringPrincipal, in unsigned long aCacheKey,
       in nsIURI aReferrer, in unsigned long aReferrerPolicy,
       in nsIInputStream aPostData,
       in string aExtraHeaders, in nsISupports aFile,
       in boolean aIsPrivate);
 
   /**
    * Save a channel to a file. It must not be opened yet.
    * @see saveURI
--- a/dom/webbrowserpersist/nsIWebBrowserPersistDocument.idl
+++ b/dom/webbrowserpersist/nsIWebBrowserPersistDocument.idl
@@ -3,16 +3,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 interface nsIInputStream;
 interface nsIOutputStream;
+interface nsIPrincipal;
 interface nsITabParent;
 interface nsIWebBrowserPersistResourceVisitor;
 interface nsIWebBrowserPersistWriteCompletion;
 
 /**
  * Interface for the URI-mapping information that can be supplied when
  * serializing the DOM of an nsIWebBrowserPersistDocument.
  *
@@ -55,16 +56,17 @@ interface nsIWebBrowserPersistDocument :
   readonly attribute AUTF8String documentURI;
   readonly attribute AUTF8String baseURI;
   readonly attribute ACString contentType;
   readonly attribute ACString characterSet;
   readonly attribute AString title;
   readonly attribute AString referrer;
   readonly attribute AString contentDisposition;
   readonly attribute nsIInputStream postData;
+  readonly attribute nsIPrincipal principal;
 
   /**
    * The cache key.  Unlike in nsISHEntry, where it's wrapped in an
    * nsISupportsPRUint32, this is just the integer.
    */
   readonly attribute unsigned long cacheKey;
 
   /**
--- a/dom/webbrowserpersist/nsWebBrowserPersist.cpp
+++ b/dom/webbrowserpersist/nsWebBrowserPersist.cpp
@@ -78,16 +78,17 @@ struct nsWebBrowserPersist::WalkData
 };
 
 // Information about a DOM document
 struct nsWebBrowserPersist::DocData
 {
     nsCOMPtr<nsIURI> mBaseURI;
     nsCOMPtr<nsIWebBrowserPersistDocument> mDocument;
     nsCOMPtr<nsIURI> mFile;
+    nsCOMPtr<nsIPrincipal> mPrincipal;
     nsCString mCharset;
 };
 
 // Information about a URI
 struct nsWebBrowserPersist::URIData
 {
     bool mNeedsPersisting;
     bool mSaved;
@@ -408,44 +409,48 @@ NS_IMETHODIMP nsWebBrowserPersist::SetPr
 {
     mProgressListener = aProgressListener;
     mProgressListener2 = do_QueryInterface(aProgressListener);
     mEventSink = do_GetInterface(aProgressListener);
     return NS_OK;
 }
 
 NS_IMETHODIMP nsWebBrowserPersist::SaveURI(
-    nsIURI *aURI, uint32_t aCacheKey,
+    nsIURI *aURI, nsIPrincipal *aPrincipal, uint32_t aCacheKey,
     nsIURI *aReferrer, uint32_t aReferrerPolicy,
     nsIInputStream *aPostData, const char *aExtraHeaders,
     nsISupports *aFile, nsILoadContext* aPrivacyContext)
 {
-    return SavePrivacyAwareURI(aURI, aCacheKey, aReferrer, aReferrerPolicy,
-                               aPostData, aExtraHeaders, aFile,
-                               aPrivacyContext && aPrivacyContext->UsePrivateBrowsing());
+    bool isPrivate =
+      aPrivacyContext && aPrivacyContext->UsePrivateBrowsing();
+    return SavePrivacyAwareURI(aURI, aPrincipal, aCacheKey,
+                               aReferrer, aReferrerPolicy,
+                               aPostData, aExtraHeaders, aFile, isPrivate);
 }
 
 NS_IMETHODIMP nsWebBrowserPersist::SavePrivacyAwareURI(
-    nsIURI *aURI, uint32_t aCacheKey,
+    nsIURI *aURI, nsIPrincipal *aPrincipal, uint32_t aCacheKey,
     nsIURI *aReferrer, uint32_t aReferrerPolicy,
     nsIInputStream *aPostData, const char *aExtraHeaders,
     nsISupports *aFile, bool aIsPrivate)
 {
     NS_ENSURE_TRUE(mFirstAndOnlyUse, NS_ERROR_FAILURE);
     mFirstAndOnlyUse = false; // Stop people from reusing this object!
 
     nsCOMPtr<nsIURI> fileAsURI;
     nsresult rv;
     rv = GetValidURIFromObject(aFile, getter_AddRefs(fileAsURI));
     NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
 
     // SaveURI doesn't like broken uris.
     mPersistFlags |= PERSIST_FLAGS_FAIL_ON_BROKEN_LINKS;
-    rv = SaveURIInternal(aURI, aCacheKey, aReferrer, aReferrerPolicy,
-                         aPostData, aExtraHeaders, fileAsURI, false, aIsPrivate);
+    rv = SaveURIInternal(aURI, aPrincipal, aCacheKey,
+                         aReferrer, aReferrerPolicy,
+                         aPostData, aExtraHeaders, fileAsURI,
+                         false, aIsPrivate);
     return NS_FAILED(rv) ? rv : NS_OK;
 }
 
 NS_IMETHODIMP nsWebBrowserPersist::SaveChannel(
     nsIChannel *aChannel, nsISupports *aFile)
 {
     NS_ENSURE_TRUE(mFirstAndOnlyUse, NS_ERROR_FAILURE);
     mFirstAndOnlyUse = false; // Stop people from reusing this object!
@@ -594,27 +599,32 @@ nsWebBrowserPersist::SerializeNextFile()
             URIData *data = iter.UserData();
             if (data->mNeedsPersisting && !data->mSaved) {
                 urisToPersist++;
             }
         }
     }
 
     if (urisToPersist > 0) {
+        nsCOMPtr<nsIPrincipal> docPrincipal;
+        //XXXgijs I *think* this is already always true, but let's be sure.
+        MOZ_ASSERT(mDocList.Length() > 0,
+            "Should have the document for any walked URIs to persist!");
+        nsresult rv = mDocList.ElementAt(0)->mDocument->
+            GetPrincipal(getter_AddRefs(docPrincipal));
+        NS_ENSURE_SUCCESS_VOID(rv);
         // Persist each file in the uri map. The document(s)
         // will be saved after the last one of these is saved.
         for (auto iter = mURIMap.Iter(); !iter.Done(); iter.Next()) {
             URIData *data = iter.UserData();
 
             if (!data->mNeedsPersisting || data->mSaved) {
                 continue;
             }
 
-            nsresult rv;
-
             // Create a URI from the key.
             nsCOMPtr<nsIURI> uri;
             rv = NS_NewURI(getter_AddRefs(uri), iter.Key(),
                            data->mCharset.get());
             if (NS_WARN_IF(NS_FAILED(rv))) {
                 break;
             }
 
@@ -622,17 +632,17 @@ nsWebBrowserPersist::SerializeNextFile()
             nsCOMPtr<nsIURI> fileAsURI = data->mDataPath;
             rv = AppendPathToURI(fileAsURI, data->mFilename, fileAsURI);
             if (NS_WARN_IF(NS_FAILED(rv))) {
                 break;
             }
 
             // The Referrer Policy doesn't matter here since the referrer is
             // nullptr.
-            rv = SaveURIInternal(uri, 0, nullptr,
+            rv = SaveURIInternal(uri, docPrincipal, 0, nullptr,
                                  mozilla::net::RP_Unset, nullptr, nullptr,
                                  fileAsURI, true, mIsPrivate);
             // If SaveURIInternal fails, then it will have called EndDownload,
             // which means that |data| is no longer valid memory. We MUST bail.
             if (NS_WARN_IF(NS_FAILED(rv))) {
                 break;
             }
 
@@ -1319,17 +1329,18 @@ nsWebBrowserPersist::AppendPathToURI(nsI
     AppendUTF16toUTF8(aPath, newPath);
 
     return NS_MutateURI(aURI)
              .SetPathQueryRef(newPath)
              .Finalize(aOutURI);
 }
 
 nsresult nsWebBrowserPersist::SaveURIInternal(
-    nsIURI *aURI, uint32_t aCacheKey, nsIURI *aReferrer,
+    nsIURI *aURI, nsIPrincipal* aTriggeringPrincipal,
+    uint32_t aCacheKey, nsIURI *aReferrer,
     uint32_t aReferrerPolicy, nsIInputStream *aPostData,
     const char *aExtraHeaders, nsIURI *aFile,
     bool aCalcFileExt, bool aIsPrivate)
 {
     NS_ENSURE_ARG_POINTER(aURI);
     NS_ENSURE_ARG_POINTER(aFile);
 
     nsresult rv = NS_OK;
@@ -1345,17 +1356,17 @@ nsresult nsWebBrowserPersist::SaveURIInt
     {
         loadFlags |= nsIRequest::LOAD_FROM_CACHE;
     }
 
     // Open a channel to the URI
     nsCOMPtr<nsIChannel> inputChannel;
     rv = NS_NewChannel(getter_AddRefs(inputChannel),
                        aURI,
-                       nsContentUtils::GetSystemPrincipal(),
+                       aTriggeringPrincipal,
                        nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
                        nsIContentPolicy::TYPE_OTHER,
                        nullptr,  // aPerformanceStorage
                        nullptr,  // aLoadGroup
                        static_cast<nsIInterfaceRequestor*>(this),
                        loadFlags);
 
     nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(inputChannel);
@@ -1375,26 +1386,16 @@ nsresult nsWebBrowserPersist::SaveURIInt
     {
         nsCOMPtr<nsIEncodedChannel> encodedChannel(do_QueryInterface(inputChannel));
         if (encodedChannel)
         {
             encodedChannel->SetApplyConversion(false);
         }
     }
 
-    if (mPersistFlags & PERSIST_FLAGS_FORCE_ALLOW_COOKIES)
-    {
-        nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
-                do_QueryInterface(inputChannel);
-        if (httpChannelInternal) {
-            rv = httpChannelInternal->SetThirdPartyFlags(nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW);
-            MOZ_ASSERT(NS_SUCCEEDED(rv));
-        }
-    }
-
     // Set the referrer, post data and headers if any
     nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(inputChannel));
     if (httpChannel)
     {
         // Referrer
         if (aReferrer)
         {
             rv = httpChannel->SetReferrerWithPolicy(aReferrer, aReferrerPolicy);
--- a/dom/webbrowserpersist/nsWebBrowserPersist.h
+++ b/dom/webbrowserpersist/nsWebBrowserPersist.h
@@ -52,17 +52,18 @@ public:
     NS_DECL_NSIREQUESTOBSERVER
     NS_DECL_NSISTREAMLISTENER
     NS_DECL_NSIPROGRESSEVENTSINK
 
 // Private members
 private:
     virtual ~nsWebBrowserPersist();
     nsresult SaveURIInternal(
-        nsIURI *aURI, uint32_t aCacheKey, nsIURI *aReferrer,
+        nsIURI *aURI, nsIPrincipal* aTriggeringPrincipal,
+        uint32_t aCacheKey, nsIURI *aReferrer,
         uint32_t aReferrerPolicy, nsIInputStream *aPostData,
         const char *aExtraHeaders, nsIURI *aFile,
         bool aCalcFileExt, bool aIsPrivate);
     nsresult SaveChannelInternal(
         nsIChannel *aChannel, nsIURI *aFile, bool aCalcFileExt);
     nsresult SaveDocumentInternal(
         nsIWebBrowserPersistDocument *aDocument,
         nsIURI *aFile,
--- a/toolkit/components/browser/nsWebBrowser.cpp
+++ b/toolkit/components/browser/nsWebBrowser.cpp
@@ -981,31 +981,34 @@ NS_IMETHODIMP
 nsWebBrowser::SetProgressListener(nsIWebProgressListener* aProgressListener)
 {
   mProgressListener = aProgressListener;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWebBrowser::SaveURI(nsIURI* aURI,
+                      nsIPrincipal* aPrincipal,
                       uint32_t aCacheKey,
                       nsIURI* aReferrer,
                       uint32_t aReferrerPolicy,
                       nsIInputStream* aPostData,
                       const char* aExtraHeaders,
                       nsISupports* aFile,
                       nsILoadContext* aPrivacyContext)
 {
   return SavePrivacyAwareURI(
-    aURI, aCacheKey, aReferrer, aReferrerPolicy, aPostData, aExtraHeaders,
-    aFile, aPrivacyContext && aPrivacyContext->UsePrivateBrowsing());
+    aURI, aPrincipal, aCacheKey, aReferrer, aReferrerPolicy, aPostData,
+    aExtraHeaders, aFile,
+    aPrivacyContext && aPrivacyContext->UsePrivateBrowsing());
 }
 
 NS_IMETHODIMP
 nsWebBrowser::SavePrivacyAwareURI(nsIURI* aURI,
+                                  nsIPrincipal* aPrincipal,
                                   uint32_t aCacheKey,
                                   nsIURI* aReferrer,
                                   uint32_t aReferrerPolicy,
                                   nsIInputStream* aPostData,
                                   const char* aExtraHeaders,
                                   nsISupports* aFile,
                                   bool aIsPrivate)
 {
@@ -1033,18 +1036,19 @@ nsWebBrowser::SavePrivacyAwareURI(nsIURI
   // Create a throwaway persistence object to do the work
   nsresult rv;
   mPersist = do_CreateInstance(NS_WEBBROWSERPERSIST_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
   mPersist->SetProgressListener(this);
   mPersist->SetPersistFlags(mPersistFlags);
   mPersist->GetCurrentState(&mPersistCurrentState);
 
-  rv = mPersist->SavePrivacyAwareURI(uri, aCacheKey, aReferrer, aReferrerPolicy,
-                                     aPostData, aExtraHeaders, aFile, aIsPrivate);
+  rv = mPersist->SavePrivacyAwareURI(uri, aPrincipal, aCacheKey,
+                                     aReferrer, aReferrerPolicy, aPostData,
+                                     aExtraHeaders, aFile, aIsPrivate);
   if (NS_FAILED(rv)) {
     mPersist = nullptr;
   }
   return rv;
 }
 
 NS_IMETHODIMP
 nsWebBrowser::SaveChannel(nsIChannel* aChannel, nsISupports* aFile)
--- a/toolkit/components/downloads/test/unit/head.js
+++ b/toolkit/components/downloads/test/unit/head.js
@@ -278,17 +278,18 @@ function promiseStartLegacyDownload(aSou
       // Initialize the components so they reference each other.  This will cause
       // the Download object to be created and added to the public downloads.
       transfer.init(sourceURI, NetUtil.newURI(targetFile), null, mimeInfo, null,
                     null, persist, isPrivate);
       persist.progressListener = transfer;
 
       // Start the actual download process.
       persist.savePrivacyAwareURI(
-        sourceURI, 0, referrer, Ci.nsIHttpChannel.REFERRER_POLICY_UNSAFE_URL,
+        sourceURI, Services.scriptSecurityManager.getSystemPrincipal(),
+        0, referrer, Ci.nsIHttpChannel.REFERRER_POLICY_UNSAFE_URL,
         null, null, targetFile, isPrivate);
     }).catch(do_report_unexpected_exception);
 
   });
 }
 
 /**
  * Starts a new download using the nsIHelperAppService interface, and controls
--- a/toolkit/components/viewsource/content/viewSourceUtils.js
+++ b/toolkit/components/viewsource/content/viewSourceUtils.js
@@ -221,17 +221,21 @@ var gViewSourceUtils = {
           this.viewSourceProgressListener.file = file;
 
           var webBrowserPersist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
             .createInstance(this.mnsIWebBrowserPersist);
           // the default setting is to not decode. we need to decode.
           webBrowserPersist.persistFlags = this.mnsIWebBrowserPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
           webBrowserPersist.progressListener = this.viewSourceProgressListener;
           let referrerPolicy = Ci.nsIHttpChannel.REFERRER_POLICY_NO_REFERRER;
-          webBrowserPersist.savePrivacyAwareURI(uri, null, null, referrerPolicy, null, null, file, data.isPrivate);
+          let ssm = Services.scriptSecurityManager;
+          let principal = ssm.createCodebasePrincipal(data.uri,
+            browser.contentPrincipal.originAttributes);
+          webBrowserPersist.savePrivacyAwareURI(uri, principal, null, null,
+            referrerPolicy, null, null, file, data.isPrivate);
 
           let helperService = Cc["@mozilla.org/uriloader/external-helper-app-service;1"]
             .getService(Ci.nsPIExternalAppLauncher);
           if (data.isPrivate) {
             // register the file to be deleted when possible
             helperService.deleteTemporaryPrivateFileWhenPossible(file);
           } else {
             // register the file to be deleted on app exit
--- a/toolkit/content/contentAreaUtils.js
+++ b/toolkit/content/contentAreaUtils.js
@@ -57,24 +57,25 @@ function forbidCPOW(arg, func, argname) 
 // - An image with an extension (e.g. .jpg) in its file name, using
 //   Context->Save Image As...
 // - An image without an extension (e.g. a banner ad on cnn.com) using
 //   the above method.
 // - A linked document using Save Link As...
 // - A linked document using Alt-click Save Link As...
 //
 function saveURL(aURL, aFileName, aFilePickerTitleKey, aShouldBypassCache,
-                 aSkipPrompt, aReferrer, aSourceDocument, aIsContentWindowPrivate) {
+                 aSkipPrompt, aReferrer, aSourceDocument,
+                 aIsContentWindowPrivate, aPrincipal) {
   forbidCPOW(aURL, "saveURL", "aURL");
   forbidCPOW(aReferrer, "saveURL", "aReferrer");
   // Allow aSourceDocument to be a CPOW.
 
   internalSave(aURL, null, aFileName, null, null, aShouldBypassCache,
                aFilePickerTitleKey, null, aReferrer, aSourceDocument,
-               aSkipPrompt, null, aIsContentWindowPrivate);
+               aSkipPrompt, null, aIsContentWindowPrivate, aPrincipal);
 }
 
 // Just like saveURL, but will get some info off the image before
 // calling internalSave
 // Clientele: (Make sure you don't break any of these)
 //  - Context ->  Save Image As...
 const imgICache = Ci.imgICache;
 const nsISupportsCString = Ci.nsISupportsCString;
@@ -107,17 +108,17 @@ const nsISupportsCString = Ci.nsISupport
  * @param aContentDisp (string, optional)
  *        The content disposition of the image.
  * @param aIsContentWindowPrivate (bool)
  *        Whether or not the containing window is in private browsing mode.
  *        Does not need to be provided is aDoc is passed.
  */
 function saveImageURL(aURL, aFileName, aFilePickerTitleKey, aShouldBypassCache,
                       aSkipPrompt, aReferrer, aDoc, aContentType, aContentDisp,
-                      aIsContentWindowPrivate) {
+                      aIsContentWindowPrivate, aPrincipal) {
   forbidCPOW(aURL, "saveImageURL", "aURL");
   forbidCPOW(aReferrer, "saveImageURL", "aReferrer");
 
   if (aDoc && aIsContentWindowPrivate == undefined) {
     if (Cu.isCrossProcessWrapper(aDoc)) {
       Deprecated.warning("saveImageURL should not be passed document CPOWs. " +
                          "The caller should pass in the content type and " +
                          "disposition themselves",
@@ -151,17 +152,17 @@ function saveImageURL(aURL, aFileName, a
       }
     } catch (e) {
       // Failure to get type and content-disposition off the image is non-fatal
     }
   }
 
   internalSave(aURL, null, aFileName, aContentDisp, aContentType,
                aShouldBypassCache, aFilePickerTitleKey, null, aReferrer,
-               null, aSkipPrompt, null, aIsContentWindowPrivate);
+               aDoc, aSkipPrompt, null, aIsContentWindowPrivate, aPrincipal);
 }
 
 // This is like saveDocument, but takes any browser/frame-like element
 // and saves the current document inside it,
 // whether in-process or out-of-process.
 function saveBrowser(aBrowser, aSkipPrompt, aOuterWindowID = 0) {
   if (!aBrowser) {
     throw "Must have a browser when calling saveBrowser";
@@ -326,21 +327,25 @@ XPCOMUtils.defineConstant(this, "kSaveAs
  *        default downloads folder without prompting.
  * @param aCacheKey [optional]
  *        If set will be passed to saveURI.  See nsIWebBrowserPersist for
  *        allowed values.
  * @param aIsContentWindowPrivate [optional]
  *        This parameter is provided when the aInitiatingDocument is not a
  *        real document object. Stores whether aInitiatingDocument.defaultView
  *        was private or not.
+ * @param aPrincipal [optional]
+ *        This parameter is provided when neither aDocument nor
+ *        aInitiatingDocument is provided. Used to determine what level of
+ *        privilege to load the URI with.
  */
 function internalSave(aURL, aDocument, aDefaultFileName, aContentDisposition,
                       aContentType, aShouldBypassCache, aFilePickerTitleKey,
                       aChosenData, aReferrer, aInitiatingDocument, aSkipPrompt,
-                      aCacheKey, aIsContentWindowPrivate) {
+                      aCacheKey, aIsContentWindowPrivate, aPrincipal) {
   forbidCPOW(aURL, "internalSave", "aURL");
   forbidCPOW(aReferrer, "internalSave", "aReferrer");
   forbidCPOW(aCacheKey, "internalSave", "aCacheKey");
   // Allow aInitiatingDocument to be a CPOW.
 
   if (aSkipPrompt == undefined)
     aSkipPrompt = false;
 
@@ -406,18 +411,27 @@ function internalSave(aURL, aDocument, a
 
     let isPrivate = aIsContentWindowPrivate;
     if (isPrivate === undefined) {
       isPrivate = aInitiatingDocument.nodeType == 9 /* DOCUMENT_NODE */
         ? PrivateBrowsingUtils.isContentWindowPrivate(aInitiatingDocument.defaultView)
         : aInitiatingDocument.isPrivate;
     }
 
+    // We have to cover the cases here where we were either passed an explicit
+    // principal, or a 'real' document (with a nodePrincipal property), or an
+    // nsIWebBrowserPersistDocument which has a principal property.
+    let sourcePrincipal =
+      aPrincipal ||
+      (aDocument && (aDocument.nodePrincipal || aDocument.principal)) ||
+      (aInitiatingDocument && aInitiatingDocument.nodePrincipal);
+
     var persistArgs = {
       sourceURI,
+      sourcePrincipal,
       sourceReferrer: aReferrer,
       sourceDocument: useSaveDocument ? aDocument : null,
       targetContentType: (saveAsType == kSaveAsType_Text) ? "text/plain" : null,
       targetFile: file,
       sourceCacheKey: aCacheKey,
       sourcePostData: nonCPOWDocument ? getPostData(aDocument) : null,
       bypassCache: aShouldBypassCache,
       isPrivate,
@@ -458,18 +472,17 @@ function internalSave(aURL, aDocument, a
  * @param persistArgs.isPrivate
  *        Indicates whether this is taking place in a private browsing context.
  */
 function internalPersist(persistArgs) {
   var persist = makeWebBrowserPersist();
 
   // Calculate persist flags.
   const nsIWBP = Ci.nsIWebBrowserPersist;
-  const flags = nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
-                nsIWBP.PERSIST_FLAGS_FORCE_ALLOW_COOKIES;
+  const flags = nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
   if (persistArgs.bypassCache)
     persist.persistFlags = flags | nsIWBP.PERSIST_FLAGS_BYPASS_CACHE;
   else
     persist.persistFlags = flags | nsIWBP.PERSIST_FLAGS_FROM_CACHE;
 
   // Leave it to WebBrowserPersist to discover the encoding type (or lack thereof):
   persist.persistFlags |= nsIWBP.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
 
@@ -506,16 +519,17 @@ function internalPersist(persistArgs) {
       encodingFlags |= nsIWBP.ENCODE_FLAGS_ENCODE_BASIC_ENTITIES;
     }
 
     const kWrapColumn = 80;
     persist.saveDocument(persistArgs.sourceDocument, targetFileURL, filesFolder,
                          persistArgs.targetContentType, encodingFlags, kWrapColumn);
   } else {
     persist.savePrivacyAwareURI(persistArgs.sourceURI,
+                                persistArgs.sourcePrincipal,
                                 persistArgs.sourceCacheKey,
                                 persistArgs.sourceReferrer,
                                 Ci.nsIHttpChannel.REFERRER_POLICY_UNSET,
                                 persistArgs.sourcePostData,
                                 null,
                                 targetFileURL,
                                 persistArgs.isPrivate);
   }
--- a/toolkit/content/tests/browser/browser_saveImageURL.js
+++ b/toolkit/content/tests/browser/browser_saveImageURL.js
@@ -31,17 +31,18 @@ add_task(async function preferred_API() 
     gBrowser,
     url: IMAGE_PAGE,
   }, async function(browser) {
     let url = await ContentTask.spawn(browser, null, async function() {
       let image = content.document.getElementById("image");
       return image.href;
     });
 
-    saveImageURL(url, "image.jpg", null, true, false, null, null, null, null, false);
+    saveImageURL(url, "image.jpg", null, true, false, null, null, null, null,
+      false, gBrowser.contentPrincipal);
     let channel = gBrowser.contentDocumentAsCPOW.docShell.currentDocumentChannel;
     if (channel) {
       ok(true, channel.QueryInterface(Ci.nsIHttpChannelInternal)
                       .channelIsForDownload);
 
       // Throttleable is the only class flag assigned to downloads.
       ok(channel.QueryInterface(Ci.nsIClassOfService).classFlags,
          Ci.nsIClassOfService.Throttleable);
--- a/toolkit/mozapps/extensions/LightweightThemeManager.jsm
+++ b/toolkit/mozapps/extensions/LightweightThemeManager.jsm
@@ -966,17 +966,18 @@ function _persistImage(sourceURL, localF
     Ci.nsIWebBrowserPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
     Ci.nsIWebBrowserPersist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION |
     (PERSIST_BYPASS_CACHE ?
        Ci.nsIWebBrowserPersist.PERSIST_FLAGS_BYPASS_CACHE :
        Ci.nsIWebBrowserPersist.PERSIST_FLAGS_FROM_CACHE);
 
   persist.progressListener = new _persistProgressListener(successCallback);
 
-  persist.saveURI(sourceURI, 0,
+  let sourcePrincipal = Services.scriptSecurityManager.createCodebasePrincipal(sourceURI, {});
+  persist.saveURI(sourceURI, sourcePrincipal, 0,
                   null, Ci.nsIHttpChannel.REFERRER_POLICY_UNSET,
                   null, null, targetURI, null);
 }
 
 function _persistProgressListener(successCallback) {
   this.onLocationChange = function() {};
   this.onProgressChange = function() {};
   this.onStatusChange   = function() {};