commit 23c6e68b1208d20ea342d3d4693f1ea7c1050661 Author: Gijs Kruitbosch gijskruitbosch@gmail.com Date: Fri Jun 22 15:41:39 2018 +0100
Bug 1469916, r=ckerschb,jkt
--HG-- extra : rebase_source : 180442deeef92f0e9202d76c5e4e46b630072d99 extra : source : be11a32900298eb6fd4d18ad21b9a699995254c3 --- browser/base/content/nsContextMenu.js | 22 +++-- browser/base/content/pageinfo/pageInfo.js | 3 +- browser/base/content/utilityOverlay.js | 3 +- browser/components/shell/nsMacShellService.cpp | 4 +- devtools/shared/gcli/commands/screenshot.js | 3 +- devtools/shim/devtools-startup.js | 22 ++++- dom/base/nsContentAreaDragDrop.cpp | 8 +- dom/base/nsContentAreaDragDrop.h | 1 + dom/tests/browser/browser.ini | 4 + dom/tests/browser/browser_persist_cookies.js | 94 ++++++++++++++++++++++ dom/tests/browser/mimeme.sjs | 26 ++++++ .../browser/set-samesite-cookies-and-redirect.sjs | 33 ++++++++ .../PWebBrowserPersistDocument.ipdl | 2 + .../WebBrowserPersistDocumentChild.cpp | 5 ++ .../WebBrowserPersistLocalDocument.cpp | 8 ++ .../WebBrowserPersistRemoteDocument.cpp | 13 +++ .../WebBrowserPersistRemoteDocument.h | 3 + dom/webbrowserpersist/nsIWebBrowserPersist.idl | 15 ++-- .../nsIWebBrowserPersistDocument.idl | 2 + dom/webbrowserpersist/nsWebBrowserPersist.cpp | 45 ++++++----- dom/webbrowserpersist/nsWebBrowserPersist.h | 3 +- toolkit/components/browser/nsWebBrowser.cpp | 12 ++- toolkit/components/downloads/test/unit/head.js | 3 +- .../viewsource/content/viewSourceUtils.js | 6 +- toolkit/content/contentAreaUtils.js | 28 +++++-- .../content/tests/browser/browser_saveImageURL.js | 3 +- .../mozapps/extensions/LightweightThemeManager.jsm | 3 +- 27 files changed, 314 insertions(+), 60 deletions(-)
diff --git a/browser/base/content/nsContextMenu.js b/browser/base/content/nsContextMenu.js index 368d0475ac34..5cebd9c38c56 100644 --- a/browser/base/content/nsContextMenu.js +++ b/browser/base/content/nsContextMenu.js @@ -986,12 +986,22 @@ nsContextMenu.prototype = { 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); }, @@ -1228,13 +1238,15 @@ nsContextMenu.prototype = { 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) { urlSecurityCheck(this.mediaURL, this.principal); var dialogTitle = this.onVideo ? "SaveVideoTitle" : "SaveAudioTitle"; diff --git a/browser/base/content/pageinfo/pageInfo.js b/browser/base/content/pageinfo/pageInfo.js index 86f548c74494..405b9443342d 100644 --- a/browser/base/content/pageinfo/pageInfo.js +++ b/browser/base/content/pageinfo/pageInfo.js @@ -704,7 +704,8 @@ function saveMedia() { 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++) { diff --git a/browser/base/content/utilityOverlay.js b/browser/base/content/utilityOverlay.js index b73a01a0b0f3..a1ab9ddd1c9b 100644 --- a/browser/base/content/utilityOverlay.js +++ b/browser/base/content/utilityOverlay.js @@ -258,7 +258,8 @@ function openLinkIn(url, where, params) {
// 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 " + diff --git a/browser/components/shell/nsMacShellService.cpp b/browser/components/shell/nsMacShellService.cpp index 0a2672d8f84b..9b46fb8eca87 100644 --- a/browser/components/shell/nsMacShellService.cpp +++ b/browser/components/shell/nsMacShellService.cpp @@ -155,8 +155,8 @@ nsMacShellService::SetDesktopBackground(nsIDOMElement* aElement, loadContext = do_QueryInterface(docShell); }
- return wbp->SaveURI(imageURI, nullptr, - docURI, content->OwnerDoc()->GetReferrerPolicy(), + return wbp->SaveURI(imageURI, aElement->NodePrincipal(), nullptr, + docURI, aElement->OwnerDoc()->GetReferrerPolicy(), nullptr, nullptr, mBackgroundFile, loadContext); } diff --git a/devtools/shared/gcli/commands/screenshot.js b/devtools/shared/gcli/commands/screenshot.js index fc1383225aa1..a64f6527c4c0 100644 --- a/devtools/shared/gcli/commands/screenshot.js +++ b/devtools/shared/gcli/commands/screenshot.js @@ -558,7 +558,6 @@ var saveToFile = Task.async(function* (context, reply) { // 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; let isPrivate = @@ -577,7 +576,9 @@ var saveToFile = Task.async(function* (context, reply) { isPrivate); let listener = new DownloadListener(window, tr); persist.progressListener = listener; + const principal = Services.scriptSecurityManager.getSystemPrincipal(); persist.savePrivacyAwareURI(sourceURI, + principal, null, document.documentURIObject, Ci.nsIHttpChannel.REFERRER_POLICY_UNSET, diff --git a/devtools/shim/devtools-startup.js b/devtools/shim/devtools-startup.js index 9089812048db..3a0211b547a6 100644 --- a/devtools/shim/devtools-startup.js +++ b/devtools/shim/devtools-startup.js @@ -43,6 +43,8 @@ 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");
XPCOMUtils.defineLazyGetter(this, "StartupBundle", function () { const url = "chrome://devtools-shim/locale/startup.properties"; @@ -901,16 +903,32 @@ const JsonView = { // 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. let persistable = browser.frameLoader; persistable.startPersistence(0, { onDocumentReady(doc) { let uri = chrome.makeURI(doc.documentURI, doc.characterSet); let 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"); diff --git a/dom/base/nsContentAreaDragDrop.cpp b/dom/base/nsContentAreaDragDrop.cpp index 2b315267464d..55ed2d5aa480 100644 --- a/dom/base/nsContentAreaDragDrop.cpp +++ b/dom/base/nsContentAreaDragDrop.cpp @@ -146,6 +146,7 @@ NS_IMPL_ISUPPORTS(nsContentAreaDragDropDataProvider, nsIFlavorDataProvider) // into the file system nsresult nsContentAreaDragDropDataProvider::SaveURIToFile(nsIURI* inSourceURI, + nsIPrincipal* inTriggeringPrincipal, nsIFile* inDestFile, bool isPrivate) { @@ -167,7 +168,8 @@ nsContentAreaDragDropDataProvider::SaveURIToFile(nsIURI* inSourceURI, persist->SetPersistFlags(nsIWebBrowserPersist::PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION);
// referrer policy can be anything since the referrer is nullptr - return persist->SavePrivacyAwareURI(inSourceURI, nullptr, nullptr, + return persist->SavePrivacyAwareURI(inSourceURI, + inTriggeringPrincipal, nullptr, nullptr, mozilla::net::RP_Unset, nullptr, nullptr, inDestFile, isPrivate); @@ -347,7 +349,9 @@ nsContentAreaDragDropDataProvider::GetFlavorData(nsITransferable *aTransferable, 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); diff --git a/dom/base/nsContentAreaDragDrop.h b/dom/base/nsContentAreaDragDrop.h index 978e74905414..eb3416dbfbf7 100644 --- a/dom/base/nsContentAreaDragDrop.h +++ b/dom/base/nsContentAreaDragDrop.h @@ -77,6 +77,7 @@ public: NS_DECL_NSIFLAVORDATAPROVIDER
nsresult SaveURIToFile(nsIURI* inSourceURI, + nsIPrincipal* inTriggeringPrincipal, nsIFile* inDestFile, bool isPrivate); };
diff --git a/dom/tests/browser/browser.ini b/dom/tests/browser/browser.ini index e1d853f1af5b..11cf9ebf2346 100644 --- a/dom/tests/browser/browser.ini +++ b/dom/tests/browser/browser.ini @@ -55,6 +55,10 @@ skip-if = !e10s || (os == "win" && processor == "x86") # Large-Allocation requir [browser_localStorage_e10s.js] skip-if = !e10s # 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] support-files = focus_after_prompt.html diff --git a/dom/tests/browser/browser_persist_cookies.js b/dom/tests/browser/browser_persist_cookies.js new file mode 100644 index 000000000000..78f3966f3bbc --- /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(); + } + } + }); + }); + }); +}); diff --git a/dom/tests/browser/mimeme.sjs b/dom/tests/browser/mimeme.sjs new file mode 100644 index 000000000000..9b92548f041d --- /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; + } +} diff --git a/dom/tests/browser/set-samesite-cookies-and-redirect.sjs b/dom/tests/browser/set-samesite-cookies-and-redirect.sjs new file mode 100644 index 000000000000..74a494db9dbc --- /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"> + `); + } +} diff --git a/dom/webbrowserpersist/PWebBrowserPersistDocument.ipdl b/dom/webbrowserpersist/PWebBrowserPersistDocument.ipdl index ad6332763e68..7c2fc64c53d2 100644 --- a/dom/webbrowserpersist/PWebBrowserPersistDocument.ipdl +++ b/dom/webbrowserpersist/PWebBrowserPersistDocument.ipdl @@ -10,6 +10,7 @@ include protocol PFileDescriptorSet; include protocol PChildToParentStream; //FIXME: bug #792908 include protocol PParentToChildStream; //FIXME: bug #792908
+include PBackgroundSharedTypes; include IPCStream;
namespace mozilla { @@ -29,6 +30,7 @@ struct WebBrowserPersistDocumentAttrs { nsString contentDisposition; uint32_t cacheKey; uint32_t persistFlags; + PrincipalInfo principal; };
// IPDL doesn't have tuples, so this gives the pair of strings from diff --git a/dom/webbrowserpersist/WebBrowserPersistDocumentChild.cpp b/dom/webbrowserpersist/WebBrowserPersistDocumentChild.cpp index 4a8e31b6f4eb..7b893d145f7a 100644 --- a/dom/webbrowserpersist/WebBrowserPersistDocumentChild.cpp +++ b/dom/webbrowserpersist/WebBrowserPersistDocumentChild.cpp @@ -41,6 +41,7 @@ WebBrowserPersistDocumentChild::Start(nsIWebBrowserPersistDocument* aDocument) return; }
+ nsCOMPtr<nsIPrincipal> principal; WebBrowserPersistDocumentAttrs attrs; nsCOMPtr<nsIInputStream> postDataStream; #define ENSURE(e) do { \ @@ -60,6 +61,10 @@ WebBrowserPersistDocumentChild::Start(nsIWebBrowserPersistDocument* aDocument) 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
diff --git a/dom/webbrowserpersist/WebBrowserPersistLocalDocument.cpp b/dom/webbrowserpersist/WebBrowserPersistLocalDocument.cpp index d6527e47d7ca..6d706c39f1c2 100644 --- a/dom/webbrowserpersist/WebBrowserPersistLocalDocument.cpp +++ b/dom/webbrowserpersist/WebBrowserPersistLocalDocument.cpp @@ -199,6 +199,14 @@ WebBrowserPersistLocalDocument::GetPostData(nsIInputStream** aStream) 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() { diff --git a/dom/webbrowserpersist/WebBrowserPersistRemoteDocument.cpp b/dom/webbrowserpersist/WebBrowserPersistRemoteDocument.cpp index c7dcca8619da..cfe831cfb57d 100644 --- a/dom/webbrowserpersist/WebBrowserPersistRemoteDocument.cpp +++ b/dom/webbrowserpersist/WebBrowserPersistRemoteDocument.cpp @@ -9,6 +9,9 @@ #include "WebBrowserPersistResourcesParent.h" #include "WebBrowserPersistSerializeParent.h" #include "mozilla/Unused.h" +#include "mozilla/ipc/BackgroundUtils.h" + +#include "nsIPrincipal.h"
namespace mozilla {
@@ -23,6 +26,8 @@ WebBrowserPersistRemoteDocument , mAttrs(aAttrs) , mPostData(aPostData) { + nsresult rv; + mPrincipal = ipc::PrincipalInfoToPrincipal(mAttrs.principal(), &rv); }
WebBrowserPersistRemoteDocument::~WebBrowserPersistRemoteDocument() @@ -133,6 +138,14 @@ WebBrowserPersistRemoteDocument::GetPostData(nsIInputStream** aStream) }
NS_IMETHODIMP +WebBrowserPersistRemoteDocument::GetPrincipal(nsIPrincipal** aPrincipal) +{ + nsCOMPtr<nsIPrincipal> nodePrincipal = mPrincipal; + nodePrincipal.forget(aPrincipal); + return NS_OK; +} + +NS_IMETHODIMP WebBrowserPersistRemoteDocument::ReadResources(nsIWebBrowserPersistResourceVisitor* aVisitor) { if (!mActor) { diff --git a/dom/webbrowserpersist/WebBrowserPersistRemoteDocument.h b/dom/webbrowserpersist/WebBrowserPersistRemoteDocument.h index 08d435903843..2808ac4b53c0 100644 --- a/dom/webbrowserpersist/WebBrowserPersistRemoteDocument.h +++ b/dom/webbrowserpersist/WebBrowserPersistRemoteDocument.h @@ -13,6 +13,8 @@ #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 @@ -40,6 +42,7 @@ private: WebBrowserPersistDocumentParent* mActor; Attrs mAttrs; nsCOMPtr<nsIInputStream> mPostData; + nsCOMPtr<nsIPrincipal> mPrincipal;
friend class WebBrowserPersistDocumentParent; WebBrowserPersistRemoteDocument(WebBrowserPersistDocumentParent* aActor, diff --git a/dom/webbrowserpersist/nsIWebBrowserPersist.idl b/dom/webbrowserpersist/nsIWebBrowserPersist.idl index 8de84f5e2904..9573a024eebf 100644 --- a/dom/webbrowserpersist/nsIWebBrowserPersist.idl +++ b/dom/webbrowserpersist/nsIWebBrowserPersist.idl @@ -13,6 +13,7 @@ interface nsIWebProgressListener; interface nsIFile; interface nsIChannel; interface nsILoadContext; +interface nsIPrincipal;
/** * Interface for persisting DOM documents and URIs to local or remote storage. @@ -67,12 +68,6 @@ interface nsIWebBrowserPersist : nsICancelable 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. @@ -117,6 +112,8 @@ interface nsIWebBrowserPersist : nsICancelable * @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 An object representing the URI in the cache or * <CODE>nullptr</CODE>. This can be a necko cache key, * an nsIWebPageDescriptor, or the currentDescriptor of an @@ -146,7 +143,8 @@ interface nsIWebBrowserPersist : nsICancelable * * @throws NS_ERROR_INVALID_ARG One or more arguments was invalid. */ - void saveURI(in nsIURI aURI, in nsISupports aCacheKey, + void saveURI(in nsIURI aURI, in nsIPrincipal aTriggeringPrincipal, + in nsISupports aCacheKey, in nsIURI aReferrer, in unsigned long aReferrerPolicy, in nsIInputStream aPostData, in string aExtraHeaders, in nsISupports aFile, @@ -158,7 +156,8 @@ interface nsIWebBrowserPersist : nsICancelable * of intermediate data, etc.) * @see saveURI for all other parameter descriptions */ - void savePrivacyAwareURI(in nsIURI aURI, in nsISupports aCacheKey, + void savePrivacyAwareURI(in nsIURI aURI, + in nsIPrincipal aTriggeringPrincipal, in nsISupports aCacheKey, in nsIURI aReferrer, in unsigned long aReferrerPolicy, in nsIInputStream aPostData, in string aExtraHeaders, in nsISupports aFile, diff --git a/dom/webbrowserpersist/nsIWebBrowserPersistDocument.idl b/dom/webbrowserpersist/nsIWebBrowserPersistDocument.idl index ba5bea8b2906..f7aa146766d0 100644 --- a/dom/webbrowserpersist/nsIWebBrowserPersistDocument.idl +++ b/dom/webbrowserpersist/nsIWebBrowserPersistDocument.idl @@ -9,6 +9,7 @@ interface nsIDOMDocument; interface nsIInputStream; interface nsIOutputStream; +interface nsIPrincipal; interface nsITabParent; interface nsIWebBrowserPersistResourceVisitor; interface nsIWebBrowserPersistWriteCompletion; @@ -61,6 +62,7 @@ interface nsIWebBrowserPersistDocument : nsISupports 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 diff --git a/dom/webbrowserpersist/nsWebBrowserPersist.cpp b/dom/webbrowserpersist/nsWebBrowserPersist.cpp index fd6d9d0d9315..12e6c41c37d8 100644 --- a/dom/webbrowserpersist/nsWebBrowserPersist.cpp +++ b/dom/webbrowserpersist/nsWebBrowserPersist.cpp @@ -84,6 +84,7 @@ struct nsWebBrowserPersist::DocData nsCOMPtr<nsIURI> mBaseURI; nsCOMPtr<nsIWebBrowserPersistDocument> mDocument; nsCOMPtr<nsIURI> mFile; + nsCOMPtr<nsIPrincipal> mPrincipal; nsCString mCharset; };
@@ -414,18 +415,20 @@ NS_IMETHODIMP nsWebBrowserPersist::SetProgressListener( }
NS_IMETHODIMP nsWebBrowserPersist::SaveURI( - nsIURI *aURI, nsISupports *aCacheKey, + nsIURI *aURI, nsIPrincipal *aPrincipal, nsISupports *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, nsISupports *aCacheKey, + nsIURI *aURI, nsIPrincipal *aPrincipal, nsISupports *aCacheKey, nsIURI *aReferrer, uint32_t aReferrerPolicy, nsIInputStream *aPostData, const char *aExtraHeaders, nsISupports *aFile, bool aIsPrivate) @@ -440,8 +443,10 @@ NS_IMETHODIMP nsWebBrowserPersist::SavePrivacyAwareURI(
// 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; }
@@ -600,6 +605,13 @@ nsWebBrowserPersist::SerializeNextFile() }
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()) { @@ -609,8 +621,6 @@ nsWebBrowserPersist::SerializeNextFile() continue; }
- nsresult rv; - // Create a URI from the key. nsCOMPtr<nsIURI> uri; rv = NS_NewURI(getter_AddRefs(uri), iter.Key(), @@ -632,7 +642,7 @@ nsWebBrowserPersist::SerializeNextFile()
// The Referrer Policy doesn't matter here since the referrer is // nullptr. - rv = SaveURIInternal(uri, nullptr, nullptr, + rv = SaveURIInternal(uri, docPrincipal, nullptr, nullptr, mozilla::net::RP_Unset, nullptr, nullptr, fileAsURI, true, mIsPrivate); // If SaveURIInternal fails, then it will have called EndDownload, @@ -1329,7 +1339,8 @@ nsWebBrowserPersist::AppendPathToURI(nsIURI *aURI, const nsAString & aPath, nsCO }
nsresult nsWebBrowserPersist::SaveURIInternal( - nsIURI *aURI, nsISupports *aCacheKey, nsIURI *aReferrer, + nsIURI *aURI, nsIPrincipal* aTriggeringPrincipal, + nsISupports *aCacheKey, nsIURI *aReferrer, uint32_t aReferrerPolicy, nsIInputStream *aPostData, const char *aExtraHeaders, nsIURI *aFile, bool aCalcFileExt, bool aIsPrivate) @@ -1385,7 +1396,7 @@ nsresult nsWebBrowserPersist::SaveURIInternal( 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 @@ -1415,16 +1426,6 @@ nsresult nsWebBrowserPersist::SaveURIInternal( } }
- 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) diff --git a/dom/webbrowserpersist/nsWebBrowserPersist.h b/dom/webbrowserpersist/nsWebBrowserPersist.h index 17b570d783e7..b9a2e3da73b6 100644 --- a/dom/webbrowserpersist/nsWebBrowserPersist.h +++ b/dom/webbrowserpersist/nsWebBrowserPersist.h @@ -57,7 +57,8 @@ public: private: virtual ~nsWebBrowserPersist(); nsresult SaveURIInternal( - nsIURI *aURI, nsISupports *aCacheKey, nsIURI *aReferrer, + nsIURI *aURI, nsIPrincipal* aTriggeringPrincipal, + nsISupports *aCacheKey, nsIURI *aReferrer, uint32_t aReferrerPolicy, nsIInputStream *aPostData, const char *aExtraHeaders, nsIURI *aFile, bool aCalcFileExt, bool aIsPrivate); diff --git a/toolkit/components/browser/nsWebBrowser.cpp b/toolkit/components/browser/nsWebBrowser.cpp index 40ac82210502..cdc3804fee73 100644 --- a/toolkit/components/browser/nsWebBrowser.cpp +++ b/toolkit/components/browser/nsWebBrowser.cpp @@ -998,6 +998,7 @@ nsWebBrowser::SetProgressListener(nsIWebProgressListener* aProgressListener)
NS_IMETHODIMP nsWebBrowser::SaveURI(nsIURI* aURI, + nsIPrincipal* aPrincipal, nsISupports* aCacheKey, nsIURI* aReferrer, uint32_t aReferrerPolicy, @@ -1007,12 +1008,14 @@ nsWebBrowser::SaveURI(nsIURI* aURI, 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, nsISupports* aCacheKey, nsIURI* aReferrer, uint32_t aReferrerPolicy, @@ -1050,8 +1053,9 @@ nsWebBrowser::SavePrivacyAwareURI(nsIURI* aURI, 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; } diff --git a/toolkit/components/downloads/test/unit/head.js b/toolkit/components/downloads/test/unit/head.js index 63bf2ff884a7..6c5c1b095ada 100644 --- a/toolkit/components/downloads/test/unit/head.js +++ b/toolkit/components/downloads/test/unit/head.js @@ -299,7 +299,8 @@ function promiseStartLegacyDownload(aSourceUrl, aOptions) {
// Start the actual download process. persist.savePrivacyAwareURI( - sourceURI, null, referrer, Ci.nsIHttpChannel.REFERRER_POLICY_UNSAFE_URL, + sourceURI, Services.scriptSecurityManager.getSystemPrincipal(), + null, referrer, Ci.nsIHttpChannel.REFERRER_POLICY_UNSAFE_URL, null, null, targetFile, isPrivate); }).catch(do_report_unexpected_exception);
diff --git a/toolkit/components/viewsource/content/viewSourceUtils.js b/toolkit/components/viewsource/content/viewSourceUtils.js index 610667cde3cd..d91f3481f802 100644 --- a/toolkit/components/viewsource/content/viewSourceUtils.js +++ b/toolkit/components/viewsource/content/viewSourceUtils.js @@ -228,7 +228,11 @@ var gViewSourceUtils = { 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); diff --git a/toolkit/content/contentAreaUtils.js b/toolkit/content/contentAreaUtils.js index 48cf448798a5..fa5a7a56c935 100644 --- a/toolkit/content/contentAreaUtils.js +++ b/toolkit/content/contentAreaUtils.js @@ -62,14 +62,15 @@ function forbidCPOW(arg, func, argname) { // - 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 @@ -112,7 +113,7 @@ const nsISupportsCString = Ci.nsISupportsCString; */ function saveImageURL(aURL, aFileName, aFilePickerTitleKey, aShouldBypassCache, aSkipPrompt, aReferrer, aDoc, aContentType, aContentDisp, - aIsContentWindowPrivate) { + aIsContentWindowPrivate, aPrincipal) { forbidCPOW(aURL, "saveImageURL", "aURL"); forbidCPOW(aReferrer, "saveImageURL", "aReferrer");
@@ -156,7 +157,7 @@ function saveImageURL(aURL, aFileName, aFilePickerTitleKey, aShouldBypassCache,
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 @@ -350,11 +351,15 @@ XPCOMUtils.defineConstant(this, "kSaveAsType_Text", kSaveAsType_Text); * 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"); @@ -430,8 +435,17 @@ function internalSave(aURL, aDocument, aDefaultFileName, aContentDisposition, : 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, @@ -482,8 +496,7 @@ function internalPersist(persistArgs) {
// 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 @@ -530,6 +543,7 @@ function internalPersist(persistArgs) { persistArgs.targetContentType, encodingFlags, kWrapColumn); } else { persist.savePrivacyAwareURI(persistArgs.sourceURI, + persistArgs.sourcePrincipal, persistArgs.sourceCacheKey, persistArgs.sourceReferrer, Ci.nsIHttpChannel.REFERRER_POLICY_UNSET, diff --git a/toolkit/content/tests/browser/browser_saveImageURL.js b/toolkit/content/tests/browser/browser_saveImageURL.js index 970aaea43ca9..609db2cb2adc 100644 --- a/toolkit/content/tests/browser/browser_saveImageURL.js +++ b/toolkit/content/tests/browser/browser_saveImageURL.js @@ -36,7 +36,8 @@ add_task(async function preferred_API() { 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); // eslint-disable-next-line mozilla/no-cpows-in-tests let channel = gBrowser.contentDocumentAsCPOW.docShell.currentDocumentChannel; if (channel) { diff --git a/toolkit/mozapps/extensions/LightweightThemeManager.jsm b/toolkit/mozapps/extensions/LightweightThemeManager.jsm index 720f27b48244..d5efecca0de7 100644 --- a/toolkit/mozapps/extensions/LightweightThemeManager.jsm +++ b/toolkit/mozapps/extensions/LightweightThemeManager.jsm @@ -869,7 +869,8 @@ function _persistImage(sourceURL, localFileName, successCallback) {
persist.progressListener = new _persistProgressListener(successCallback);
- persist.saveURI(sourceURI, null, + let sourcePrincipal = Services.scriptSecurityManager.createCodebasePrincipal(sourceURI, {}); + persist.saveURI(sourceURI, sourcePrincipal, null, null, Ci.nsIHttpChannel.REFERRER_POLICY_UNSET, null, null, targetURI, null); }