Bug 1772726 - Port osfile.jsm usage to IOUtils in services/sync r=markh

Differential Revision: https://phabricator.services.mozilla.com/D148444
This commit is contained in:
Barret Rennie 2022-08-03 17:05:21 +00:00
parent 3f144b8c3d
commit ec47642ebf
9 changed files with 86 additions and 76 deletions

View File

@ -42,14 +42,9 @@ ChromeUtils.defineESModuleGetters(lazy, {
PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs",
});
XPCOMUtils.defineLazyModuleGetters(lazy, {
OS: "resource://gre/modules/osfile.jsm",
});
function ensureDirectory(path) {
let basename = lazy.OS.Path.dirname(path);
return lazy.OS.File.makeDir(basename, {
from: lazy.OS.Constants.Path.profileDir,
return IOUtils.makeDirectory(PathUtils.parent(path), {
createAncestors: true,
});
}

View File

@ -39,7 +39,6 @@ ChromeUtils.defineESModuleGetters(lazy, {
XPCOMUtils.defineLazyModuleGetters(lazy, {
Observers: "resource://services-common/observers.js",
OS: "resource://gre/modules/osfile.jsm",
Resource: "resource://services-sync/resource.js",
});
@ -725,13 +724,13 @@ BookmarksStore.prototype = {
},
async _openMirror() {
let mirrorPath = lazy.OS.Path.join(
lazy.OS.Constants.Path.profileDir,
let mirrorPath = PathUtils.join(
PathUtils.profileDir,
"weave",
"bookmarks.sqlite"
);
await lazy.OS.File.makeDir(lazy.OS.Path.dirname(mirrorPath), {
from: lazy.OS.Constants.Path.profileDir,
await IOUtils.makeDirectory(PathUtils.parent(mirrorPath), {
createAncestors: true,
});
return lazy.SyncedBookmarksMirror.open({

View File

@ -33,7 +33,6 @@ XPCOMUtils.defineLazyModuleGetters(lazy, {
FxAccounts: "resource://gre/modules/FxAccounts.jsm",
ObjectUtils: "resource://gre/modules/ObjectUtils.jsm",
Observers: "resource://services-common/observers.js",
OS: "resource://gre/modules/osfile.jsm",
Resource: "resource://services-sync/resource.js",
Status: "resource://services-sync/status.js",
Svc: "resource://services-sync/util.js",
@ -198,23 +197,36 @@ class ErrorSanitizer {
"28": this.E_NO_SPACE_ON_DEVICE, // ENOSPC
};
static DOMErrorSubstitutions = {
NotFoundError: this.E_NO_FILE_OR_DIR,
NotAllowedError: this.E_PERMISSION_DENIED,
};
static reWinError = /^(?<head>Win error (?<errno>\d+))(?<detail>.*) \(.*\r?\n?\)$/m;
static reUnixError = /^(?<head>Unix error (?<errno>\d+))(?<detail>.*) \(.*\)$/;
static #cleanOSErrorMessage(error) {
let match = this.reWinError.exec(error);
static #cleanOSErrorMessage(message, error = undefined) {
if (DOMException.isInstance(error)) {
const sub = this.DOMErrorSubstitutions[error.name];
message = message.replaceAll("\\", "/");
if (sub) {
return `${sub} ${message}`;
}
}
let match = this.reWinError.exec(message);
if (match) {
let head =
this.WindowsErrorSubstitutions[match.groups.errno] || match.groups.head;
return head + match.groups.detail.replaceAll("\\", "/");
}
match = this.reUnixError.exec(error);
match = this.reUnixError.exec(message);
if (match) {
let head =
this.UnixErrorSubstitutions[match.groups.errno] || match.groups.head;
return head + match.groups.detail;
}
return error;
return message;
}
// A regex we can use to replace the profile dir in error messages. We use a
@ -222,30 +234,36 @@ class ErrorSanitizer {
// This escaping function is from:
// https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions
static reProfileDir = new RegExp(
lazy.OS.Constants.Path.profileDir.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"),
PathUtils.profileDir.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"),
"gi"
);
// The "public" entry-point.
static cleanErrorMessage(error) {
/**
* Clean an error message, removing PII and normalizing OS-specific messages.
*
* @param {string} message The error message
* @param {Error?} error The error class instance, if any.
*/
static cleanErrorMessage(message, error = undefined) {
// There's a chance the profiledir is in the error string which is PII we
// want to avoid including in the ping.
error = error.replace(this.reProfileDir, "[profileDir]");
message = message.replace(this.reProfileDir, "[profileDir]");
// MSG_INVALID_URL from /dom/bindings/Errors.msg -- no way to access this
// directly from JS.
if (error.endsWith("is not a valid URL.")) {
error = "<URL> is not a valid URL.";
if (message.endsWith("is not a valid URL.")) {
message = "<URL> is not a valid URL.";
}
// Try to filter things that look somewhat like a URL (in that they contain a
// colon in the middle of non-whitespace), in case anything else is including
// these in error messages. Note that JSON.stringified stuff comes through
// here, so we explicitly ignore double-quotes as well.
error = error.replace(/[^\s"]+:[^\s"]+/g, "<URL>");
message = message.replace(/[^\s"]+:[^\s"]+/g, "<URL>");
// Anywhere that's normalized the guid in errors we can easily filter
// to make it easier to aggregate these types of errors
error = error.replace(/<guid: ([^>]+)>/g, "<GUID>");
return this.#cleanOSErrorMessage(error);
message = message.replace(/<guid: ([^>]+)>/g, "<GUID>");
return this.#cleanOSErrorMessage(message, error);
}
}
@ -1161,6 +1179,13 @@ class SyncTelemetryImpl {
return { name: "autherror", from: error.source };
}
if (DOMException.isInstance(error)) {
return {
name: "unexpectederror",
error: ErrorSanitizer.cleanErrorMessage(error.message, error),
};
}
let httpCode =
error.status || (error.response && error.response.status) || error.code;

View File

@ -28,7 +28,6 @@ const { XPCOMUtils } = ChromeUtils.importESModule(
"resource://gre/modules/XPCOMUtils.sys.mjs"
);
const lazy = {};
ChromeUtils.defineModuleGetter(lazy, "OS", "resource://gre/modules/osfile.jsm");
const FxAccountsCommon = ChromeUtils.import(
"resource://gre/modules/FxAccountsCommon.js"
);
@ -343,7 +342,7 @@ var Utils = {
let [fileName] = args.splice(-1);
return PathUtils.join(
Services.dirsvc.get("ProfD", Ci.nsIFile).path,
PathUtils.profileDir,
"weave",
...args,
`${fileName}.json`
@ -375,9 +374,9 @@ var Utils = {
}
try {
return await CommonUtils.readJSON(path);
return await IOUtils.readJSON(path);
} catch (e) {
if (!(e instanceof lazy.OS.File.Error && e.becauseNoSuchFile)) {
if (!DOMException.isInstance(e) || e.name !== "NotFoundError") {
if (that._log) {
that._log.debug("Failed to load json", e);
}
@ -402,16 +401,14 @@ var Utils = {
* Promise resolved when the write has been performed.
*/
async jsonSave(filePath, that, obj) {
let path = lazy.OS.Path.join(
lazy.OS.Constants.Path.profileDir,
let path = PathUtils.join(
PathUtils.profileDir,
"weave",
...(filePath + ".json").split("/")
);
let dir = lazy.OS.Path.dirname(path);
let dir = PathUtils.parent(path);
await lazy.OS.File.makeDir(dir, {
from: lazy.OS.Constants.Path.profileDir,
});
await IOUtils.makeDirectory(dir, { createAncestors: true });
if (that._log) {
that._log.trace("Saving json to disk: " + path);
@ -419,7 +416,7 @@ var Utils = {
let json = typeof obj == "function" ? obj.call(that) : obj;
return CommonUtils.writeJSON(json, path);
return IOUtils.writeJSON(path, json);
},
/**
@ -474,20 +471,20 @@ var Utils = {
* Object to use for logging
*/
jsonMove(aFrom, aTo, that) {
let pathFrom = lazy.OS.Path.join(
lazy.OS.Constants.Path.profileDir,
let pathFrom = PathUtils.join(
PathUtils.profileDir,
"weave",
...(aFrom + ".json").split("/")
);
let pathTo = lazy.OS.Path.join(
lazy.OS.Constants.Path.profileDir,
let pathTo = PathUtils.join(
PathUtils.profileDir,
"weave",
...(aTo + ".json").split("/")
);
if (that._log) {
that._log.trace("Moving " + pathFrom + " to " + pathTo);
}
return lazy.OS.File.move(pathFrom, pathTo, { noOverwrite: true });
return IOUtils.move(pathFrom, pathTo, { noOverwrite: true });
},
/**
@ -502,15 +499,15 @@ var Utils = {
* Object to use for logging
*/
jsonRemove(filePath, that) {
let path = lazy.OS.Path.join(
lazy.OS.Constants.Path.profileDir,
let path = PathUtils.join(
PathUtils.profileDir,
"weave",
...(filePath + ".json").split("/")
);
if (that._log) {
that._log.trace("Deleting " + path);
}
return lazy.OS.File.remove(path, { ignoreAbsent: true });
return IOUtils.remove(path, { ignoreAbsent: true });
},
/**

View File

@ -7,7 +7,6 @@ const { BookmarkHTMLUtils } = ChromeUtils.importESModule(
const { BookmarkJSONUtils } = ChromeUtils.importESModule(
"resource://gre/modules/BookmarkJSONUtils.sys.mjs"
);
const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
const {
Bookmark,
BookmarkFolder,
@ -472,8 +471,8 @@ async function test_restoreOrImport(engine, { replace }) {
});
_(`Get Firefox!: ${bmk1.guid}`);
let backupFilePath = OS.Path.join(
OS.Constants.Path.tmpDir,
let backupFilePath = PathUtils.join(
PathUtils.tempDir,
`t_b_e_${Date.now()}.json`
);

View File

@ -1,7 +1,6 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
const { PromiseUtils } = ChromeUtils.import(
"resource://gre/modules/PromiseUtils.jsm"
);
@ -125,11 +124,9 @@ add_task(async function test_invalidChangedIDs() {
let tracker = engine._tracker;
await tracker._beforeSave();
await OS.File.writeAtomic(
tracker._storage.path,
new TextEncoder().encode("5"),
{ tmpPath: tracker._storage.path + ".tmp" }
);
await IOUtils.writeUTF8(tracker._storage.path, "5", {
tmpPath: tracker._storage.path + ".tmp",
});
ok(!tracker._storage.dataReady);
const changes = await tracker.getChangedIDs();

View File

@ -1,7 +1,6 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
const { Service } = ChromeUtils.import("resource://services-sync/service.js");
async function makeSteamEngine() {
@ -116,7 +115,6 @@ add_task(async function test_lastSync() {
add_task(async function test_toFetch() {
_("SyncEngine.toFetch corresponds to file on disk");
await SyncTestingInfrastructure(server);
const filename = "weave/toFetch/steam.json";
await testSteamEngineStorage({
toFetch: guidSetOfSize(3),
@ -153,9 +151,13 @@ add_task(async function test_toFetch() {
await testSteamEngineStorage({
toFetch: guidSetOfSize(2),
async beforeCheck() {
let toFetchPath = OS.Path.join(OS.Constants.Path.profileDir, filename);
let bytes = new TextEncoder().encode(JSON.stringify(this.toFetch));
await OS.File.writeAtomic(toFetchPath, bytes, {
let toFetchPath = PathUtils.join(
PathUtils.profileDir,
"weave",
"toFetch",
"steam.json"
);
await IOUtils.writeJSON(toFetchPath, this.toFetch, {
tmpPath: toFetchPath + ".tmp",
});
},
@ -169,7 +171,6 @@ add_task(async function test_toFetch() {
add_task(async function test_previousFailed() {
_("SyncEngine.previousFailed corresponds to file on disk");
await SyncTestingInfrastructure(server);
const filename = "weave/failed/steam.json";
await testSteamEngineStorage({
previousFailed: guidSetOfSize(3),
@ -206,12 +207,13 @@ add_task(async function test_previousFailed() {
await testSteamEngineStorage({
previousFailed: guidSetOfSize(2),
async beforeCheck() {
let previousFailedPath = OS.Path.join(
OS.Constants.Path.profileDir,
filename
let previousFailedPath = PathUtils.join(
PathUtils.profileDir,
"weave",
"failed",
"steam.json"
);
let bytes = new TextEncoder().encode(JSON.stringify(this.previousFailed));
await OS.File.writeAtomic(previousFailedPath, bytes, {
await IOUtils.writeJSON(previousFailedPath, this.previousFailed, {
tmpPath: previousFailedPath + ".tmp",
});
},

View File

@ -7,7 +7,6 @@ const { Resource } = ChromeUtils.import("resource://services-sync/resource.js");
const { RotaryEngine } = ChromeUtils.import(
"resource://testing-common/services/sync/rotaryengine.js"
);
const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
const { getFxAccountsSingleton } = ChromeUtils.import(
"resource://gre/modules/FxAccounts.jsm"
);
@ -546,7 +545,7 @@ add_task(async function test_engine_fail_ioerror() {
equal(failureReason.name, "unexpectederror");
// ensure the profile dir in the exception message has been stripped.
ok(
!failureReason.error.includes(OS.Constants.Path.profileDir),
!failureReason.error.includes(PathUtils.profileDir),
failureReason.error
);
ok(failureReason.error.includes("[profileDir]"), failureReason.error);
@ -671,13 +670,10 @@ add_task(async function test_clean_real_os_error() {
engine.enabled = true;
let server = await serverForFoo(engine);
await SyncTestingInfrastructure(server);
let path =
Services.appinfo.OS == "WINNT"
? "no\\such\\path.json"
: "no/such/path.json";
let path = PathUtils.join(PathUtils.profileDir, "no", "such", "path.json");
try {
await CommonUtils.writeJSON({}, path);
throw new Error("should fail to write the file");
await IOUtils.readJSON(path);
throw new Error("should fail to read the file");
} catch (ex) {
engine._errToThrow = ex;
}
@ -692,7 +688,7 @@ add_task(async function test_clean_real_os_error() {
equal(failureReason.name, "unexpectederror");
equal(
failureReason.error,
"OS error [File/Path not found] during operation open on file no/such/path.json"
"OS error [File/Path not found] Could not open the file at [profileDir]/no/such/path.json"
);
});
} finally {

View File

@ -50,7 +50,6 @@ XPCOMUtils.defineLazyModuleGetters(lazy, {
JsonSchema: "resource://gre/modules/JsonSchema.jsm",
Log: "resource://gre/modules/Log.jsm",
Logger: "resource://tps/logger.jsm",
OS: "resource://gre/modules/osfile.jsm",
SessionStore: "resource:///modules/sessionstore/SessionStore.jsm",
Svc: "resource://services-sync/util.js",
SyncTelemetry: "resource://services-sync/telemetry.js",
@ -1009,7 +1008,8 @@ var TPS = {
_getFileRelativeToSourceRoot(testFileURL, relativePath) {
let file = lazy.fileProtocolHandler.getFileFromURLSpec(testFileURL);
let root = file.parent.parent.parent.parent.parent; // <root>/services/sync/tests/tps/test_foo.js // <root>/services/sync/tests/tps // <root>/services/sync/tests // <root>/services/sync // <root>/services // <root>
root.appendRelativePath(lazy.OS.Path.normalize(relativePath));
root.appendRelativePath(relativePath);
root.normalize();
return root;
},