mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-27 14:52:16 +00:00
Bug 1884995 - Add BackupResource abstract class. r=mconley,backup-reviewers
Adds a `BackupResource` abstract class to be extended by more specific resource handlers and a `BackupResources` module which resources can be registered with. The BackupResource base includes helpers to get the size of files and directories. All registed resources will be provided to the `BackupService` constructor for it instantiate them. Differential Revision: https://phabricator.services.mozilla.com/D203795
This commit is contained in:
parent
b3eba1dc18
commit
ae5f491626
15
browser/components/backup/BackupResources.sys.mjs
Normal file
15
browser/components/backup/BackupResources.sys.mjs
Normal file
@ -0,0 +1,15 @@
|
||||
/* 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 https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// Remove this import after BackupResource is referenced elsewhere.
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import { BackupResource } from "resource:///modules/backup/BackupResource.sys.mjs";
|
||||
|
||||
/**
|
||||
* Classes exported here are registered as a resource that can be
|
||||
* backed up and restored in the BackupService.
|
||||
*
|
||||
* They must extend the BackupResource base class.
|
||||
*/
|
||||
export {};
|
@ -2,6 +2,8 @@
|
||||
* 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/. */
|
||||
|
||||
import * as BackupResources from "resource:///modules/backup/BackupResources.sys.mjs";
|
||||
|
||||
const lazy = {};
|
||||
|
||||
ChromeUtils.defineLazyGetter(lazy, "logConsole", function () {
|
||||
@ -27,6 +29,13 @@ export class BackupService {
|
||||
*/
|
||||
static #instance = null;
|
||||
|
||||
/**
|
||||
* Map of instantiated BackupResource classes.
|
||||
*
|
||||
* @type {Map<string, BackupResource>}
|
||||
*/
|
||||
#resources = new Map();
|
||||
|
||||
/**
|
||||
* Returns a reference to a BackupService singleton. If this is the first time
|
||||
* that this getter is accessed, this causes the BackupService singleton to be
|
||||
@ -39,14 +48,24 @@ export class BackupService {
|
||||
if (this.#instance) {
|
||||
return this.#instance;
|
||||
}
|
||||
this.#instance = new BackupService();
|
||||
this.#instance = new BackupService(BackupResources);
|
||||
this.#instance.takeMeasurements();
|
||||
|
||||
return this.#instance;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
/**
|
||||
* Create a BackupService instance.
|
||||
*
|
||||
* @param {object} [backupResources=BackupResources] - Object containing BackupResource classes to associate with this service.
|
||||
*/
|
||||
constructor(backupResources = BackupResources) {
|
||||
lazy.logConsole.debug("Instantiated");
|
||||
|
||||
for (const resourceName in backupResources) {
|
||||
let resource = BackupResources[resourceName];
|
||||
this.#resources.set(resource.key, resource);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -75,5 +94,10 @@ export class BackupService {
|
||||
// And then record the value in kilobytes, since that's what everything
|
||||
// else is going to be measured in.
|
||||
Glean.browserBackup.profDDiskSpace.set(profDDiskSpaceMB * BYTES_IN_KB);
|
||||
|
||||
// Measure the size of each file we are going to backup.
|
||||
for (let resourceClass of this.#resources.values()) {
|
||||
await new resourceClass().measure(PathUtils.profileDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,5 +12,7 @@ SPHINX_TREES["docs"] = "docs"
|
||||
XPCSHELL_TESTS_MANIFESTS += ["tests/xpcshell/xpcshell.toml"]
|
||||
|
||||
EXTRA_JS_MODULES.backup += [
|
||||
"BackupResources.sys.mjs",
|
||||
"BackupService.sys.mjs",
|
||||
"resources/BackupResource.sys.mjs",
|
||||
]
|
||||
|
109
browser/components/backup/resources/BackupResource.sys.mjs
Normal file
109
browser/components/backup/resources/BackupResource.sys.mjs
Normal file
@ -0,0 +1,109 @@
|
||||
/* 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 https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// Convert from bytes to kilobytes (not kibibytes).
|
||||
const BYTES_IN_KB = 1000;
|
||||
|
||||
/**
|
||||
* An abstract class representing a set of data within a user profile
|
||||
* that can be persisted to a separate backup archive file, and restored
|
||||
* to a new user profile from that backup archive file.
|
||||
*/
|
||||
export class BackupResource {
|
||||
/**
|
||||
* This must be overridden to return a simple string identifier for the
|
||||
* resource, for example "places" or "extensions". This key is used as
|
||||
* a unique identifier for the resource.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
static get key() {
|
||||
throw new Error("BackupResource::key needs to be overridden.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of a file.
|
||||
*
|
||||
* @param {string} filePath - path to a file.
|
||||
* @returns {Promise<number|null>} - the size of the file in kilobytes, or null if the
|
||||
* file does not exist, the path is a directory or the size is unknown.
|
||||
*/
|
||||
static async getFileSize(filePath) {
|
||||
if (!(await IOUtils.exists(filePath))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let { size } = await IOUtils.stat(filePath);
|
||||
|
||||
if (size < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let sizeInKb = Math.ceil(size / BYTES_IN_KB);
|
||||
// Make the measurement fuzzier by rounding to the nearest 10kb.
|
||||
let nearestTenthKb = Math.round(sizeInKb / 10) * 10;
|
||||
|
||||
return Math.max(nearestTenthKb, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the total size of a directory.
|
||||
*
|
||||
* @param {string} directoryPath - path to a directory.
|
||||
* @returns {Promise<number|null>} - the size of all descendants of the directory in kilobytes, or null if the
|
||||
* directory does not exist, the path is not a directory or the size is unknown.
|
||||
*/
|
||||
static async getDirectorySize(directoryPath) {
|
||||
if (!(await IOUtils.exists(directoryPath))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let { type } = await IOUtils.stat(directoryPath);
|
||||
|
||||
if (type != "directory") {
|
||||
return null;
|
||||
}
|
||||
|
||||
let children = await IOUtils.getChildren(directoryPath, {
|
||||
ignoreAbsent: true,
|
||||
});
|
||||
|
||||
let size = 0;
|
||||
for (const childFilePath of children) {
|
||||
let { size: childSize, type: childType } = await IOUtils.stat(
|
||||
childFilePath
|
||||
);
|
||||
|
||||
if (childSize >= 0) {
|
||||
let sizeInKb = Math.ceil(childSize / BYTES_IN_KB);
|
||||
// Make the measurement fuzzier by rounding to the nearest 10kb.
|
||||
let nearestTenthKb = Math.round(sizeInKb / 10) * 10;
|
||||
size += Math.max(nearestTenthKb, 1);
|
||||
}
|
||||
|
||||
if (childType == "directory") {
|
||||
let childDirectorySize = await this.getDirectorySize(childFilePath);
|
||||
if (Number.isInteger(childDirectorySize)) {
|
||||
size += childDirectorySize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
constructor() {}
|
||||
|
||||
/**
|
||||
* This must be overridden to record telemetry on the size of any
|
||||
* data associated with this BackupResource.
|
||||
*
|
||||
* @param {string} profilePath - path to a profile directory.
|
||||
* @returns {Promise<undefined>}
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
async measure(profilePath) {
|
||||
throw new Error("BackupResource::measure needs to be overridden.");
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
{
|
||||
"chrome://browser/content/browser.xhtml": {
|
||||
"PersonalToolbar": { "collapsed": "false" },
|
||||
"main-window": {
|
||||
"screenX": "852",
|
||||
"screenY": "125",
|
||||
"width": "1484",
|
||||
"height": "1256",
|
||||
"sizemode": "normal"
|
||||
},
|
||||
"sidebar-box": {
|
||||
"sidebarcommand": "viewBookmarksSidebar",
|
||||
"width": "323",
|
||||
"style": "width: 323px;"
|
||||
},
|
||||
"sidebar-title": { "value": "Bookmarks" }
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { BackupResource } = ChromeUtils.importESModule(
|
||||
"resource:///modules/backup/BackupResource.sys.mjs"
|
||||
);
|
||||
|
||||
const EXPECTED_KILOBYTES_FOR_XULSTORE = 1;
|
||||
|
||||
add_setup(() => {
|
||||
do_get_profile();
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests that BackupService.getFileSize will get the size of a file in kilobytes.
|
||||
*/
|
||||
add_task(async function test_getFileSize() {
|
||||
let file = do_get_file("data/test_xulstore.json");
|
||||
|
||||
let testFilePath = PathUtils.join(PathUtils.profileDir, "test_xulstore.json");
|
||||
|
||||
await IOUtils.copy(file.path, PathUtils.profileDir);
|
||||
|
||||
let size = await BackupResource.getFileSize(testFilePath);
|
||||
|
||||
Assert.equal(
|
||||
size,
|
||||
EXPECTED_KILOBYTES_FOR_XULSTORE,
|
||||
"Size of the test_xulstore.json is rounded up to the nearest kilobyte."
|
||||
);
|
||||
|
||||
await IOUtils.remove(testFilePath);
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests that BackupService.getFileSize will get the total size of all the files in a directory and it's children in kilobytes.
|
||||
*/
|
||||
add_task(async function test_getDirectorySize() {
|
||||
let file = do_get_file("data/test_xulstore.json");
|
||||
|
||||
// Create a test directory with the test json file in it.
|
||||
let testDir = PathUtils.join(PathUtils.profileDir, "testDir");
|
||||
await IOUtils.makeDirectory(testDir);
|
||||
await IOUtils.copy(file.path, testDir);
|
||||
|
||||
// Create another test directory inside of that one.
|
||||
let nestedTestDir = PathUtils.join(testDir, "testDir");
|
||||
await IOUtils.makeDirectory(nestedTestDir);
|
||||
await IOUtils.copy(file.path, nestedTestDir);
|
||||
|
||||
let size = await BackupResource.getDirectorySize(testDir);
|
||||
|
||||
Assert.equal(
|
||||
size,
|
||||
EXPECTED_KILOBYTES_FOR_XULSTORE * 2,
|
||||
`Total size of the directory is rounded up to the nearest kilobyte
|
||||
and is equal to twice the size of the test_xulstore.json file`
|
||||
);
|
||||
|
||||
await IOUtils.remove(testDir, { recursive: true });
|
||||
});
|
@ -2,4 +2,7 @@
|
||||
firefox-appdir = "browser"
|
||||
skip-if = ["os == 'android'"]
|
||||
|
||||
["test_BrowserResource.js"]
|
||||
support-files = ["data/test_xulstore.json"]
|
||||
|
||||
["test_measurements.js"]
|
||||
|
Loading…
Reference in New Issue
Block a user