commit e4049a13c32bfb3f3b22368969721ac216bc6f0b parent 5f3af8f6b058653ba4d2fadf908e4e96fa1c665a Author: Andrea Marchesini <amarchesini@mozilla.com> Date: Mon, 10 Nov 2025 14:06:22 +0000 Bug 1998795 - IPProtection: implement the ACTIVATING state - part 3 - ACTIVE state in the proxy manager, r=ip-protection-reviewers,sstreich Differential Revision: https://phabricator.services.mozilla.com/D271668 Diffstat:
16 files changed, 321 insertions(+), 365 deletions(-)
diff --git a/browser/components/ipprotection/IPPAutoStart.sys.mjs b/browser/components/ipprotection/IPPAutoStart.sys.mjs @@ -10,6 +10,7 @@ ChromeUtils.defineESModuleGetters(lazy, { IPProtectionServerlist: "resource:///modules/ipprotection/IPProtectionServerlist.sys.mjs", IPPProxyManager: "resource:///modules/ipprotection/IPPProxyManager.sys.mjs", + IPPProxyStates: "resource:///modules/ipprotection/IPPProxyManager.sys.mjs", IPProtectionService: "resource:///modules/ipprotection/IPProtectionService.sys.mjs", IPProtectionStates: @@ -85,7 +86,7 @@ class IPPAutoStartSingleton { case lazy.IPProtectionStates.READY: if (this.#shouldStartWhenReady) { this.#shouldStartWhenReady = false; - lazy.IPProtectionService.start(/* user action: */ false); + lazy.IPPProxyManager.start(/* user action: */ false); } break; @@ -118,6 +119,10 @@ class IPPEarlyStartupFilter { "IPProtectionService:StateChanged", this.handleEvent ); + lazy.IPPProxyManager.addEventListener( + "IPPProxyManager:StateChanged", + this.handleEvent + ); } } @@ -127,6 +132,10 @@ class IPPEarlyStartupFilter { if (this.autoStartAndAtStartup) { this.#autoStartAndAtStartup = false; + lazy.IPPProxyManager.removeEventListener( + "IPPProxyManager:StateChanged", + this.handleEvent + ); lazy.IPProtectionService.removeEventListener( "IPProtectionService:StateChanged", this.handleEvent @@ -147,15 +156,15 @@ class IPPEarlyStartupFilter { this.uninit(); break; - case lazy.IPProtectionStates.ACTIVE: - // We have completed our task. - this.uninit(); - break; - default: // Let's ignoring any other state. break; } + + if (lazy.IPPProxyManager.state === lazy.IPPProxyStates.ACTIVE) { + // We have completed our task. + this.uninit(); + } } } diff --git a/browser/components/ipprotection/IPPProxyManager.sys.mjs b/browser/components/ipprotection/IPPProxyManager.sys.mjs @@ -35,18 +35,22 @@ ChromeUtils.defineLazyGetter(lazy, "logConsole", function () { /** * @typedef {object} IPPProxyStates * List of the possible states of the IPPProxyManager. + * @property {string} NOT_READY + * The proxy is not ready because the main state machine is not in the READY state. * @property {string} READY * The proxy is ready to be activated. + * @property {string} ACTIVE + * The proxy is active. * @property {string} ERROR * Error * - * TODO: eventually this will be a proper state machine. - * * Note: If you update this list of states, make sure to update the * corresponding documentation in the `docs` folder as well. */ export const IPPProxyStates = Object.freeze({ + NOT_READY: "not-ready", READY: "ready", + ACTIVE: "active", ERROR: "error", }); @@ -54,7 +58,7 @@ export const IPPProxyStates = Object.freeze({ * Manages the proxy connection for the IPProtectionService. */ class IPPProxyManagerSingleton extends EventTarget { - #state = IPPProxyStates.READY; + #state = IPPProxyStates.NOT_READY; #pass = null; /**@type {import("./IPPChannelFilter.sys.mjs").IPPChannelFilter | null} */ @@ -92,6 +96,10 @@ class IPPProxyManagerSingleton extends EventTarget { this.errors = []; + if (this.#state === IPPProxyStates.ACTIVE) { + this.stop(false); + } + this.reset(); this.#connection = null; this.usageObserver.stop(); @@ -103,7 +111,7 @@ class IPPProxyManagerSingleton extends EventTarget { * @returns {Date} */ get activatedAt() { - return this.active && this.#activatedAt; + return this.#state === IPPProxyStates.ACTIVE && this.#activatedAt; } get usageObserver() { @@ -125,7 +133,7 @@ class IPPProxyManagerSingleton extends EventTarget { } get active() { - return !!this.#connection?.active && !!this.#connection?.proxyInfo; + return this.#state === IPPProxyStates.ACTIVE; } get isolationKey() { @@ -155,14 +163,47 @@ class IPPProxyManagerSingleton extends EventTarget { } /** - * Starts the proxy connection: - * - Gets a new proxy pass if needed. - * - Find the server to use. - * - Adds usage and network-error observers. + * Start the proxy if the user is eligible. * - * @returns {Promise<boolean>} + * @param {boolean} userAction + * True if started by user action, false if system action */ - async start() { + async start(userAction = true) { + if (this.#state === IPPProxyStates.NOT_READY) { + throw new Error("This method should not be called when not ready"); + } + + let started = false; + try { + started = await this.#startInternal(); + } catch (error) { + this.#setErrorState(ERRORS.GENERIC, error); + return; + } + + if (this.#state === IPPProxyStates.ERROR) { + return; + } + + // Proxy failed to start but no error was given. + if (!started) { + this.#setState(IPPProxyStates.READY); + return; + } + + this.#setState(IPPProxyStates.ACTIVE); + + Glean.ipprotection.toggled.record({ + userAction, + enabled: true, + }); + + if (userAction) { + this.#reloadCurrentTab(); + } + } + + async #startInternal() { await lazy.IPProtectionServerlist.maybeFetchList(); const enrollAndEntitleData = @@ -184,16 +225,6 @@ class IPPProxyManagerSingleton extends EventTarget { this.errors = []; - try { - const started = await this.#startInternal(); - return started; - } catch (error) { - this.#setErrorState(ERRORS.GENERIC, error); - return false; - } - } - - async #startInternal() { this.createChannelFilter(); // If the current proxy pass is valid, no need to re-authenticate. @@ -205,8 +236,8 @@ class IPPProxyManagerSingleton extends EventTarget { const location = lazy.IPProtectionServerlist.getDefaultLocation(); const server = lazy.IPProtectionServerlist.selectServer(location?.city); if (!server) { - lazy.logConsole.error("No server found"); - throw new Error("No server found"); + this.#setErrorState(ERRORS.GENERIC, "No server found"); + return false; } lazy.logConsole.debug("Server:", server?.hostname); @@ -225,26 +256,54 @@ class IPPProxyManagerSingleton extends EventTarget { lazy.logConsole.info("Started"); - if (this.active) { + if (!!this.#connection?.active && !!this.#connection?.proxyInfo) { this.#activatedAt = ChromeUtils.now(); + return true; } - return this.active; + return false; } /** - * Stops the proxy connection and observers. Returns the duration of the connection. + * Stops the proxy. * - * @returns {int} + * @param {boolean} userAction + * True if started by user action, false if system action */ - stop() { + async stop(userAction = true) { + if (this.#state !== IPPProxyStates.ACTIVE) { + return; + } + this.cancelChannelFilter(); this.networkErrorObserver.stop(); lazy.logConsole.info("Stopped"); - return ChromeUtils.now() - this.#activatedAt; + const sessionLength = ChromeUtils.now() - this.#activatedAt; + + Glean.ipprotection.toggled.record({ + userAction, + duration: sessionLength, + enabled: false, + }); + + this.#setState(IPPProxyStates.READY); + + if (userAction) { + this.#reloadCurrentTab(); + } + } + + /** + * Gets the current window and reloads the selected tab. + */ + #reloadCurrentTab() { + let win = Services.wm.getMostRecentBrowserWindow(); + if (win) { + win.gBrowser.reloadTab(win.gBrowser.selectedTab); + } } /** @@ -252,22 +311,13 @@ class IPPProxyManagerSingleton extends EventTarget { */ async reset() { this.#pass = null; - if (this.active) { + if (this.#state === IPPProxyStates.ACTIVE) { await this.stop(); } } #handleEvent(_event) { - if ( - lazy.IPProtectionService.state === lazy.IPProtectionStates.UNAVAILABLE || - lazy.IPProtectionService.state === lazy.IPProtectionStates.UNAUTHENTICATED - ) { - if (this.active) { - this.stop(false); - } - - this.reset(); - } + this.updateState(); } /** @@ -344,8 +394,15 @@ class IPPProxyManagerSingleton extends EventTarget { } updateState() { - // TODO: something better here. - this.#setState(IPPProxyStates.READY); + this.stop(false); + this.reset(); + + if (lazy.IPProtectionService.state === lazy.IPProtectionStates.READY) { + this.#setState(IPPProxyStates.READY); + return; + } + + this.#setState(IPPProxyStates.NOT_READY); } /** diff --git a/browser/components/ipprotection/IPPStartupCache.sys.mjs b/browser/components/ipprotection/IPPStartupCache.sys.mjs @@ -50,12 +50,6 @@ class IPPStartupCacheSingleton { "IPProtectionService:StateChanged", this.handleEvent ); - - // The state cannot be "ACTIVE" from cache. In case we need to activate the - // proxy at startup time, something else will take care of it. - if (this.#stateFromCache === lazy.IPProtectionStates.ACTIVE) { - this.#stateFromCache = lazy.IPProtectionStates.READY; - } } async initOnStartupCompleted() {} diff --git a/browser/components/ipprotection/IPProtection.sys.mjs b/browser/components/ipprotection/IPProtection.sys.mjs @@ -15,8 +15,6 @@ ChromeUtils.defineESModuleGetters(lazy, { "resource:///modules/ipprotection/IPProtectionPanel.sys.mjs", IPProtectionService: "resource:///modules/ipprotection/IPProtectionService.sys.mjs", - IPProtectionStates: - "resource:///modules/ipprotection/IPProtectionService.sys.mjs", IPPProxyManager: "resource:///modules/ipprotection/IPPProxyManager.sys.mjs", IPPProxyStates: "resource:///modules/ipprotection/IPPProxyManager.sys.mjs", requestIdleCallback: "resource://gre/modules/Timer.sys.mjs", @@ -274,8 +272,7 @@ class IPProtectionWidget { * @param {XULElement} toolbaritem - the widget toolbaritem. */ #onCreated(toolbaritem) { - let isActive = - lazy.IPProtectionService.state === lazy.IPProtectionStates.ACTIVE; + let isActive = lazy.IPPProxyManager.state === lazy.IPPProxyStates.ACTIVE; let isError = lazy.IPPProxyManager.state === lazy.IPPProxyStates.ERROR && lazy.IPPProxyManager.errors.includes(ERRORS.GENERIC); @@ -319,7 +316,7 @@ class IPProtectionWidget { await Promise.resolve(); let moved = !!lazy.CustomizableUI.getPlacementOfWidget(widgetId); if (!moved) { - lazy.IPProtectionService.stop(); + lazy.IPPProxyManager.stop(); } } @@ -339,8 +336,7 @@ class IPProtectionWidget { event.type == "IPPProxyManager:StateChanged" ) { let status = { - isActive: - lazy.IPProtectionService.state === lazy.IPProtectionStates.ACTIVE, + isActive: lazy.IPPProxyManager.state === lazy.IPPProxyStates.ACTIVE, isError: lazy.IPPProxyManager.state === lazy.IPPProxyStates.ERROR && lazy.IPPProxyManager.errors.includes(ERRORS.GENERIC), diff --git a/browser/components/ipprotection/IPProtectionPanel.sys.mjs b/browser/components/ipprotection/IPProtectionPanel.sys.mjs @@ -172,11 +172,11 @@ export class IPProtectionPanel { } #startProxy() { - lazy.IPProtectionService.start(); + lazy.IPPProxyManager.start(); } #stopProxy() { - lazy.IPProtectionService.stop(); + lazy.IPPProxyManager.stop(); } showHelpPage() { diff --git a/browser/components/ipprotection/IPProtectionService.sys.mjs b/browser/components/ipprotection/IPProtectionService.sys.mjs @@ -12,7 +12,6 @@ ChromeUtils.defineESModuleGetters(lazy, { "resource:///modules/ipprotection/IPPEnrollAndEntitleManager.sys.mjs", IPPHelpers: "resource:///modules/ipprotection/IPProtectionHelpers.sys.mjs", IPPNimbusHelper: "resource:///modules/ipprotection/IPPNimbusHelper.sys.mjs", - IPPProxyManager: "resource:///modules/ipprotection/IPPProxyManager.sys.mjs", IPPSignInWatcher: "resource:///modules/ipprotection/IPPSignInWatcher.sys.mjs", IPPStartupCache: "resource:///modules/ipprotection/IPPStartupCache.sys.mjs", SpecialMessageActions: @@ -34,8 +33,6 @@ const ENABLED_PREF = "browser.ipProtection.enabled"; * The user is signed out but eligible (via nimbus). The panel should show the login view. * @property {string} READY * Ready to be activated. - * @property {string} ACTIVE - * Proxy is active. * * Note: If you update this list of states, make sure to update the * corresponding documentation in the `docs` folder as well. @@ -45,7 +42,6 @@ export const IPProtectionStates = Object.freeze({ UNAVAILABLE: "unavailable", UNAUTHENTICATED: "unauthenticated", READY: "ready", - ACTIVE: "active", }); /** @@ -124,10 +120,6 @@ class IPProtectionServiceSingleton extends EventTarget { return; } - if (this.#state === IPProtectionStates.ACTIVE) { - this.stop(false); - } - this.#helpers.forEach(helper => helper.uninit()); this.#setState(IPProtectionStates.UNINITIALIZED); @@ -139,68 +131,6 @@ class IPProtectionServiceSingleton extends EventTarget { ); } - /** - * Start the proxy if the user is eligible. - * - * @param {boolean} userAction - * True if started by user action, false if system action - */ - async start(userAction = true) { - const started = await lazy.IPPProxyManager.start(); - - // Proxy failed to start but no error was given. - if (!started) { - return; - } - - this.#setState(IPProtectionStates.ACTIVE); - - Glean.ipprotection.toggled.record({ - userAction, - enabled: true, - }); - - if (userAction) { - this.reloadCurrentTab(); - } - } - - /** - * Stops the proxy. - * - * @param {boolean} userAction - * True if started by user action, false if system action - */ - async stop(userAction = true) { - if (!lazy.IPPProxyManager.active) { - return; - } - - const sessionLength = lazy.IPPProxyManager.stop(); - - Glean.ipprotection.toggled.record({ - userAction, - duration: sessionLength, - enabled: false, - }); - - this.#setState(IPProtectionStates.READY); - - if (userAction) { - this.reloadCurrentTab(); - } - } - - /** - * Gets the current window and reloads the selected tab. - */ - reloadCurrentTab() { - let win = Services.wm.getMostRecentBrowserWindow(); - if (win) { - win.gBrowser.reloadTab(win.gBrowser.selectedTab); - } - } - async startLoginFlow(browser) { return lazy.SpecialMessageActions.fxaSignInFlow(SIGNIN_DATA, browser); } @@ -240,11 +170,6 @@ class IPProtectionServiceSingleton extends EventTarget { : IPProtectionStates.UNAUTHENTICATED; } - // The connection is already active. - if (lazy.IPPProxyManager.active) { - return IPProtectionStates.ACTIVE; - } - // Check if the current account is enrolled and has an entitlement. if (!lazy.IPPEnrollAndEntitleManager.isEnrolledAndEntitled && !eligible) { return IPProtectionStates.UNAVAILABLE; diff --git a/browser/components/ipprotection/docs/StateMachine.rst b/browser/components/ipprotection/docs/StateMachine.rst @@ -13,7 +13,6 @@ The service transitions across the following states: - ``UNAVAILABLE``: User not eligible (Nimbus) or signed out with no eligibility; UI hidden. - ``UNAUTHENTICATED``: User signed out but eligible; UI shows login. - ``READY``: Ready to activate the proxy. -- ``ACTIVE``: Proxy is active. High‑level transitions ---------------------- @@ -21,7 +20,6 @@ High‑level transitions - Feature disabled → ``UNINITIALIZED``. - During startup, if initialization isn’t complete, use cached state from ``IPPStartupCache``. - Not signed in → ``UNAVAILABLE`` if not eligible, otherwise ``UNAUTHENTICATED``. -- Proxy already active → ``ACTIVE``. - If an entitlement is cached/valid → ``READY``. - Otherwise, check enrollment with Guardian (via ``IPPErollHelper``): - Not enrolled → ``UNAVAILABLE`` (not eligible). diff --git a/browser/components/ipprotection/tests/browser/browser_IPProtectionService.js b/browser/components/ipprotection/tests/browser/browser_IPProtectionService.js @@ -335,8 +335,8 @@ add_task(async function test_IPProtectionService_retry_errors() { IPPProxyManager.setErrorState(); let startedEventPromise = BrowserTestUtils.waitForEvent( - IPProtectionService, - "IPProtectionService:StateChanged", + IPPProxyManager, + "IPPProxyManager:StateChanged", false, () => !!IPPProxyManager.activatedAt ); @@ -344,13 +344,9 @@ add_task(async function test_IPProtectionService_retry_errors() { await startedEventPromise; - Assert.equal( - IPProtectionService.state, - IPProtectionStates.ACTIVE, - "Proxy is active" - ); + Assert.equal(IPPProxyManager.state, IPPProxyStates.ACTIVE, "Proxy is active"); - IPProtectionService.stop(); + IPPProxyManager.stop(); await closePanel(); await cleanupAlpha(); @@ -381,8 +377,8 @@ add_task(async function test_IPProtectionService_stop_on_signout() { ); let startedEventPromise = BrowserTestUtils.waitForEvent( - IPProtectionService, - "IPProtectionService:StateChanged", + IPPProxyManager, + "IPPProxyManager:StateChanged", false, () => !!IPPProxyManager.activatedAt ); @@ -390,11 +386,7 @@ add_task(async function test_IPProtectionService_stop_on_signout() { await startedEventPromise; - Assert.equal( - IPProtectionService.state, - IPProtectionStates.ACTIVE, - "Proxy is active" - ); + Assert.equal(IPPProxyManager.state, IPPProxyStates.ACTIVE, "Proxy is active"); let vpnOffPromise = BrowserTestUtils.waitForEvent( IPProtectionService, @@ -410,8 +402,8 @@ add_task(async function test_IPProtectionService_stop_on_signout() { await vpnOffPromise; Assert.notStrictEqual( - IPProtectionService.state, - IPProtectionStates.ACTIVE, + IPPProxyManager.state, + IPPProxyStates.ACTIVE, "Proxy has stopped" ); @@ -469,19 +461,15 @@ add_task(async function test_IPProtectionService_reload() { content.connectionToggleEl.click(); await tabReloaded; - Assert.equal( - IPProtectionService.state, - IPProtectionStates.ACTIVE, - "Proxy is active" - ); + Assert.equal(IPPProxyManager.state, IPPProxyStates.ACTIVE, "Proxy is active"); tabReloaded = waitForTabReloaded(gBrowser.selectedTab); content.connectionToggleEl.click(); await tabReloaded; Assert.notStrictEqual( - IPProtectionService.state, - IPProtectionStates.ACTIVE, + IPPProxyManager.state, + IPPProxyStates.ACTIVE, "Proxy is not active" ); diff --git a/browser/components/ipprotection/tests/browser/browser_ipprotection_telemetry.js b/browser/components/ipprotection/tests/browser/browser_ipprotection_telemetry.js @@ -7,6 +7,7 @@ const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { + IPPProxyManager: "resource:///modules/ipprotection/IPPProxyManager.sys.mjs", IPProtectionService: "resource:///modules/ipprotection/IPProtectionService.sys.mjs", }); @@ -60,8 +61,8 @@ add_task(async function user_toggle_on_and_off() { Services.fog.testResetFOG(); await Services.fog.testFlushAllChildren(); let vpnOnPromise = BrowserTestUtils.waitForEvent( - lazy.IPProtectionService, - "IPProtectionService:StateChanged", + lazy.IPPProxyManager, + "IPPProxyManager:StateChanged", false, () => !!IPPProxyManager.activatedAt ); @@ -76,8 +77,8 @@ add_task(async function user_toggle_on_and_off() { Assert.equal(toggledEvents[0].extra.userAction, "true"); let vpnOffPromise = BrowserTestUtils.waitForEvent( - lazy.IPProtectionService, - "IPProtectionService:StateChanged", + lazy.IPPProxyManager, + "IPPProxyManager:StateChanged", false, () => !IPPProxyManager.activatedAt ); @@ -146,8 +147,8 @@ add_task(async function toggle_off_on_shutdown() { Services.fog.testResetFOG(); let vpnOnPromise = BrowserTestUtils.waitForEvent( - lazy.IPProtectionService, - "IPProtectionService:StateChanged", + lazy.IPPProxyManager, + "IPPProxyManager:StateChanged", false, () => !!IPPProxyManager.activatedAt ); diff --git a/browser/components/ipprotection/tests/browser/browser_ipprotection_toolbar.js b/browser/components/ipprotection/tests/browser/browser_ipprotection_toolbar.js @@ -7,6 +7,7 @@ const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { + IPPProxyManager: "resource:///modules/ipprotection/IPPProxyManager.sys.mjs", IPProtectionService: "resource:///modules/ipprotection/IPProtectionService.sys.mjs", IPProtectionStates: @@ -85,8 +86,8 @@ add_task(async function toolbar_icon_status() { Assert.ok(toggle, "Status card connection toggle should be present"); let vpnOnPromise = BrowserTestUtils.waitForEvent( - lazy.IPProtectionService, - "IPProtectionService:StateChanged", + lazy.IPPProxyManager, + "IPPProxyManager:StateChanged", false, () => !!IPPProxyManager.activatedAt ); @@ -98,8 +99,8 @@ add_task(async function toolbar_icon_status() { "Toolbar icon should now show connected status" ); let vpnOffPromise = BrowserTestUtils.waitForEvent( - lazy.IPProtectionService, - "IPProtectionService:StateChanged", + lazy.IPPProxyManager, + "IPPProxyManager:StateChanged", false, () => lazy.IPProtectionService.state === lazy.IPProtectionStates.READY ); @@ -132,8 +133,8 @@ add_task(async function toolbar_icon_status_new_window() { let content = await openPanel(); let vpnOnPromise = BrowserTestUtils.waitForEvent( - lazy.IPProtectionService, - "IPProtectionService:StateChanged", + lazy.IPPProxyManager, + "IPPProxyManager:StateChanged", false, () => !!IPPProxyManager.activatedAt ); @@ -177,8 +178,8 @@ add_task(async function customize_toolbar_remove_widget() { ).position; let stoppedEventPromise = BrowserTestUtils.waitForEvent( - lazy.IPProtectionService, - "IPProtectionService:StateChanged", + lazy.IPPProxyManager, + "IPPProxyManager:StateChanged", false, () => lazy.IPProtectionService.state === lazy.IPProtectionStates.READY ); diff --git a/browser/components/ipprotection/tests/xpcshell/head.js b/browser/components/ipprotection/tests/xpcshell/head.js @@ -7,6 +7,9 @@ const { IPProtectionService, IPProtectionStates } = ChromeUtils.importESModule( "resource:///modules/ipprotection/IPProtectionService.sys.mjs" ); +const { IPPProxyManager, IPPProxyStates } = ChromeUtils.importESModule( + "resource:///modules/ipprotection/IPPProxyManager.sys.mjs" +); const { IPPSignInWatcher } = ChromeUtils.importESModule( "resource:///modules/ipprotection/IPPSignInWatcher.sys.mjs" ); diff --git a/browser/components/ipprotection/tests/xpcshell/test_IPPStartupCache.js b/browser/components/ipprotection/tests/xpcshell/test_IPPStartupCache.js @@ -83,23 +83,6 @@ add_task(async function test_IPPStartupCache_enabled() { "The state is unitialized" ); } - - // ACTIVE to READY - { - Services.prefs.setCharPref( - "browser.ipProtection.stateCache", - IPProtectionStates.ACTIVE - ); - - const cache = new IPPStartupCacheSingleton(); - cache.init(); - - Assert.ok( - !cache.isStartupCompleted, - "In XPCShell mode the cache is active" - ); - Assert.equal(cache.state, IPProtectionStates.READY, "The state is READY"); - } }); /** diff --git a/browser/components/ipprotection/tests/xpcshell/test_IPProtectionPanel.js b/browser/components/ipprotection/tests/xpcshell/test_IPProtectionPanel.js @@ -249,12 +249,12 @@ add_task(async function test_IPProtectionPanel_started_stopped() { IPProtectionService.updateState(); let startedEventPromise = waitForEvent( - IPProtectionService, - "IPProtectionService:StateChanged", - () => IPProtectionService.state === IPProtectionStates.ACTIVE + IPPProxyManager, + "IPPProxyManager:StateChanged", + () => IPPProxyManager.state === IPPProxyStates.ACTIVE ); - IPProtectionService.start(); + IPPProxyManager.start(); await startedEventPromise; @@ -271,12 +271,12 @@ add_task(async function test_IPProtectionPanel_started_stopped() { ); let stoppedEventPromise = waitForEvent( - IPProtectionService, - "IPProtectionService:StateChanged", - () => IPProtectionService.state !== IPProtectionStates.ACTIVE + IPPProxyManager, + "IPPProxyManager:StateChanged", + () => IPPProxyManager.state !== IPPProxyStates.ACTIVE ); - IPProtectionService.stop(); + IPPProxyManager.stop(); await stoppedEventPromise; diff --git a/browser/components/ipprotection/tests/xpcshell/test_IPProtectionService.js b/browser/components/ipprotection/tests/xpcshell/test_IPProtectionService.js @@ -9,9 +9,6 @@ const { AddonTestUtils } = ChromeUtils.importESModule( const { ExtensionTestUtils } = ChromeUtils.importESModule( "resource://testing-common/ExtensionXPCShellUtils.sys.mjs" ); -const { IPPProxyManager } = ChromeUtils.importESModule( - "resource:///modules/ipprotection/IPPProxyManager.sys.mjs" -); const { IPPEnrollAndEntitleManager } = ChromeUtils.importESModule( "resource:///modules/ipprotection/IPPEnrollAndEntitleManager.sys.mjs" ); @@ -38,98 +35,6 @@ add_setup(async function () { }); /** - * Tests that starting the service gets a state changed event. - */ -add_task(async function test_IPProtectionService_start() { - let sandbox = sinon.createSandbox(); - setupStubs(sandbox); - - IPProtectionService.init(); - - await waitForEvent( - IPProtectionService, - "IPProtectionService:StateChanged", - () => IPProtectionService.state === IPProtectionStates.READY - ); - - Assert.ok( - !IPPProxyManager.activatedAt, - "IP Protection service should not be active initially" - ); - - let startedEventPromise = waitForEvent( - IPProtectionService, - "IPProtectionService:StateChanged", - () => IPProtectionService.state === IPProtectionStates.ACTIVE - ); - - IPProtectionService.start(); - - await startedEventPromise; - - Assert.equal( - IPProtectionService.state, - IPProtectionStates.ACTIVE, - "IP Protection service should be active after starting" - ); - Assert.ok( - !!IPPProxyManager.activatedAt, - "IP Protection service should have an activation timestamp" - ); - Assert.ok( - IPPProxyManager.active, - "IP Protection service should have an active connection" - ); - - IPProtectionService.uninit(); - sandbox.restore(); -}); - -/** - * Tests that stopping the service gets stop events. - */ -add_task(async function test_IPProtectionService_stop() { - let sandbox = sinon.createSandbox(); - setupStubs(sandbox); - - const waitForReady = waitForEvent( - IPProtectionService, - "IPProtectionService:StateChanged", - () => IPProtectionService.state === IPProtectionStates.READY - ); - - IPProtectionService.init(); - await waitForReady; - - await IPProtectionService.start(); - - let stoppedEventPromise = waitForEvent( - IPProtectionService, - "IPProtectionService:StateChanged", - () => IPProtectionService.state !== IPProtectionStates.ACTIVE - ); - IPProtectionService.stop(); - - await stoppedEventPromise; - Assert.notEqual( - IPProtectionService.state, - IPProtectionStates.ACTIVE, - "IP Protection service should not be active after stopping" - ); - Assert.ok( - !IPPProxyManager.activatedAt, - "IP Protection service should not have an activation timestamp after stopping" - ); - Assert.ok( - !IPProtectionService.connection, - "IP Protection service should not have an active connection" - ); - - IPProtectionService.uninit(); - sandbox.restore(); -}); - -/** * Tests that a signed in status sends a status changed event. */ add_task(async function test_IPProtectionService_updateState_signedIn() { diff --git a/browser/components/ipprotection/tests/xpcshell/test_IPProtectionStates.js b/browser/components/ipprotection/tests/xpcshell/test_IPProtectionStates.js @@ -200,62 +200,3 @@ add_task(async function test_IPProtectionStates_ready() { IPProtectionService.uninit(); sandbox.restore(); }); - -/** - * Tests the active state. - */ -add_task(async function test_IPProtectionStates_active() { - let sandbox = sinon.createSandbox(); - sandbox.stub(IPPSignInWatcher, "isSignedIn").get(() => true); - sandbox - .stub(IPProtectionService.guardian, "isLinkedToGuardian") - .resolves(true); - sandbox.stub(IPProtectionService.guardian, "fetchUserInfo").resolves({ - status: 200, - error: undefined, - entitlement: { uid: 42 }, - }); - sandbox.stub(IPProtectionService.guardian, "fetchProxyPass").resolves({ - status: 200, - error: undefined, - pass: { - isValid: () => options.validProxyPass, - asBearerToken: () => "Bearer helloworld", - }, - }); - - const waitForReady = waitForEvent( - IPProtectionService, - "IPProtectionService:StateChanged", - () => IPProtectionService.state === IPProtectionStates.READY - ); - - IPProtectionService.init(); - - await waitForReady; - - Assert.equal( - IPProtectionService.state, - IPProtectionStates.READY, - "IP Protection service should be ready" - ); - - await IPProtectionService.start(false); - - Assert.equal( - IPProtectionService.state, - IPProtectionStates.ACTIVE, - "IP Protection service should be active" - ); - - await IPProtectionService.stop(false); - - Assert.equal( - IPProtectionService.state, - IPProtectionStates.READY, - "IP Protection service should be ready again" - ); - - IPProtectionService.uninit(); - sandbox.restore(); -}); diff --git a/browser/components/ipprotection/tests/xpcshell/test_IPProxyManager.js b/browser/components/ipprotection/tests/xpcshell/test_IPProxyManager.js @@ -4,9 +4,6 @@ "use strict"; -const { IPPProxyManager, IPPProxyStates } = ChromeUtils.importESModule( - "resource:///modules/ipprotection/IPPProxyManager.sys.mjs" -); const { IPPEnrollAndEntitleManager } = ChromeUtils.importESModule( "resource:///modules/ipprotection/IPPEnrollAndEntitleManager.sys.mjs" ); @@ -16,6 +13,98 @@ add_setup(async function () { }); /** + * Tests that starting the service gets a state changed event. + */ +add_task(async function test_IPPProxyManager_start() { + let sandbox = sinon.createSandbox(); + setupStubs(sandbox); + + IPProtectionService.init(); + + await waitForEvent( + IPProtectionService, + "IPProtectionService:StateChanged", + () => IPProtectionService.state === IPProtectionStates.READY + ); + + Assert.ok( + !IPPProxyManager.activatedAt, + "IP Protection service should not be active initially" + ); + + let startedEventPromise = waitForEvent( + IPPProxyManager, + "IPPProxyManager:StateChanged", + () => IPPProxyManager.state === IPPProxyStates.ACTIVE + ); + + IPPProxyManager.start(); + + await startedEventPromise; + + Assert.equal( + IPPProxyManager.state, + IPPProxyStates.ACTIVE, + "IP Protection service should be active after starting" + ); + Assert.ok( + !!IPPProxyManager.activatedAt, + "IP Protection service should have an activation timestamp" + ); + Assert.ok( + IPPProxyManager.active, + "IP Protection service should have an active connection" + ); + + IPProtectionService.uninit(); + sandbox.restore(); +}); + +/** + * Tests that stopping the service gets stop events. + */ +add_task(async function test_IPPProxyManager_stop() { + let sandbox = sinon.createSandbox(); + setupStubs(sandbox); + + const waitForReady = waitForEvent( + IPProtectionService, + "IPProtectionService:StateChanged", + () => IPProtectionService.state === IPProtectionStates.READY + ); + + IPProtectionService.init(); + await waitForReady; + + await IPPProxyManager.start(); + + let stoppedEventPromise = waitForEvent( + IPPProxyManager, + "IPPProxyManager:StateChanged", + () => IPPProxyManager.state !== IPPProxyStates.ACTIVE + ); + IPPProxyManager.stop(); + + await stoppedEventPromise; + Assert.notEqual( + IPPProxyManager.state, + IPPProxyStates.ACTIVE, + "IP Protection service should not be active after stopping" + ); + Assert.ok( + !IPPProxyManager.activatedAt, + "IP Protection service should not have an activation timestamp after stopping" + ); + Assert.ok( + !IPProtectionService.connection, + "IP Protection service should not have an active connection" + ); + + IPProtectionService.uninit(); + sandbox.restore(); +}); + +/** * Tests that the proxy manager gets proxy pass and connection on starting * and removes the connection after after stop. */ @@ -23,14 +112,15 @@ add_task(async function test_IPPProxyManager_start_stop_reset() { const sandbox = sinon.createSandbox(); setupStubs(sandbox); - IPProtectionService.init(); - - await waitForEvent( + let readyEvent = waitForEvent( IPProtectionService, "IPProtectionService:StateChanged", () => IPProtectionService.state === IPProtectionStates.READY ); + IPProtectionService.init(); + await readyEvent; + await IPPProxyManager.start(); Assert.ok(IPPProxyManager.active, "Should be active after starting"); @@ -45,7 +135,7 @@ add_task(async function test_IPPProxyManager_start_stop_reset() { "Should have a valid proxy pass after starting" ); - await IPPProxyManager.stop(); + IPPProxyManager.stop(); Assert.ok(!IPPProxyManager.active, "Should not be active after starting"); @@ -129,7 +219,7 @@ add_task(async function test_IPPProxyStates_error() { "IP Protection service should be ready" ); - await IPProtectionService.start(false); + await IPPProxyManager.start(false); Assert.equal( IPPProxyManager.state, @@ -140,3 +230,68 @@ add_task(async function test_IPPProxyStates_error() { IPProtectionService.uninit(); sandbox.restore(); }); + +/** + * Tests the active state. + */ +add_task(async function test_IPPProxytates_active() { + let sandbox = sinon.createSandbox(); + sandbox.stub(IPPSignInWatcher, "isSignedIn").get(() => true); + sandbox + .stub(IPProtectionService.guardian, "isLinkedToGuardian") + .resolves(true); + sandbox.stub(IPProtectionService.guardian, "fetchUserInfo").resolves({ + status: 200, + error: undefined, + entitlement: { uid: 42 }, + }); + sandbox.stub(IPProtectionService.guardian, "fetchProxyPass").resolves({ + status: 200, + error: undefined, + pass: { + isValid: () => options.validProxyPass, + asBearerToken: () => "Bearer helloworld", + }, + }); + + const waitForReady = waitForEvent( + IPProtectionService, + "IPProtectionService:StateChanged", + () => IPProtectionService.state === IPProtectionStates.READY + ); + + IPProtectionService.init(); + + await waitForReady; + + Assert.equal( + IPProtectionService.state, + IPProtectionStates.READY, + "IP Protection service should be ready" + ); + + await IPPProxyManager.start(false); + + Assert.equal( + IPProtectionService.state, + IPProtectionStates.READY, + "IP Protection service should be in ready state" + ); + + Assert.equal( + IPPProxyManager.state, + IPPProxyStates.ACTIVE, + "IP Protection service should be active" + ); + + await IPPProxyManager.stop(false); + + Assert.equal( + IPProtectionService.state, + IPProtectionStates.READY, + "IP Protection service should be ready again" + ); + + IPProtectionService.uninit(); + sandbox.restore(); +});