зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1745361 - Create unique directories and files with IOUtils r=Gijs,webdriver-reviewers,extension-reviewers,robwu
PathUtils::CreateUniquePath was doing main thread IO, so we're moving it to IOUtils to use its event loop to do the IO. Additionally, we're adding IOUtils::CreateUniqueDirectory which is the same as ::CreateUniqueFile, but for directories. Differential Revision: https://phabricator.services.mozilla.com/D133841
This commit is contained in:
Родитель
9460e18a04
Коммит
32cf1f6e27
|
@ -214,6 +214,28 @@ namespace IOUtils {
|
|||
*/
|
||||
Promise<boolean> exists(DOMString path);
|
||||
|
||||
/**
|
||||
* Create a file with a unique name and return its path.
|
||||
*
|
||||
* @param parent An absolute path to the directory where the file is to be
|
||||
* created.
|
||||
* @param prefix A prefix for the filename.
|
||||
*
|
||||
* @return A promise that resolves to a unique filename.
|
||||
*/
|
||||
Promise<DOMString> createUniqueFile(DOMString parent, DOMString prefix, optional unsigned long permissions = 0644);
|
||||
|
||||
/**
|
||||
* Create a directory with a unique name and return its path.
|
||||
*
|
||||
* @param parent An absolute path to the directory where the file is to be
|
||||
* created.
|
||||
* @param prefix A prefix for the directory name.
|
||||
*
|
||||
* @return A promise that resolves to a unique directory name.
|
||||
*/
|
||||
Promise<DOMString> createUniqueDirectory(DOMString parent, DOMString prefix, optional unsigned long permissions = 0755);
|
||||
|
||||
#if defined(XP_WIN)
|
||||
/**
|
||||
* Return the Windows-specific file attributes of the file at the given path.
|
||||
|
|
|
@ -50,14 +50,6 @@ namespace PathUtils {
|
|||
[Throws]
|
||||
DOMString joinRelative(DOMString base, DOMString relativePath);
|
||||
|
||||
/**
|
||||
* Creates a unique path from the provided path.
|
||||
*
|
||||
* @param path An absolute path.
|
||||
*/
|
||||
[Throws]
|
||||
DOMString createUniquePath(DOMString path);
|
||||
|
||||
/**
|
||||
* Creates an adjusted path using a path whose length is already close
|
||||
* to MAX_PATH. For windows only.
|
||||
|
|
|
@ -712,6 +712,49 @@ already_AddRefed<Promise> IOUtils::Exists(GlobalObject& aGlobal,
|
|||
});
|
||||
}
|
||||
|
||||
/* static */
|
||||
already_AddRefed<Promise> IOUtils::CreateUniqueFile(
|
||||
GlobalObject& aGlobal, const nsAString& aParent, const nsAString& aPrefix,
|
||||
const uint32_t aPermissions) {
|
||||
return CreateUnique(aGlobal, aParent, aPrefix, nsIFile::NORMAL_FILE_TYPE,
|
||||
aPermissions);
|
||||
}
|
||||
|
||||
/* static */
|
||||
already_AddRefed<Promise> IOUtils::CreateUniqueDirectory(
|
||||
GlobalObject& aGlobal, const nsAString& aParent, const nsAString& aPrefix,
|
||||
const uint32_t aPermissions) {
|
||||
return CreateUnique(aGlobal, aParent, aPrefix, nsIFile::DIRECTORY_TYPE,
|
||||
aPermissions);
|
||||
}
|
||||
|
||||
/* static */
|
||||
already_AddRefed<Promise> IOUtils::CreateUnique(GlobalObject& aGlobal,
|
||||
const nsAString& aParent,
|
||||
const nsAString& aPrefix,
|
||||
const uint32_t aFileType,
|
||||
const uint32_t aPermissions) {
|
||||
return WithPromiseAndState(aGlobal, [&](Promise* promise, auto& state) {
|
||||
nsCOMPtr<nsIFile> file = new nsLocalFile();
|
||||
REJECT_IF_INIT_PATH_FAILED(file, aParent, promise);
|
||||
|
||||
if (nsresult rv = file->Append(aPrefix); NS_FAILED(rv)) {
|
||||
RejectJSPromise(
|
||||
promise,
|
||||
IOError(rv).WithMessage("Could not append prefix `%s' to parent `%s'",
|
||||
NS_ConvertUTF16toUTF8(aPrefix).get(),
|
||||
file->HumanReadablePath().get()));
|
||||
return;
|
||||
}
|
||||
|
||||
DispatchAndResolve<nsString>(
|
||||
state->mEventQueue, promise,
|
||||
[file = std::move(file), aPermissions, aFileType]() {
|
||||
return CreateUniqueSync(file, aFileType, aPermissions);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#if defined(XP_WIN)
|
||||
|
||||
/* static */
|
||||
|
@ -1588,6 +1631,22 @@ Result<bool, IOUtils::IOError> IOUtils::ExistsSync(nsIFile* aFile) {
|
|||
return exists;
|
||||
}
|
||||
|
||||
/* static */
|
||||
Result<nsString, IOUtils::IOError> IOUtils::CreateUniqueSync(
|
||||
nsIFile* aFile, const uint32_t aFileType, const uint32_t aPermissions) {
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
if (nsresult rv = aFile->CreateUnique(aFileType, aPermissions);
|
||||
NS_FAILED(rv)) {
|
||||
return Err(IOError(rv).WithMessage("Could not create unique path"));
|
||||
}
|
||||
|
||||
nsString path;
|
||||
MOZ_ALWAYS_SUCCEEDS(aFile->GetPath(path));
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
#if defined(XP_WIN)
|
||||
|
||||
Result<uint32_t, IOUtils::IOError> IOUtils::GetWindowsAttributesSync(
|
||||
|
|
|
@ -124,6 +124,24 @@ class IOUtils final {
|
|||
static already_AddRefed<Promise> Exists(GlobalObject& aGlobal,
|
||||
const nsAString& aPath);
|
||||
|
||||
static already_AddRefed<Promise> CreateUniqueFile(
|
||||
GlobalObject& aGlobal, const nsAString& aParent, const nsAString& aPrefix,
|
||||
const uint32_t aPermissions);
|
||||
static already_AddRefed<Promise> CreateUniqueDirectory(
|
||||
GlobalObject& aGlobal, const nsAString& aParent, const nsAString& aPrefix,
|
||||
const uint32_t aPermissions);
|
||||
|
||||
private:
|
||||
/**
|
||||
* A helper method for CreateUniqueFile and CreateUniqueDirectory.
|
||||
*/
|
||||
static already_AddRefed<Promise> CreateUnique(GlobalObject& aGlobal,
|
||||
const nsAString& aParent,
|
||||
const nsAString& aPrefix,
|
||||
const uint32_t aFileType,
|
||||
const uint32_t aPermissions);
|
||||
|
||||
public:
|
||||
#if defined(XP_WIN)
|
||||
static already_AddRefed<Promise> GetWindowsAttributes(GlobalObject& aGlobal,
|
||||
const nsAString& aPath);
|
||||
|
@ -395,6 +413,19 @@ class IOUtils final {
|
|||
*/
|
||||
static Result<bool, IOError> ExistsSync(nsIFile* aFile);
|
||||
|
||||
/**
|
||||
* Create a file or directory with a unique path.
|
||||
*
|
||||
* @param aFile The location of the file or directory (including prefix)
|
||||
* @param aFileType One of |nsIFile::NORMAL_FILE_TYPE| or
|
||||
* |nsIFile::DIRECTORY_TYPE|.
|
||||
* @param aperms The permissions to create the file or directory with.
|
||||
*
|
||||
* @return A unique path.
|
||||
*/
|
||||
static Result<nsString, IOError> CreateUniqueSync(
|
||||
nsIFile* aFile, const uint32_t aFileType, const uint32_t aPermissions);
|
||||
|
||||
#if defined(XP_WIN)
|
||||
/**
|
||||
* Return the Windows-specific attributes of the file.
|
||||
|
|
|
@ -37,7 +37,6 @@ static constexpr auto ERROR_EMPTY_PATH =
|
|||
static constexpr auto ERROR_INITIALIZE_PATH = "Could not initialize path"_ns;
|
||||
static constexpr auto ERROR_GET_PARENT = "Could not get parent path"_ns;
|
||||
static constexpr auto ERROR_JOIN = "Could not append to path"_ns;
|
||||
static constexpr auto ERROR_CREATE_UNIQUE = "Could not create unique path"_ns;
|
||||
|
||||
static void ThrowError(ErrorResult& aErr, const nsresult aResult,
|
||||
const nsCString& aMessage) {
|
||||
|
@ -217,28 +216,6 @@ void PathUtils::JoinRelative(const GlobalObject&, const nsAString& aBasePath,
|
|||
MOZ_ALWAYS_SUCCEEDS(path->GetPath(aResult));
|
||||
}
|
||||
|
||||
void PathUtils::CreateUniquePath(const GlobalObject&, const nsAString& aPath,
|
||||
nsString& aResult, ErrorResult& aErr) {
|
||||
if (aPath.IsEmpty()) {
|
||||
aErr.ThrowNotAllowedError(ERROR_EMPTY_PATH);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFile> path = new nsLocalFile();
|
||||
if (nsresult rv = InitFileWithPath(path, aPath); NS_FAILED(rv)) {
|
||||
ThrowError(aErr, rv, ERROR_INITIALIZE_PATH);
|
||||
return;
|
||||
}
|
||||
|
||||
if (nsresult rv = path->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
|
||||
NS_FAILED(rv)) {
|
||||
ThrowError(aErr, rv, ERROR_CREATE_UNIQUE);
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_SUCCEEDS(path->GetPath(aResult));
|
||||
}
|
||||
|
||||
void PathUtils::ToExtendedWindowsPath(const GlobalObject&,
|
||||
const nsAString& aPath, nsString& aResult,
|
||||
ErrorResult& aErr) {
|
||||
|
|
|
@ -50,9 +50,6 @@ class PathUtils final {
|
|||
const nsAString& aRelativePath, nsString& aResult,
|
||||
ErrorResult& aErr);
|
||||
|
||||
static void CreateUniquePath(const GlobalObject&, const nsAString& aPath,
|
||||
nsString& aResult, ErrorResult& aErr);
|
||||
|
||||
static void ToExtendedWindowsPath(const GlobalObject&, const nsAString& aPath,
|
||||
nsString& aResult, ErrorResult& aErr);
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ support-files =
|
|||
|
||||
[test_ioutils.html]
|
||||
[test_ioutils_copy_move.html]
|
||||
[test_ioutils_create_unique.html]
|
||||
[test_ioutils_dir_iteration.html]
|
||||
[test_ioutils_mac_xattr.html]
|
||||
skip-if = (os != "mac")
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test the IOUtils file I/O API</title>
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
<script src="file_ioutils_test_fixtures.js"></script>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
const { Assert } = ChromeUtils.import("resource://testing-common/Assert.jsm");
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
function octalFormat(n) {
|
||||
let s = n.toString(8);
|
||||
while (s.length < 3) {
|
||||
s = `0${s}`;
|
||||
}
|
||||
return `0o${s}`;
|
||||
}
|
||||
|
||||
async function check(method, path, prefix, type, perms) {
|
||||
const filename = PathUtils.filename(path);
|
||||
|
||||
ok(filename.startsWith(prefix), `IOUtils.${method} uses the prefix`);
|
||||
ok(await IOUtils.exists(path), `IOUtils.${method} creates a file`);
|
||||
|
||||
const stat = await IOUtils.stat(path);
|
||||
is(stat.type, type, `IOUtils.${method} creates a "${type}" file`);
|
||||
|
||||
is(
|
||||
octalFormat(stat.permissions),
|
||||
octalFormat(perms),
|
||||
`IOUtils.${method} creates a file with the correct permissions`
|
||||
);
|
||||
}
|
||||
|
||||
add_task(async function test_createUnique() {
|
||||
const tempDir = PathUtils.join(
|
||||
await PathUtils.getTempDir(),
|
||||
"test_createUnique.tmp.d"
|
||||
);
|
||||
|
||||
const filesToChmod = [];
|
||||
|
||||
SimpleTest.registerCleanupFunction(async function test_createUnique_cleanup() {
|
||||
for (const file of filesToChmod) {
|
||||
if (await IOUtils.exists(file)) {
|
||||
await IOUtils.setPermissions(file, 0o666);
|
||||
}
|
||||
}
|
||||
|
||||
await IOUtils.remove(tempDir, { recursive: true });
|
||||
});
|
||||
|
||||
const isWindows = Services.appinfo.OS === "WINNT";
|
||||
|
||||
info("Creating a unique directory")
|
||||
const dir = await IOUtils.createUniqueDirectory(tempDir, "unique-dir", 0o600);
|
||||
await check("createUniqueDirectory", dir, "unique-dir", "directory", isWindows ? 0o666 : 0o600);
|
||||
|
||||
info("Creating a unique directory with the same prefix")
|
||||
const dir2 = await IOUtils.createUniqueDirectory(tempDir, "unique-dir", 0o700);
|
||||
await check("createUniqueDirectory", dir2, "unique-dir", "directory", isWindows ? 0o666 : 0o700);
|
||||
ok(dir !== dir2, "IOUtils.createUniqueDirectory creates unique paths");
|
||||
|
||||
info("Creating a unique file");
|
||||
const file = await IOUtils.createUniqueFile(tempDir, "unique-file", 0o641);
|
||||
await check("createUniqueFile", file, "unique-file", "regular", isWindows ? 0o666 : 0o641);
|
||||
|
||||
info("Creating a unique file with the same prefix");
|
||||
const file2 = await IOUtils.createUniqueFile(tempDir, "unique-file", 0o400);
|
||||
filesToChmod.push(file2);
|
||||
await check("createUniqueFile", file2, "unique-file", "regular", isWindows ? 0o444 : 0o400);
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -423,21 +423,6 @@
|
|||
);
|
||||
}
|
||||
});
|
||||
|
||||
add_task(async function test_createUniquePath() {
|
||||
let path = PathUtils.join(await PathUtils.getProfileDir(), ".test");
|
||||
|
||||
let firstPath = PathUtils.createUniquePath(path);
|
||||
let secondPath = PathUtils.createUniquePath(path);
|
||||
SimpleTest.registerCleanupFunction(async () => {
|
||||
await IOUtils.remove(firstPath);
|
||||
await IOUtils.remove(secondPath);
|
||||
});
|
||||
|
||||
isnot(firstPath, secondPath, "Create unique paths returns different paths");
|
||||
is(PathUtils.filename(firstPath), ".test", "PathUtils.createUniquePath() matches filename for first path");
|
||||
is(PathUtils.filename(secondPath), "-1.test", "PathUtils.createUniquePath() has unique filename for second path");
|
||||
});
|
||||
</script>
|
||||
|
||||
<body>
|
||||
|
|
|
@ -198,8 +198,11 @@ function parseRanges(ranges) {
|
|||
print.printToFile = async function(browser, settings) {
|
||||
// Create a unique filename for the temporary PDF file
|
||||
const tempDir = await PathUtils.getTempDir();
|
||||
const basePath = PathUtils.join(tempDir, "marionette.pdf");
|
||||
const filePath = await PathUtils.createUniquePath(basePath);
|
||||
const filePath = await IOUtils.createUniqueFile(
|
||||
tempDir,
|
||||
"marionette.pdf",
|
||||
0o600
|
||||
);
|
||||
|
||||
let printSettings = getPrintSettings(settings, filePath);
|
||||
|
||||
|
|
|
@ -1225,8 +1225,11 @@ ContentPrefService2.prototype = {
|
|||
if (aConn) {
|
||||
await aConn.close();
|
||||
}
|
||||
let backupFile = aPath + ".corrupt";
|
||||
let uniquePath = PathUtils.createUniquePath(backupFile);
|
||||
let uniquePath = await IOUtils.createUniqueFile(
|
||||
PathUtils.parent(aPath),
|
||||
PathUtils.filename(aPath) + ".corrupt",
|
||||
0o600
|
||||
);
|
||||
await IOUtils.copy(aPath, uniquePath);
|
||||
await IOUtils.remove(aPath);
|
||||
this.log("Completed DB cleanup.");
|
||||
|
|
|
@ -245,8 +245,10 @@ JSONFile.prototype = {
|
|||
|
||||
// Move the original file to a backup location, ignoring errors.
|
||||
try {
|
||||
let uniquePath = await PathUtils.createUniquePath(
|
||||
this.path + ".corrupt"
|
||||
let uniquePath = await IOUtils.createUniqueFile(
|
||||
PathUtils.parent(this.path),
|
||||
PathUtils.filename(this.path) + ".corrupt",
|
||||
0o600
|
||||
);
|
||||
await IOUtils.move(this.path, uniquePath);
|
||||
this._recordTelemetry("load", cleansedBasename, "invalid_json");
|
||||
|
|
|
@ -4050,8 +4050,7 @@ var XPIInstall = {
|
|||
// ownsTempFile so that we will cleanup later (see installAddonSet).
|
||||
try {
|
||||
let tmpDir = Services.dirsvc.get("TmpD", Ci.nsIFile).path;
|
||||
let path = PathUtils.join(tmpDir, "tmpaddon");
|
||||
let uniquePath = PathUtils.createUniquePath(path);
|
||||
let uniquePath = await IOUtils.createUniqueFile(tmpDir, "tmpaddon");
|
||||
await IOUtils.copy(sourceAddon._sourceBundle.path, uniquePath);
|
||||
// Make sure to update file modification times so this is detected
|
||||
// as a new add-on.
|
||||
|
|
Загрузка…
Ссылка в новой задаче