From e2d70a23ef5aeb9b8f787d29280097793ae86a5d Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Wed, 19 Aug 2026 18:10:39 -0700 Subject: [PATCH] fix(browser): finish profile reset state cleanup --- .../browser/browser-profile-reset.test.ts | 30 +++++++++++++++++-- src/main/browser/browser-profile-reset.ts | 12 ++++++-- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/main/browser/browser-profile-reset.test.ts b/src/main/browser/browser-profile-reset.test.ts index df6db06e717..ef77a88137e 100644 --- a/src/main/browser/browser-profile-reset.test.ts +++ b/src/main/browser/browser-profile-reset.test.ts @@ -10,11 +10,12 @@ vi.mock('electron', () => ({ import { findPartitionWebContents, resetBrowserProfilePartition } from './browser-profile-reset' import { acquireCookieMutationLock, withCookieMutationLock } from './browser-cookie-import-clear' -type SessionMock = Session & Mocked> +type SessionMock = Session & Mocked> function makeSession(overrides: Partial = {}): SessionMock { return { clearData: vi.fn(async () => undefined), + clearStorageData: vi.fn(async () => undefined), ...overrides } as SessionMock } @@ -33,7 +34,7 @@ describe('resetBrowserProfilePartition', () => { getAllWebContentsMock.mockReturnValue([]) }) - it('clears the whole partition, not a filtered subset', async () => { + it('rotates media-device IDs and clears the whole partition', async () => { const targetSession = makeSession() await resetBrowserProfilePartition({ targetSession, @@ -41,6 +42,8 @@ describe('resetBrowserProfilePartition', () => { clearPendingCookieImport: vi.fn(), clearImportedSource: vi.fn() }) + expect(targetSession.clearStorageData).toHaveBeenCalledTimes(1) + expect(targetSession.clearStorageData).toHaveBeenCalledWith({ storages: ['cookies'] }) expect(targetSession.clearData).toHaveBeenCalledTimes(1) expect(targetSession.clearData).toHaveBeenCalledWith() }) @@ -61,6 +64,7 @@ describe('resetBrowserProfilePartition', () => { const order: string[] = [] const clearImportedSource = vi.fn(() => void order.push('source')) const targetSession = makeSession({ + clearStorageData: vi.fn(async () => void order.push('clearStorageData')), clearData: vi.fn(async () => void order.push('clearData')) }) await resetBrowserProfilePartition({ @@ -70,7 +74,7 @@ describe('resetBrowserProfilePartition', () => { clearImportedSource }) expect(clearImportedSource).toHaveBeenCalledTimes(1) - expect(order).toEqual(['source', 'staged', 'clearData']) + expect(order).toEqual(['source', 'staged', 'clearStorageData', 'clearData']) }) it('reloads every live context in the partition after clearing', async () => { @@ -132,6 +136,26 @@ describe('resetBrowserProfilePartition', () => { expect(healthy.reload).toHaveBeenCalledTimes(1) }) + it('reloads live contexts after a partially failed clear', async () => { + const targetSession = makeSession({ + clearData: vi.fn(async () => { + throw new Error('clear failed') + }) + }) + const live = makeContents(targetSession) + getAllWebContentsMock.mockReturnValue([live]) + + await expect( + resetBrowserProfilePartition({ + targetSession, + partition: 'persist:orca-browser', + clearPendingCookieImport: vi.fn(), + clearImportedSource: vi.fn() + }) + ).rejects.toThrow('clear failed') + expect(live.reload).toHaveBeenCalledTimes(1) + }) + it('waits for the session mutation lock and releases it after failure', async () => { const targetSession = makeSession({ clearData: vi.fn(async () => { diff --git a/src/main/browser/browser-profile-reset.ts b/src/main/browser/browser-profile-reset.ts index e273eb1f25f..944696aa994 100644 --- a/src/main/browser/browser-profile-reset.ts +++ b/src/main/browser/browser-profile-reset.ts @@ -36,8 +36,14 @@ export async function resetBrowserProfilePartition( // Clear metadata first so a partial reset never advertises an import whose data may be gone. clearImportedSource() clearPendingCookieImport(partition) - // clearStorageData bypasses trust-token, bounce-tracking, and network-history removal. - await targetSession.clearData() - reloadPartitionContexts(targetSession) + try { + // Electron only rotates its persisted media-device ID salt on a cookie storage clear. + await targetSession.clearStorageData({ storages: ['cookies'] }) + // BrowsingDataRemover additionally reaches trust tokens, bounce tracking, and network history. + await targetSession.clearData() + } finally { + // A rejected removal can still leave the partition partially cleared. + reloadPartitionContexts(targetSession) + } }) }