зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1893269 - Build out basic infrastructure for backup management UI. r=backup-reviewers,settings-reviewers,firefox-desktop-core-reviewers ,Gijs,fluent-reviewers,kpatenio
Differential Revision: https://phabricator.services.mozilla.com/D209161
This commit is contained in:
Родитель
ab7f68990a
Коммит
26bf957cd0
|
@ -425,6 +425,20 @@ let JSWINDOWACTORS = {
|
|||
enablePreference: "browser.aboutwelcome.enabled",
|
||||
},
|
||||
|
||||
BackupUI: {
|
||||
parent: {
|
||||
esModuleURI: "resource:///actors/BackupUIParent.sys.mjs",
|
||||
},
|
||||
child: {
|
||||
esModuleURI: "resource:///actors/BackupUIChild.sys.mjs",
|
||||
events: {
|
||||
"BackupUI:InitWidget": { wantUntrusted: true },
|
||||
},
|
||||
},
|
||||
matches: ["about:preferences*", "about:settings*"],
|
||||
enablePreference: "browser.backup.preferences.ui.enabled",
|
||||
},
|
||||
|
||||
BlockedSite: {
|
||||
parent: {
|
||||
esModuleURI: "resource:///actors/BlockedSiteParent.sys.mjs",
|
||||
|
|
|
@ -34,7 +34,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
|||
* backups. It also does most of the heavy lifting for the restoration of a
|
||||
* profile backup.
|
||||
*/
|
||||
export class BackupService {
|
||||
export class BackupService extends EventTarget {
|
||||
/**
|
||||
* The BackupService singleton instance.
|
||||
*
|
||||
|
@ -50,12 +50,47 @@ export class BackupService {
|
|||
*/
|
||||
#resources = new Map();
|
||||
|
||||
/**
|
||||
* Set to true if a backup is currently in progress. Causes stateUpdate()
|
||||
* to be called.
|
||||
*
|
||||
* @see BackupService.stateUpdate()
|
||||
* @param {boolean} val
|
||||
* True if a backup is in progress.
|
||||
*/
|
||||
set #backupInProgress(val) {
|
||||
if (this.#_state.backupInProgress != val) {
|
||||
this.#_state.backupInProgress = val;
|
||||
this.stateUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* True if a backup is currently in progress.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
#backupInProgress = false;
|
||||
get #backupInProgress() {
|
||||
return this.#_state.backupInProgress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches an event to let listeners know that the BackupService state
|
||||
* object has been updated.
|
||||
*/
|
||||
stateUpdate() {
|
||||
this.dispatchEvent(new CustomEvent("BackupService:StateUpdate"));
|
||||
}
|
||||
|
||||
/**
|
||||
* An object holding the current state of the BackupService instance, for
|
||||
* the purposes of representing it in the user interface. Ideally, this would
|
||||
* be named #state instead of #_state, but sphinx-js seems to be fairly
|
||||
* unhappy with that coupled with the ``state`` getter.
|
||||
*
|
||||
* @type {object}
|
||||
*/
|
||||
#_state = { backupInProgress: false };
|
||||
|
||||
/**
|
||||
* A Promise that will resolve once the postRecovery steps are done. It will
|
||||
|
@ -190,6 +225,7 @@ export class BackupService {
|
|||
* @param {object} [backupResources=DefaultBackupResources] - Object containing BackupResource classes to associate with this service.
|
||||
*/
|
||||
constructor(backupResources = DefaultBackupResources) {
|
||||
super();
|
||||
lazy.logConsole.debug("Instantiated");
|
||||
|
||||
for (const resourceName in backupResources) {
|
||||
|
@ -214,6 +250,17 @@ export class BackupService {
|
|||
return this.#postRecoveryPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a state object describing the state of the BackupService for the
|
||||
* purposes of representing it in the user interface. The returned state
|
||||
* object is immutable.
|
||||
*
|
||||
* @type {object}
|
||||
*/
|
||||
get state() {
|
||||
return Object.freeze(structuredClone(this.#_state));
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {object} CreateBackupResult
|
||||
* @property {string} stagingPath
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/* 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/. */
|
||||
|
||||
/**
|
||||
* A JSWindowActor that is responsible for marshalling information between
|
||||
* the BackupService singleton and any registered UI widgets that need to
|
||||
* represent data from that service. Any UI widgets that want to receive
|
||||
* state updates from BackupService should emit a BackupUI:InitWidget
|
||||
* event in a document that this actor pair is registered for.
|
||||
*/
|
||||
export class BackupUIChild extends JSWindowActorChild {
|
||||
#inittedWidgets = new WeakSet();
|
||||
|
||||
/**
|
||||
* Handles BackupUI:InitWidget custom events fired by widgets that want to
|
||||
* register with BackupUIChild. Firing this event sends a message to the
|
||||
* parent to request the BackupService state which will result in a
|
||||
* `backupServiceState` property of the widget to be set when that state is
|
||||
* received. Subsequent state updates will also cause that state property to
|
||||
* be set.
|
||||
*
|
||||
* @param {Event} event
|
||||
* The BackupUI:InitWidget custom event that the widget fired.
|
||||
*/
|
||||
handleEvent(event) {
|
||||
if (event.type == "BackupUI:InitWidget") {
|
||||
this.#inittedWidgets.add(event.target);
|
||||
this.sendAsyncMessage("RequestState");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles messages sent by BackupUIParent.
|
||||
*
|
||||
* @param {ReceiveMessageArgument} message
|
||||
* The message received from the BackupUIParent.
|
||||
*/
|
||||
receiveMessage(message) {
|
||||
if (message.name == "StateUpdate") {
|
||||
let widgets = ChromeUtils.nondeterministicGetWeakSetKeys(
|
||||
this.#inittedWidgets
|
||||
);
|
||||
for (let widget of widgets) {
|
||||
if (widget.isConnected) {
|
||||
// Note: we might need to switch to using Cu.cloneInto here in the
|
||||
// event that these widgets are embedded in a non-parent-process
|
||||
// context, like in an onboarding card.
|
||||
widget.backupServiceState = message.data.state;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/* 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/. */
|
||||
|
||||
const lazy = {};
|
||||
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
BackupService: "resource:///modules/backup/BackupService.sys.mjs",
|
||||
});
|
||||
|
||||
/**
|
||||
* A JSWindowActor that is responsible for marshalling information between
|
||||
* the BackupService singleton and any registered UI widgets that need to
|
||||
* represent data from that service.
|
||||
*/
|
||||
export class BackupUIParent extends JSWindowActorParent {
|
||||
/**
|
||||
* A reference to the BackupService singleton instance.
|
||||
*
|
||||
* @type {BackupService}
|
||||
*/
|
||||
#bs;
|
||||
|
||||
/**
|
||||
* Create a BackupUIParent instance. If a BackupUIParent is instantiated
|
||||
* before BrowserGlue has a chance to initialize the BackupService, this
|
||||
* constructor will cause it to initialize first.
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
// We use init() rather than get(), since it's possible to load
|
||||
// about:preferences before the service has had a chance to init itself
|
||||
// via BrowserGlue.
|
||||
this.#bs = lazy.BackupService.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the BackupUIParent/BackupUIChild pair have been connected.
|
||||
*/
|
||||
actorCreated() {
|
||||
this.#bs.addEventListener("BackupService:StateUpdate", this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the BackupUIParent/BackupUIChild pair have been disconnected.
|
||||
*/
|
||||
didDestroy() {
|
||||
this.#bs.removeEventListener("BackupService:StateUpdate", this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles events fired by the BackupService.
|
||||
*
|
||||
* @param {Event} event
|
||||
* The event that the BackupService emitted.
|
||||
*/
|
||||
handleEvent(event) {
|
||||
if (event.type == "BackupService:StateUpdate") {
|
||||
this.sendState();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles messages sent by BackupUIChild.
|
||||
*
|
||||
* @param {ReceiveMessageArgument} message
|
||||
* The message received from the BackupUIChild.
|
||||
*/
|
||||
receiveMessage(message) {
|
||||
if (message.name == "RequestState") {
|
||||
this.sendState();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the StateUpdate message to the BackupUIChild, along with the most
|
||||
* recent state object from BackupService.
|
||||
*/
|
||||
sendState() {
|
||||
this.sendAsyncMessage("StateUpdate", { state: this.#bs.state });
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/* 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/. */
|
||||
|
||||
import { html } from "chrome://global/content/vendor/lit.all.mjs";
|
||||
import { MozLitElement } from "chrome://global/content/lit-utils.mjs";
|
||||
|
||||
/**
|
||||
* The widget for managing the BackupService that is embedded within the main
|
||||
* document of about:settings / about:preferences.
|
||||
*/
|
||||
export default class BackupSettings extends MozLitElement {
|
||||
static properties = {
|
||||
backupServiceState: { type: Object },
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a BackupSettings instance and sets the initial default
|
||||
* state.
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
this.backupServiceState = {
|
||||
backupInProgress: false,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches the BackupUI:InitWidget custom event upon being attached to the
|
||||
* DOM, which registers with BackupUIChild for BackupService state updates.
|
||||
*/
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("BackupUI:InitWidget", { bubbles: true })
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`<div>
|
||||
Backup in progress:
|
||||
${this.backupServiceState.backupInProgress ? "Yes" : "No"}
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("backup-settings", BackupSettings);
|
|
@ -0,0 +1,32 @@
|
|||
/* 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/. */
|
||||
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import { html } from "lit.all.mjs";
|
||||
// eslint-disable-next-line import/no-unassigned-import
|
||||
import "./backup-settings.mjs";
|
||||
|
||||
export default {
|
||||
title: "Domain-specific UI Widgets/Backup/Backup Settings",
|
||||
component: "backup-settings",
|
||||
argTypes: {},
|
||||
};
|
||||
|
||||
const Template = ({ backupServiceState }) => html`
|
||||
<backup-settings .backupServiceState=${backupServiceState}></backup-settings>
|
||||
`;
|
||||
|
||||
export const BackingUpNotInProgress = Template.bind({});
|
||||
BackingUpNotInProgress.args = {
|
||||
backupServiceState: {
|
||||
backupInProgress: false,
|
||||
},
|
||||
};
|
||||
|
||||
export const BackingUpInProgress = Template.bind({});
|
||||
BackingUpInProgress.args = {
|
||||
backupServiceState: {
|
||||
backupInProgress: true,
|
||||
},
|
||||
};
|
|
@ -0,0 +1,22 @@
|
|||
==========================
|
||||
Backup UI Actors Reference
|
||||
==========================
|
||||
|
||||
The ``BackupUIParent`` and ``BackupUIChild`` actors allow UI widgets to access
|
||||
the current state of the ``BackupService`` and to subscribe to state updates.
|
||||
|
||||
UI widgets that want to subscribe to state updates must ensure that they are
|
||||
running in a process and on a page that the ``BackupUIParent/BackupUIChild``
|
||||
actor pair are registered for, and then fire a ``BackupUI::InitWidget`` event.
|
||||
|
||||
It is expected that these UI widgets will respond to having their
|
||||
``backupServiceState`` property set.
|
||||
|
||||
.. js:autoclass:: BackupUIParent
|
||||
:members:
|
||||
:private-members:
|
||||
|
||||
.. js:autoclass:: BackupUIChild
|
||||
.. js::autoattribute:: BackupUIChild#inittedWidgets
|
||||
:members:
|
||||
:private-members:
|
|
@ -12,3 +12,4 @@ into a single file that can be easily restored from.
|
|||
|
||||
backup-service
|
||||
backup-resources
|
||||
backup-ui-actors
|
||||
|
|
|
@ -7,4 +7,5 @@ browser.jar:
|
|||
content/browser/backup/debug.html (content/debug.html)
|
||||
content/browser/backup/debug.js (content/debug.js)
|
||||
#endif
|
||||
content/browser/backup/BackupManifest.1.schema.json (content/BackupManifest.1.schema.json)
|
||||
content/browser/backup/BackupManifest.1.schema.json (content/BackupManifest.1.schema.json)
|
||||
content/browser/backup/backup-settings.mjs (content/backup-settings.mjs)
|
||||
|
|
|
@ -14,6 +14,12 @@ SPHINX_TREES["docs"] = "docs"
|
|||
XPCSHELL_TESTS_MANIFESTS += ["tests/xpcshell/xpcshell.toml"]
|
||||
MARIONETTE_MANIFESTS += ["tests/marionette/manifest.toml"]
|
||||
BROWSER_CHROME_MANIFESTS += ["tests/browser/browser.toml"]
|
||||
MOCHITEST_CHROME_MANIFESTS += ["tests/chrome/chrome.toml"]
|
||||
|
||||
FINAL_TARGET_FILES.actors += [
|
||||
"actors/BackupUIChild.sys.mjs",
|
||||
"actors/BackupUIParent.sys.mjs",
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES.backup += [
|
||||
"BackupResources.sys.mjs",
|
||||
|
|
|
@ -4,4 +4,4 @@ prefs = [
|
|||
"browser.backup.preferences.ui.enabled=true",
|
||||
]
|
||||
|
||||
["browser_preferences.js"]
|
||||
["browser_settings.js"]
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
[DEFAULT]
|
||||
skip-if = ["os == 'android'"]
|
||||
|
||||
["test_backup_settings.html"]
|
|
@ -0,0 +1,43 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Tests for the BackupSettings component</title>
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script
|
||||
src="chrome://browser/content/backup/backup-settings.mjs"
|
||||
type="module"
|
||||
></script>
|
||||
<link rel="stylesheet" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
|
||||
<script>
|
||||
|
||||
const { BrowserTestUtils } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/BrowserTestUtils.sys.mjs"
|
||||
);
|
||||
|
||||
/**
|
||||
* Tests that adding a backup-settings element to the DOM causes it to
|
||||
* fire a BackupUI:InitWidget event.
|
||||
*/
|
||||
add_task(async function test_initWidget() {
|
||||
let settings = document.createElement("backup-settings");
|
||||
let content = document.getElementById("content");
|
||||
|
||||
let sawInitWidget = BrowserTestUtils.waitForEvent(content, "BackupUI:InitWidget");
|
||||
content.appendChild(settings);
|
||||
await sawInitWidget;
|
||||
ok(true, "Saw BackupUI:InitWidget");
|
||||
|
||||
settings.remove();
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
<backup-settings id="test-backup-settings"></backup-settings>
|
||||
</div>
|
||||
<pre id="test"></pre>
|
||||
</body>
|
||||
</html>
|
|
@ -84,6 +84,9 @@
|
|||
<groupbox id="dataBackupGroup" data-category="paneGeneral" hidden="true"
|
||||
data-hidden-from-search="true">
|
||||
<label><html:h2 data-l10n-id="settings-data-backup-header"/></label>
|
||||
<hbox flex="1">
|
||||
<html:backup-settings />
|
||||
</hbox>
|
||||
</groupbox>
|
||||
|
||||
<!-- Tab preferences -->
|
||||
|
|
|
@ -68,7 +68,7 @@
|
|||
<link rel="localization" href="browser/translations.ftl"/>
|
||||
<link rel="localization" href="preview/translations.ftl"/>
|
||||
<link rel="localization" href="preview/enUS-searchFeatures.ftl"/>
|
||||
<link rel="localization" href="preview/backupPreferences.ftl"/>
|
||||
<link rel="localization" href="preview/backupSettings.ftl"/>
|
||||
<link rel="localization" href="security/certificates/certManager.ftl"/>
|
||||
<link rel="localization" href="security/certificates/deviceManager.ftl"/>
|
||||
<link rel="localization" href="toolkit/updates/history.ftl"/>
|
||||
|
@ -87,6 +87,7 @@
|
|||
<script type="module" src="chrome://global/content/elements/moz-label.mjs"/>
|
||||
<script type="module" src="chrome://global/content/elements/moz-card.mjs"></script>
|
||||
<script type="module" src="chrome://global/content/elements/moz-button.mjs"></script>
|
||||
<script type="module" src="chrome://browser/content/backup/backup-settings.mjs"></script>
|
||||
</head>
|
||||
|
||||
<html:body xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
|
|
|
@ -23,6 +23,8 @@ module.exports = {
|
|||
`${projectRoot}/toolkit/content/widgets/**/*.stories.@(js|jsx|mjs|ts|tsx|md)`,
|
||||
// about:logins components stories
|
||||
`${projectRoot}/browser/components/aboutlogins/content/components/**/*.stories.mjs`,
|
||||
// Backup components stories
|
||||
`${projectRoot}/browser/components/backup/content/**/*.stories.mjs`,
|
||||
// Reader View components stories
|
||||
`${projectRoot}/toolkit/components/reader/**/*.stories.mjs`,
|
||||
// Everything else
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
preview/translations.ftl (../locales-preview/translations.ftl)
|
||||
browser (%browser/**/*.ftl)
|
||||
preview/profiles.ftl (../components/profiles/content/profiles.ftl)
|
||||
preview/backupPreferences.ftl (../locales-preview/backupPreferences.ftl)
|
||||
preview/backupSettings.ftl (../locales-preview/backupSettings.ftl)
|
||||
|
||||
@AB_CD@.jar:
|
||||
% locale browser @AB_CD@ %locale/browser/
|
||||
|
|
|
@ -56,6 +56,7 @@ extensions = [
|
|||
# excluded from valid-jsdoc in the top-level .eslintrc.js.
|
||||
js_source_path = [
|
||||
"../browser/components/backup",
|
||||
"../browser/components/backup/actors",
|
||||
"../browser/components/backup/resources",
|
||||
"../browser/components/extensions",
|
||||
"../browser/components/migration",
|
||||
|
|
Загрузка…
Ссылка в новой задаче