[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [tor-browser/tor-browser-60.4.0esr-8.5-1] Bug 1469916, r=ckerschb, jkt
commit 23c6e68b1208d20ea342d3d4693f1ea7c1050661
Author: Gijs Kruitbosch <gijskruitbosch@xxxxxxxxx>
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);
}
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits