Bug 1700850 - Part 1: Make temporary background profiles per-installation and unique. r=bytesized,mossop

Differential Revision: https://phabricator.services.mozilla.com/D110310
This commit is contained in:
Nick Alexander 2021-04-05 04:16:37 +00:00
Родитель b27ba19e6d
Коммит ea97f27b52
8 изменённых файлов: 153 добавлений и 33 удалений

Просмотреть файл

@ -13,8 +13,8 @@ namespace mozilla {
NS_IMPL_ISUPPORTS(BackgroundTasks, nsIBackgroundTasks);
nsresult BackgroundTasks::GetOrCreateTemporaryProfileDirectoryImpl(
nsIFile** aFile) {
nsresult BackgroundTasks::CreateTemporaryProfileDirectoryImpl(
const nsCString& aInstallHash, nsIFile** aFile) {
if (mBackgroundTask.isNothing()) {
return NS_ERROR_NOT_AVAILABLE;
}
@ -30,35 +30,21 @@ nsresult BackgroundTasks::GetOrCreateTemporaryProfileDirectoryImpl(
rv = GetSpecialSystemDirectory(OS_TemporaryDirectory, getter_AddRefs(file));
NS_ENSURE_SUCCESS(rv, rv);
// TODO: mix in the application hash and perhaps add some additional
// randomness.
rv = file->AppendNative(
nsPrintfCString("backgroundtask-%s", mBackgroundTask.ref().get()));
// The base path is /tmp/[vendor]BackgroundTask-[pathHash]-[taskName].
rv = file->AppendNative(nsPrintfCString("%sBackgroundTask-%s-%s",
MOZ_APP_VENDOR, aInstallHash.get(),
mBackgroundTask.ref().get()));
NS_ENSURE_SUCCESS(rv, rv);
// Make sure that the profile path exists and it's a directory.
bool exists;
rv = file->Exists(&exists);
// Create a unique profile directory. This can fail if there are too many
// (thousands) of existing directories, which is unlikely to happen.
rv = file->CreateUnique(nsIFile::DIRECTORY_TYPE, 0700);
NS_ENSURE_SUCCESS(rv, rv);
if (!exists) {
rv = file->Create(nsIFile::DIRECTORY_TYPE, 0700);
NS_ENSURE_SUCCESS(rv, rv);
} else {
bool isDir;
rv = file->IsDirectory(&isDir);
NS_ENSURE_SUCCESS(rv, rv);
if (!isDir) {
return NS_ERROR_FILE_DESTINATION_NOT_DIR;
}
}
rv = file->Clone(getter_AddRefs(mProfD));
NS_ENSURE_SUCCESS(rv, rv);
}
nsString path;
file->GetPath(path);
file.forget(aFile);
return NS_OK;
}

Просмотреть файл

@ -99,12 +99,14 @@ class BackgroundTasks final : public nsIBackgroundTasks {
return GetBackgroundTasks().isSome();
}
static nsresult GetOrCreateTemporaryProfileDirectory(nsIFile** aFile) {
static nsresult CreateTemporaryProfileDirectory(const nsCString& aInstallHash,
nsIFile** aFile) {
if (!XRE_IsParentProcess()) {
return NS_ERROR_NOT_AVAILABLE;
}
return GetSingleton()->GetOrCreateTemporaryProfileDirectoryImpl(aFile);
return GetSingleton()->CreateTemporaryProfileDirectoryImpl(aInstallHash,
aFile);
}
static nsresult RunBackgroundTask(nsICommandLine* aCmdLine) {
@ -131,7 +133,8 @@ class BackgroundTasks final : public nsIBackgroundTasks {
Maybe<nsCString> mBackgroundTask;
nsCOMPtr<nsIFile> mProfD;
nsresult GetOrCreateTemporaryProfileDirectoryImpl(nsIFile** aFile);
nsresult CreateTemporaryProfileDirectoryImpl(const nsCString& aInstallHash,
nsIFile** aFile);
virtual ~BackgroundTasks() = default;
};

Просмотреть файл

@ -13,6 +13,9 @@ with Files("docs/**"):
FINAL_LIBRARY = "xul"
for var in ("MOZ_APP_VENDOR",):
DEFINES[var] = '"%s"' % CONFIG[var]
UNIFIED_SOURCES += [
"BackgroundTasks.cpp",
]
@ -51,6 +54,7 @@ TESTING_JS_MODULES.backgroundtasks += [
"tests/BackgroundTask_crash.jsm",
"tests/BackgroundTask_policies.jsm",
"tests/BackgroundTask_shouldprocessupdates.jsm",
"tests/BackgroundTask_unique_profile.jsm",
"tests/BackgroundTask_update_sync_manager.jsm",
"tests/BackgroundTask_wait.jsm",
]

Просмотреть файл

@ -0,0 +1,72 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
* 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/. */
"use strict";
var EXPORTED_SYMBOLS = ["runBackgroundTask"];
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { Subprocess } = ChromeUtils.import(
"resource://gre/modules/Subprocess.jsm"
);
async function runBackgroundTask(commandLine) {
let sentinel = commandLine.getArgument(0);
let count =
commandLine.length > 1
? Number.parseInt(commandLine.getArgument(1), 10)
: 1;
let main = await ChromeUtils.requestProcInfo();
let info = [main.pid, Services.dirsvc.get("ProfD", Ci.nsIFile).path];
// `dump` prints to the console without formatting.
dump(`${count}: ${sentinel}${JSON.stringify(info)}${sentinel}\n`);
// Maybe launch a child.
if (count <= 1) {
return 0;
}
let command = Services.dirsvc.get("XREExeF", Ci.nsIFile).path;
let args = [
"--backgroundtask",
"unique_profile",
sentinel,
(count - 1).toString(),
];
// We must assemble all of the string fragments from stdout.
let stdoutChunks = [];
let proc = await Subprocess.call({
command,
arguments: args,
stderr: "stdout",
// Don't inherit this task's profile path.
environmentAppend: true,
environment: { XRE_PROFILE_PATH: null },
}).then(p => {
p.stdin.close();
const dumpPipe = async pipe => {
let data = await pipe.readString();
while (data) {
data = await pipe.readString();
stdoutChunks.push(data);
}
};
dumpPipe(p.stdout);
return p;
});
let { exitCode } = await proc.wait();
let stdout = stdoutChunks.join("");
for (let line of stdout.split(/\r\n|\r|\n/).slice(0, -1)) {
dump(`${count}> ${line}\n`);
}
return exitCode;
}

Просмотреть файл

@ -32,7 +32,7 @@ function getFirefoxExecutableFile() {
async function do_backgroundtask(
task,
options = { extraArgs: [], extraEnv: {} }
options = { extraArgs: [], extraEnv: {}, stdoutLines: null }
) {
options = Object.assign({}, options);
options.extraArgs = options.extraArgs || [];
@ -48,7 +48,7 @@ async function do_backgroundtask(
.QueryInterface(Ci.nsIResProtocolHandler);
let uri = protocolHandler.getSubstitution("testing-common");
Assert.ok(uri, "resource://testing-common is not substituted");
Assert.ok(!!uri, "resource://testing-common is not substituted");
// The equivalent of _TESTING_MODULES_DIR in xpcshell.
options.extraEnv.XPCSHELL_TESTING_MODULES_URI = uri.spec;
@ -59,6 +59,8 @@ async function do_backgroundtask(
options.extraEnv
)}`
);
// We must assemble all of the string fragments from stdout.
let stdoutChunks = [];
let proc = await Subprocess.call({
command,
arguments: args,
@ -70,9 +72,7 @@ async function do_backgroundtask(
const dumpPipe = async pipe => {
let data = await pipe.readString();
while (data) {
for (let line of data.split(/\r\n|\r|\n/).slice(0, -1)) {
dump("> " + line + "\n");
}
stdoutChunks.push(data);
data = await pipe.readString();
}
};
@ -82,6 +82,15 @@ async function do_backgroundtask(
});
let { exitCode } = await proc.wait();
let stdout = stdoutChunks.join("");
for (let line of stdout.split(/\r\n|\r|\n/).slice(0, -1)) {
dump("> " + line + "\n");
if (options.stdoutLines !== null && options.stdoutLines !== undefined) {
options.stdoutLines.push(line);
}
}
return exitCode;
}

Просмотреть файл

@ -0,0 +1,42 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
* vim: sw=4 ts=4 sts=4 et
* 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/. */
// Run a background task which itself waits for a launched background task, which itself waits for a
// launched background task, etc. This is an easy way to ensure that tasks are running concurrently
// without requiring concurrency primitives.
add_task(async function test_backgroundtask_unique_profile() {
let sentinel = Cc["@mozilla.org/uuid-generator;1"]
.getService(Ci.nsIUUIDGenerator)
.generateUUID()
.toString();
sentinel = sentinel.substring(1, sentinel.length - 1);
let count = 3;
let stdoutLines = [];
let exitCode = await do_backgroundtask("unique_profile", {
extraArgs: [sentinel, count.toString()],
stdoutLines,
});
Assert.equal(0, exitCode);
let infos = [];
let profiles = new Set();
for (let line of stdoutLines) {
if (line.includes(sentinel)) {
let info = JSON.parse(line.split(sentinel)[1]);
infos.push(info);
profiles.add(info[1]);
}
}
Assert.equal(
count,
profiles.size,
`Found ${count} distinct profiles: ${JSON.stringify(
Array.from(profiles)
)} in: ${JSON.stringify(infos)}`
);
});

Просмотреть файл

@ -14,6 +14,7 @@ support-files =
[test_backgroundtask_policies.js]
[test_backgroundtask_shouldprocessupdates.js]
[test_backgroundtask_specific_pref.js]
[test_backgroundtask_unique_profile.js]
[test_backgroundtask_update_sync_manager.js]
[test_backgroundtasksutils.js]
[test_manifest_with_backgroundtask.js]

Просмотреть файл

@ -4528,9 +4528,12 @@ int XREMain::XRE_mainStartup(bool* aExitFlag) {
if (BackgroundTasks::IsBackgroundTaskMode()) {
// Allow tests to specify profile path via the environment.
if (!EnvHasValue("XRE_PROFILE_PATH")) {
nsString installHash;
mDirProvider.GetInstallHash(installHash);
nsCOMPtr<nsIFile> file;
nsresult rv = BackgroundTasks::GetOrCreateTemporaryProfileDirectory(
getter_AddRefs(file));
nsresult rv = BackgroundTasks::CreateTemporaryProfileDirectory(
NS_LossyConvertUTF16toASCII(installHash), getter_AddRefs(file));
if (NS_WARN_IF(NS_FAILED(rv))) {
return 1;
}