commit c93446545b68a2c6def7832da661547d441bb3fc
parent 0303dcecad6003c2b682c16f7a9002a0f19c361d
Author: agoloman <agoloman@mozilla.com>
Date: Thu, 2 Oct 2025 20:35:13 +0300
Revert "Bug 1992112 - IPProtectionService should not use UIState to check the sign-in flag, r=ip-protection-reviewers,rking" for causing bc failures @browser_startup.js.
This reverts commit f218375f33921491641c604b1b28454df6434da1.
Diffstat:
9 files changed, 126 insertions(+), 115 deletions(-)
diff --git a/browser/components/ipprotection/IPPSignInWatcher.sys.mjs b/browser/components/ipprotection/IPPSignInWatcher.sys.mjs
@@ -1,73 +0,0 @@
-/* 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, {
- IPProtectionService:
- "resource:///modules/ipprotection/IPProtectionService.sys.mjs",
- UIState: "resource://services-sync/UIState.sys.mjs",
-});
-
-ChromeUtils.defineLazyGetter(lazy, "fxAccounts", () => {
- return ChromeUtils.importESModule(
- "resource://gre/modules/FxAccounts.sys.mjs"
- ).getFxAccountsSingleton();
-});
-
-/**
- * This class monitors the Sign-In state and triggers the update of the service
- * if needed.
- */
-class IPPSignInWatcherSingleton {
- #signedIn = false;
-
- get isSignedIn() {
- return this.#signedIn;
- }
-
- setSignedIn(signedIn) {
- this.#signedIn = signedIn;
- }
-
- /**
- * Adds an observer for the FxA sign-in state.
- */
- async init() {
- this.fxaObserver = {
- QueryInterface: ChromeUtils.generateQI([
- Ci.nsIObserver,
- Ci.nsISupportsWeakReference,
- ]),
-
- observe() {
- let { status } = lazy.UIState.get();
- let signedIn = status == lazy.UIState.STATUS_SIGNED_IN;
- if (signedIn !== IPPSignInWatcher.isSignedIn) {
- IPPSignInWatcher.setSignedIn(signedIn);
- lazy.IPProtectionService.updateState();
- }
- },
- };
-
- Services.obs.addObserver(this.fxaObserver, lazy.UIState.ON_UPDATE);
-
- const userData = await lazy.fxAccounts.getSignedInUser();
- this.#signedIn = (userData && userData.verified) || false;
- }
-
- /**
- * Removes the FxA sign-in state observer
- */
- uninit() {
- if (this.fxaObserver) {
- Services.obs.removeObserver(this.fxaObserver, lazy.UIState.ON_UPDATE);
- this.fxaObserver = null;
- }
- }
-}
-
-const IPPSignInWatcher = new IPPSignInWatcherSingleton();
-
-export { IPPSignInWatcher };
diff --git a/browser/components/ipprotection/IPProtectionHelpers.sys.mjs b/browser/components/ipprotection/IPProtectionHelpers.sys.mjs
@@ -15,10 +15,9 @@ ChromeUtils.defineESModuleGetters(lazy, {
IPProtectionStates:
"resource:///modules/ipprotection/IPProtectionService.sys.mjs",
NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
+ UIState: "resource://services-sync/UIState.sys.mjs",
});
-import { IPPSignInWatcher } from "resource:///modules/ipprotection/IPPSignInWatcher.sys.mjs";
-
const VPN_ADDON_ID = "vpn@mozilla.com";
/**
@@ -126,6 +125,44 @@ class VPNAddonHelper {
}
/**
+ * This class monitors the Sign-In state and triggers the update of the service
+ * if needed.
+ */
+class SignInStateHelper {
+ /**
+ * Adds an observer for the FxA sign-in state.
+ */
+ init() {
+ this.fxaObserver = {
+ QueryInterface: ChromeUtils.generateQI([
+ Ci.nsIObserver,
+ Ci.nsISupportsWeakReference,
+ ]),
+
+ observe() {
+ let { status } = lazy.UIState.get();
+ let signedIn = status == lazy.UIState.STATUS_SIGNED_IN;
+ if (signedIn !== lazy.IPProtectionService.signedIn) {
+ lazy.IPProtectionService.updateState();
+ }
+ },
+ };
+
+ Services.obs.addObserver(this.fxaObserver, lazy.UIState.ON_UPDATE);
+ }
+
+ /**
+ * Removes the FxA sign-in state observer
+ */
+ uninit() {
+ if (this.fxaObserver) {
+ Services.obs.removeObserver(this.fxaObserver, lazy.UIState.ON_UPDATE);
+ this.fxaObserver = null;
+ }
+ }
+}
+
+/**
* This class monitors the eligibility flag from Nimbus
*/
class EligibilityHelper {
@@ -145,9 +182,9 @@ class EligibilityHelper {
const IPPHelpers = [
new AccountResetHelper(),
new EligibilityHelper(),
+ new SignInStateHelper(),
new VPNAddonHelper(),
new UIHelper(),
- IPPSignInWatcher,
];
export { IPPHelpers };
diff --git a/browser/components/ipprotection/IPProtectionPanel.sys.mjs b/browser/components/ipprotection/IPProtectionPanel.sys.mjs
@@ -12,7 +12,6 @@ ChromeUtils.defineESModuleGetters(lazy, {
IPProtectionStates:
"resource:///modules/ipprotection/IPProtectionService.sys.mjs",
IPProtection: "resource:///modules/ipprotection/IPProtection.sys.mjs",
- IPPSignInWatcher: "resource:///modules/ipprotection/IPPSignInWatcher.sys.mjs",
});
import {
@@ -110,11 +109,14 @@ export class IPProtectionPanel {
constructor(window, variant = "") {
this.handleEvent = this.#handleEvent.bind(this);
- let { activatedAt: protectionEnabledSince, hasUpgraded } =
- lazy.IPProtectionService;
+ let {
+ isSignedIn,
+ activatedAt: protectionEnabledSince,
+ hasUpgraded,
+ } = lazy.IPProtectionService;
this.state = {
- isSignedOut: !lazy.IPPSignInWatcher.isSignedIn,
+ isSignedOut: !isSignedIn,
isProtectionEnabled: !!protectionEnabledSince,
protectionEnabledSince,
location: {
@@ -357,6 +359,7 @@ export class IPProtectionPanel {
} else if (event.type == "IPProtectionService:StateChanged") {
let {
state,
+ isSignedIn,
activatedAt: protectionEnabledSince,
hasUpgraded,
} = lazy.IPProtectionService;
@@ -365,7 +368,7 @@ export class IPProtectionPanel {
lazy.IPProtectionService.errors.includes(ERRORS.GENERIC);
this.setState({
- isSignedOut: !lazy.IPPSignInWatcher.isSignedIn,
+ isSignedOut: !isSignedIn,
isProtectionEnabled: !!protectionEnabledSince,
protectionEnabledSince,
hasUpgraded,
diff --git a/browser/components/ipprotection/IPProtectionService.sys.mjs b/browser/components/ipprotection/IPProtectionService.sys.mjs
@@ -10,7 +10,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
GuardianClient: "resource:///modules/ipprotection/GuardianClient.sys.mjs",
IPPHelpers: "resource:///modules/ipprotection/IPProtectionHelpers.sys.mjs",
IPPProxyManager: "resource:///modules/ipprotection/IPPProxyManager.sys.mjs",
- IPPSignInWatcher: "resource:///modules/ipprotection/IPPSignInWatcher.sys.mjs",
+ UIState: "resource://services-sync/UIState.sys.mjs",
SpecialMessageActions:
"resource://messaging-system/lib/SpecialMessageActions.sys.mjs",
NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
@@ -76,6 +76,7 @@ class IPProtectionServiceSingleton extends EventTarget {
errors = [];
enrolling = null;
+ signedIn = null;
guardian = null;
proxyManager = null;
@@ -142,7 +143,7 @@ class IPProtectionServiceSingleton extends EventTarget {
}
this.proxyManager = new lazy.IPPProxyManager(this.guardian);
- await Promise.allSettled(this.#helpers.map(helper => helper.init()));
+ this.#helpers.forEach(helper => helper.init());
await this.#updateState();
}
@@ -163,6 +164,7 @@ class IPProtectionServiceSingleton extends EventTarget {
this.#entitlement = null;
this.errors = [];
this.enrolling = null;
+ this.signedIn = null;
this.#helpers.forEach(helper => helper.uninit());
@@ -266,6 +268,7 @@ class IPProtectionServiceSingleton extends EventTarget {
* Reset the statuses that are set based on a FxA account.
*/
resetAccount() {
+ this.signedIn = null;
this.#entitlement = null;
if (this.proxyManager?.active) {
this.stop(false);
@@ -274,6 +277,17 @@ class IPProtectionServiceSingleton extends EventTarget {
}
/**
+ * Checks if a user has signed in.
+ *
+ * @returns {boolean}
+ */
+ get isSignedIn() {
+ let { status } = lazy.UIState.get();
+ this.signedIn = status == lazy.UIState.STATUS_SIGNED_IN;
+ return this.signedIn;
+ }
+
+ /**
* Checks if the user has enrolled with FxA to use the proxy.
*
* @param { boolean } onlyCached - if true only the cached clients will be checked.
@@ -422,8 +436,9 @@ class IPProtectionServiceSingleton extends EventTarget {
// For non authenticated users, we can check if they are eligible (the UI
// is shown and they have to login) or we don't know yet their current
// enroll state (no UI is shown).
+ let signedIn = this.isSignedIn;
let eligible = this.isEligible;
- if (!lazy.IPPSignInWatcher.isSignedIn) {
+ if (!signedIn) {
return !eligible
? IPProtectionStates.UNAVAILABLE
: IPProtectionStates.UNAUTHENTICATED;
diff --git a/browser/components/ipprotection/moz.build b/browser/components/ipprotection/moz.build
@@ -20,7 +20,6 @@ EXTRA_JS_MODULES.ipprotection += [
"IPProtectionServerlist.sys.mjs",
"IPProtectionService.sys.mjs",
"IPProtectionUsage.sys.mjs",
- "IPPSignInWatcher.sys.mjs",
]
BROWSER_CHROME_MANIFESTS += [
diff --git a/browser/components/ipprotection/tests/browser/head.js b/browser/components/ipprotection/tests/browser/head.js
@@ -13,10 +13,6 @@ const { IPProtectionService, IPProtectionStates } = ChromeUtils.importESModule(
"resource:///modules/ipprotection/IPProtectionService.sys.mjs"
);
-const { IPPSignInWatcher } = ChromeUtils.importESModule(
- "resource:///modules/ipprotection/IPPSignInWatcher.sys.mjs"
-);
-
const { HttpServer, HTTP_403 } = ChromeUtils.importESModule(
"resource://testing-common/httpd.sys.mjs"
);
@@ -27,6 +23,7 @@ const { NimbusTestUtils } = ChromeUtils.importESModule(
ChromeUtils.defineESModuleGetters(this, {
sinon: "resource://testing-common/Sinon.sys.mjs",
+ UIState: "resource://services-sync/UIState.sys.mjs",
ExperimentAPI: "resource://nimbus/ExperimentAPI.sys.mjs",
CustomizableUI:
"moz-src:///browser/components/customizableui/CustomizableUI.sys.mjs",
@@ -239,6 +236,7 @@ let DEFAULT_SERVICE_STATUS = {
/* exported DEFAULT_SERVICE_STATUS */
let STUBS = {
+ UIState: undefined,
isLinkedToGuardian: undefined,
enroll: undefined,
fetchUserInfo: undefined,
@@ -269,7 +267,7 @@ add_setup(async function setupVPN() {
});
function setupStubs(stubs = STUBS) {
- stubs.isSignedIn = setupSandbox.stub(IPPSignInWatcher, "isSignedIn");
+ stubs.UIState = setupSandbox.stub(UIState, "get");
stubs.isLinkedToGuardian = setupSandbox.stub(
IPProtectionService.guardian,
"isLinkedToGuardian"
@@ -297,7 +295,11 @@ function setupService(
stubs = STUBS
) {
if (typeof isSignedIn != "undefined") {
- stubs.isSignedIn.get(() => isSignedIn);
+ stubs.UIState.returns({
+ status: isSignedIn
+ ? UIState.STATUS_SIGNED_IN
+ : UIState.STATUS_NOT_CONFIGURED,
+ });
}
if (typeof isEnrolled != "undefined") {
diff --git a/browser/components/ipprotection/tests/xpcshell/test_IPProtectionPanel.js b/browser/components/ipprotection/tests/xpcshell/test_IPProtectionPanel.js
@@ -3,15 +3,15 @@ https://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
+const { UIState } = ChromeUtils.importESModule(
+ "resource://services-sync/UIState.sys.mjs"
+);
const { IPProtectionPanel } = ChromeUtils.importESModule(
"resource:///modules/ipprotection/IPProtectionPanel.sys.mjs"
);
const { IPProtectionService, IPProtectionStates } = ChromeUtils.importESModule(
"resource:///modules/ipprotection/IPProtectionService.sys.mjs"
);
-const { IPPSignInWatcher } = ChromeUtils.importESModule(
- "resource:///modules/ipprotection/IPPSignInWatcher.sys.mjs"
-);
/**
* A class that mocks the IP Protection panel.
@@ -131,7 +131,9 @@ add_task(async function test_updateState() {
*/
add_task(async function test_IPProtectionPanel_signedIn() {
let sandbox = sinon.createSandbox();
- sandbox.stub(IPPSignInWatcher, "isSignedIn").get(() => true);
+ sandbox.stub(UIState, "get").returns({
+ status: UIState.STATUS_SIGNED_IN,
+ });
sandbox
.stub(IPProtectionService.guardian, "isLinkedToGuardian")
.resolves(true);
@@ -180,7 +182,9 @@ add_task(async function test_IPProtectionPanel_signedIn() {
*/
add_task(async function test_IPProtectionPanel_signedOut() {
let sandbox = sinon.createSandbox();
- sandbox.stub(IPPSignInWatcher, "isSignedIn").get(() => false);
+ sandbox.stub(UIState, "get").returns({
+ status: UIState.STATUS_NOT_CONFIGURED,
+ });
let ipProtectionPanel = new IPProtectionPanel();
let fakeElement = new FakeIPProtectionPanelElement();
@@ -223,7 +227,9 @@ add_task(async function test_IPProtectionPanel_started_stopped() {
fakeElement.isConnected = true;
let sandbox = sinon.createSandbox();
- sandbox.stub(IPPSignInWatcher, "isSignedIn").get(() => true);
+ sandbox.stub(UIState, "get").returns({
+ status: UIState.STATUS_SIGNED_IN,
+ });
sandbox
.stub(IPProtectionService.guardian, "isLinkedToGuardian")
.resolves(true);
diff --git a/browser/components/ipprotection/tests/xpcshell/test_IPProtectionService.js b/browser/components/ipprotection/tests/xpcshell/test_IPProtectionService.js
@@ -12,8 +12,8 @@ const { ExtensionTestUtils } = ChromeUtils.importESModule(
const { IPProtectionService, IPProtectionStates } = ChromeUtils.importESModule(
"resource:///modules/ipprotection/IPProtectionService.sys.mjs"
);
-const { IPPSignInWatcher } = ChromeUtils.importESModule(
- "resource:///modules/ipprotection/IPPSignInWatcher.sys.mjs"
+const { UIState } = ChromeUtils.importESModule(
+ "resource://services-sync/UIState.sys.mjs"
);
do_get_profile();
@@ -31,7 +31,7 @@ ExtensionTestUtils.init(this);
function setupStubs(
sandbox,
options = {
- signedIn: true,
+ signedIn: UIState.STATUS_SIGNED_IN,
isLinkedToGuardian: true,
validProxyPass: true,
entitlement: {
@@ -41,7 +41,9 @@ function setupStubs(
},
}
) {
- sandbox.stub(IPPSignInWatcher, "isSignedIn").get(() => options.signedIn);
+ sandbox.stub(UIState, "get").returns({
+ status: options.signedIn,
+ });
sandbox
.stub(IPProtectionService.guardian, "isLinkedToGuardian")
.resolves(options.isLinkedToGuardian);
@@ -172,7 +174,7 @@ add_task(async function test_IPProtectionService_updateState_signedIn() {
await signedInEventPromise;
- Assert.ok(IPPSignInWatcher.isSignedIn, "Should be signed in after update");
+ Assert.ok(IPProtectionService.isSignedIn, "Should be signed in after update");
IPProtectionService.uninit();
sandbox.restore();
@@ -187,7 +189,9 @@ add_task(async function test_IPProtectionService_updateState_signedOut() {
await IPProtectionService.init();
- sandbox.stub(IPPSignInWatcher, "isSignedIn").get(() => false);
+ UIState.get.returns({
+ status: UIState.STATUS_NOT_CONFIGURED,
+ });
let signedOutEventPromise = waitForEvent(
IPProtectionService,
@@ -201,7 +205,7 @@ add_task(async function test_IPProtectionService_updateState_signedOut() {
await signedOutEventPromise;
Assert.ok(
- !IPPSignInWatcher.isSignedIn,
+ !IPProtectionService.isSignedIn,
"Should not be signed in after update"
);
@@ -291,7 +295,9 @@ add_task(async function test_IPProtectionService_hasUpgraded_signed_out() {
await IPProtectionService.init();
- sandbox.stub(IPPSignInWatcher, "isSignedIn").get(() => false);
+ UIState.get.returns({
+ status: UIState.STATUS_NOT_CONFIGURED,
+ });
let signedOutEventPromise = waitForEvent(
IPProtectionService,
diff --git a/browser/components/ipprotection/tests/xpcshell/test_IPProtectionStates.js b/browser/components/ipprotection/tests/xpcshell/test_IPProtectionStates.js
@@ -6,8 +6,8 @@ https://creativecommons.org/publicdomain/zero/1.0/ */
const { IPProtectionService, IPProtectionStates } = ChromeUtils.importESModule(
"resource:///modules/ipprotection/IPProtectionService.sys.mjs"
);
-const { IPPSignInWatcher } = ChromeUtils.importESModule(
- "resource:///modules/ipprotection/IPPSignInWatcher.sys.mjs"
+const { UIState } = ChromeUtils.importESModule(
+ "resource://services-sync/UIState.sys.mjs"
);
do_get_profile();
@@ -53,7 +53,9 @@ add_task(async function test_IPProtectionStates_uninitialized() {
*/
add_task(async function test_IPProtectionStates_uninitialized() {
let sandbox = sinon.createSandbox();
- sandbox.stub(IPPSignInWatcher, "isSignedIn").get(() => false);
+ sandbox.stub(UIState, "get").returns({
+ status: UIState.STATUS_STATUS_NOT_CONFIGURED,
+ });
sandbox
.stub(IPProtectionService.guardian, "isLinkedToGuardian")
.resolves(false);
@@ -85,7 +87,9 @@ add_task(async function test_IPProtectionStates_uninitialized() {
*/
add_task(async function test_IPProtectionStates_unauthenticated() {
let sandbox = sinon.createSandbox();
- sandbox.stub(IPPSignInWatcher, "isSignedIn").get(() => true);
+ sandbox.stub(UIState, "get").returns({
+ status: UIState.STATUS_SIGNED_IN,
+ });
sandbox
.stub(IPProtectionService.guardian, "isLinkedToGuardian")
.resolves(false);
@@ -108,7 +112,9 @@ add_task(async function test_IPProtectionStates_unauthenticated() {
"IP Protection service should no longer be unauthenticated"
);
- sandbox.stub(IPPSignInWatcher, "isSignedIn").get(() => false);
+ UIState.get.returns({
+ status: UIState.STATUS_STATUS_NOT_CONFIGURED,
+ });
await IPProtectionService.updateState();
@@ -127,7 +133,9 @@ add_task(async function test_IPProtectionStates_unauthenticated() {
*/
add_task(async function test_IPProtectionStates_enrolling() {
let sandbox = sinon.createSandbox();
- sandbox.stub(IPPSignInWatcher, "isSignedIn").get(() => true);
+ sandbox.stub(UIState, "get").returns({
+ status: UIState.STATUS_SIGNED_IN,
+ });
sandbox
.stub(IPProtectionService.guardian, "isLinkedToGuardian")
.resolves(false);
@@ -166,7 +174,9 @@ add_task(async function test_IPProtectionStates_enrolling() {
*/
add_task(async function test_IPProtectionStates_ready() {
let sandbox = sinon.createSandbox();
- sandbox.stub(IPPSignInWatcher, "isSignedIn").get(() => true);
+ sandbox.stub(UIState, "get").returns({
+ status: UIState.STATUS_SIGNED_IN,
+ });
sandbox
.stub(IPProtectionService.guardian, "isLinkedToGuardian")
.resolves(true);
@@ -184,7 +194,9 @@ add_task(async function test_IPProtectionStates_ready() {
"IP Protection service should be ready"
);
- sandbox.stub(IPPSignInWatcher, "isSignedIn").get(() => false);
+ UIState.get.returns({
+ status: UIState.STATUS_STATUS_NOT_CONFIGURED,
+ });
await IPProtectionService.updateState();
@@ -203,7 +215,9 @@ add_task(async function test_IPProtectionStates_ready() {
*/
add_task(async function test_IPProtectionStates_active() {
let sandbox = sinon.createSandbox();
- sandbox.stub(IPPSignInWatcher, "isSignedIn").get(() => true);
+ sandbox.stub(UIState, "get").returns({
+ status: UIState.STATUS_SIGNED_IN,
+ });
sandbox
.stub(IPProtectionService.guardian, "isLinkedToGuardian")
.resolves(true);
@@ -254,7 +268,9 @@ add_task(async function test_IPProtectionStates_active() {
*/
add_task(async function test_IPProtectionStates_error() {
let sandbox = sinon.createSandbox();
- sandbox.stub(IPPSignInWatcher, "isSignedIn").get(() => true);
+ sandbox.stub(UIState, "get").returns({
+ status: UIState.STATUS_SIGNED_IN,
+ });
sandbox
.stub(IPProtectionService.guardian, "isLinkedToGuardian")
.resolves(true);