diff --git a/browser/components/BrowserGlue.sys.mjs b/browser/components/BrowserGlue.sys.mjs index e50b702d48fa..4fefb967c856 100644 --- a/browser/components/BrowserGlue.sys.mjs +++ b/browser/components/BrowserGlue.sys.mjs @@ -78,7 +78,6 @@ ChromeUtils.defineESModuleGetters(lazy, { WebChannel: "resource://gre/modules/WebChannel.sys.mjs", WebProtocolHandlerRegistrar: "resource:///modules/WebProtocolHandlerRegistrar.sys.mjs", - WindowsRegistry: "resource://gre/modules/WindowsRegistry.sys.mjs", setTimeout: "resource://gre/modules/Timer.sys.mjs", }); @@ -509,73 +508,6 @@ BrowserGlue.prototype = { } }, - /** - * Show a notification bar offering a reset. - * - * @param reason - * String of either "unused" or "uninstall", specifying the reason - * why a profile reset is offered. - */ - _resetProfileNotification(reason) { - let win = lazy.BrowserWindowTracker.getTopWindow({ - allowFromInactiveWorkspace: true, - }); - if (!win) { - return; - } - - const { ResetProfile } = ChromeUtils.importESModule( - "resource://gre/modules/ResetProfile.sys.mjs" - ); - if (!ResetProfile.resetSupported()) { - return; - } - - let productName = lazy.gBrandBundle.GetStringFromName("brandShortName"); - let resetBundle = Services.strings.createBundle( - "chrome://global/locale/resetProfile.properties" - ); - - let message; - if (reason == "unused") { - message = resetBundle.formatStringFromName("resetUnusedProfile.message", [ - productName, - ]); - } else if (reason == "uninstall") { - message = resetBundle.formatStringFromName("resetUninstalled.message", [ - productName, - ]); - } else { - throw new Error( - `Unknown reason (${reason}) given to _resetProfileNotification.` - ); - } - let buttons = [ - { - label: resetBundle.formatStringFromName( - "refreshProfile.resetButton.label", - [productName] - ), - accessKey: resetBundle.GetStringFromName( - "refreshProfile.resetButton.accesskey" - ), - callback() { - ResetProfile.openConfirmationDialog(win); - }, - }, - ]; - - win.gNotificationBox.appendNotification( - "reset-profile-notification", - { - label: message, - image: "chrome://global/skin/icons/question-64.png", - priority: win.gNotificationBox.PRIORITY_INFO_LOW, - }, - buttons - ); - }, - _notifyUnsignedAddonsDisabled() { let win = lazy.BrowserWindowTracker.getTopWindow({ allowFromInactiveWorkspace: true, @@ -802,8 +734,6 @@ BrowserGlue.prototype = { } }); - this._maybeOfferProfileReset(); - this._checkForOldBuildUpdates(); // Check if Sync is configured @@ -822,56 +752,6 @@ BrowserGlue.prototype = { this._firstWindowTelemetry(aWindow); }, - _maybeOfferProfileReset() { - // Offer to reset a user's profile if it hasn't been used for 60 days. - const OFFER_PROFILE_RESET_INTERVAL_MS = 60 * 24 * 60 * 60 * 1000; - let lastUse = Services.appinfo.replacedLockTime; - let disableResetPrompt = Services.prefs.getBoolPref( - "browser.disableResetPrompt", - false - ); - - // Also check prefs.js last modified timestamp as a backstop. - // This helps for cases where the lock file checks don't work, - // e.g. NFS or because the previous time Firefox ran, it ran - // for a very long time. See bug 1054947 and related bugs. - lastUse = Math.max( - lastUse, - Services.prefs.userPrefsFileLastModifiedAtStartup - ); - - if ( - !disableResetPrompt && - lastUse && - Date.now() - lastUse >= OFFER_PROFILE_RESET_INTERVAL_MS - ) { - this._resetProfileNotification("unused"); - } else if (AppConstants.platform == "win" && !disableResetPrompt) { - // Check if we were just re-installed and offer Firefox Reset - let updateChannel; - try { - updateChannel = ChromeUtils.importESModule( - "resource://gre/modules/UpdateUtils.sys.mjs" - ).UpdateUtils.UpdateChannel; - } catch (ex) {} - if (updateChannel) { - let uninstalledValue = lazy.WindowsRegistry.readRegKey( - Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, - "Software\\Mozilla\\Firefox", - `Uninstalled-${updateChannel}` - ); - let removalSuccessful = lazy.WindowsRegistry.removeRegKey( - Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, - "Software\\Mozilla\\Firefox", - `Uninstalled-${updateChannel}` - ); - if (removalSuccessful && uninstalledValue == "True") { - this._resetProfileNotification("uninstall"); - } - } - } - }, - /** * Application shutdown handler. * diff --git a/browser/components/ReinstallCheck.sys.mjs b/browser/components/ReinstallCheck.sys.mjs new file mode 100644 index 000000000000..eb97d70db4f7 --- /dev/null +++ b/browser/components/ReinstallCheck.sys.mjs @@ -0,0 +1,57 @@ +/* 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 { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + WindowsRegistry: "resource://gre/modules/WindowsRegistry.sys.mjs", +}); + +// Reads and clears the registry value the Windows uninstaller writes +function readAndClearUninstalledValue() { + if (AppConstants.platform != "win") { + return false; + } + + if (Services.prefs.getBoolPref("browser.disableResetPrompt", false)) { + return false; + } + + let updateChannel; + try { + updateChannel = ChromeUtils.importESModule( + "resource://gre/modules/UpdateUtils.sys.mjs" + ).UpdateUtils.UpdateChannel; + } catch (ex) {} + if (updateChannel) { + let uninstalledValue = lazy.WindowsRegistry.readRegKey( + Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, + "Software\\Mozilla\\Firefox", + `Uninstalled-${updateChannel}` + ); + let removalSuccessful = lazy.WindowsRegistry.removeRegKey( + Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, + "Software\\Mozilla\\Firefox", + `Uninstalled-${updateChannel}` + ); + return removalSuccessful && uninstalledValue == "True"; + } + return false; +} + +export const ReinstallCheck = { + _wasReinstalled: null, + + /** + * @returns {boolean} whether Firefox was reinstalled since the previous run. + */ + get wasReinstalled() { + if (this._wasReinstalled === null) { + this._wasReinstalled = readAndClearUninstalledValue(); + } + return this._wasReinstalled; + }, +}; diff --git a/browser/components/asrouter/docs/targeting-attributes.md b/browser/components/asrouter/docs/targeting-attributes.md index d2d8e8bff2c6..2ef007708f4b 100644 --- a/browser/components/asrouter/docs/targeting-attributes.md +++ b/browser/components/asrouter/docs/targeting-attributes.md @@ -472,6 +472,25 @@ declare const profileAgeReset: undefined | UnixEpochNumber; type UnixEpochNumber = number; ``` +### `profileLastUse` + +The date the profile was last used before the current session, as a UNIX Epoch +timestamp. This is the more recent of the previous session's lock file time and +the `prefs.js` modification time, and is `0` when neither is available. + +#### Examples +* Has the profile been unused for at least 60 days? +```java +profileLastUse && currentDate|date - profileLastUse >= 5184000000 +``` + +#### Definition +```ts +declare const profileLastUse: UnixEpochNumber; +// UnixEpochNumber is UNIX Epoch timestamp, e.g. 1522843725924 +type UnixEpochNumber = number; +``` + ### `providerCohorts` Information about cohort settings (from prefs, including shield studies) for each provider. @@ -1334,6 +1353,16 @@ restore the previous session on startup; `false` otherwise. A boolean. `true` when both the current install and current profile support creating additional profiles using the `SelectableProfileService`; `false` otherwise. +### `canResetProfile` + +A boolean. `true` when the current profile can be refreshed. + +Any message using the `RESET_PROFILE` action should include this in its targeting. + +### `isFirefoxReinstalled` + +Windows-only. A boolean. `true` when Firefox was uninstalled and then reinstalled over an existing profile since the previous run; `false` otherwise. + ### `hasSelectableProfiles` A boolean. `true` when the `toolkit.profiles.storeID` pref has a value. Indicates that the profile is part of a profile group managed by the `SelectableProfileService`, and the user has used the multiple profiles feature. `false` otherwise. diff --git a/browser/components/asrouter/modules/ASRouterTargeting.sys.mjs b/browser/components/asrouter/modules/ASRouterTargeting.sys.mjs index 155096a4c69c..49724172599a 100644 --- a/browser/components/asrouter/modules/ASRouterTargeting.sys.mjs +++ b/browser/components/asrouter/modules/ASRouterTargeting.sys.mjs @@ -76,6 +76,8 @@ ChromeUtils.defineESModuleGetters(lazy, { PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs", ProfileAge: "resource://gre/modules/ProfileAge.sys.mjs", Region: "resource://gre/modules/Region.sys.mjs", + ReinstallCheck: "moz-src:///browser/components/ReinstallCheck.sys.mjs", + ResetProfile: "resource://gre/modules/ResetProfile.sys.mjs", SearchService: "moz-src:///toolkit/components/search/SearchService.sys.mjs", // eslint-disable-next-line mozilla/no-browser-refs-in-toolkit SelectableProfileService: @@ -785,6 +787,22 @@ const TargetingGetters = { get profileAgeReset() { return lazy.ProfileAge().then(times => times.reset); }, + get profileLastUse() { + // The lock file records when the profile was last used, but it can be + // unreliable, e.g. on NFS or when the previous session ran for a very long + // time. Use the prefs.js modification time as a backstop. See bug 1054947 + // and related bugs. + return Math.max( + Services.appinfo.replacedLockTime, + Services.prefs.userPrefsFileLastModifiedAtStartup + ); + }, + get canResetProfile() { + return lazy.ResetProfile.resetSupported(); + }, + get isFirefoxReinstalled() { + return lazy.ReinstallCheck.wasReinstalled; + }, get usesFirefoxSync() { return Services.prefs.prefHasUserValue(FXA_USERNAME_PREF); }, diff --git a/browser/components/asrouter/modules/OnboardingMessageProvider.sys.mjs b/browser/components/asrouter/modules/OnboardingMessageProvider.sys.mjs index e7fa8f5179d8..cb85208c02ca 100644 --- a/browser/components/asrouter/modules/OnboardingMessageProvider.sys.mjs +++ b/browser/components/asrouter/modules/OnboardingMessageProvider.sys.mjs @@ -2625,6 +2625,49 @@ const BASE_MESSAGES = () => [ id: "selectableProfilesUpdated", }, }, + { + id: "REFRESH_UNUSED_PROFILE_INFOBAR", + template: "infobar", + content: { + type: "global", + priority: 1, + text: { string_id: "refresh-unused-profile-infobar-message" }, + buttons: [ + { + label: { string_id: "refresh-profile-infobar-button" }, + action: { type: "RESET_PROFILE" }, + }, + ], + }, + trigger: { + id: "defaultBrowserCheck", + }, + skip_in_tests: "fires on startup, may interfere with other tests", + priority: 1, + targeting: + "source == 'startup' && canResetProfile && !'browser.disableResetPrompt'|preferenceValue && profileLastUse && currentDate|date - profileLastUse >= 5184000000 && !activeNotifications", + }, + { + id: "REFRESH_REINSTALLED_PROFILE_INFOBAR", + template: "infobar", + content: { + type: "global", + priority: 1, + text: { string_id: "refresh-reinstalled-profile-infobar-message" }, + buttons: [ + { + label: { string_id: "refresh-profile-infobar-button" }, + action: { type: "RESET_PROFILE" }, + }, + ], + }, + trigger: { + id: "defaultBrowserCheck", + }, + skip_in_tests: "fires on startup, may interfere with other tests", + targeting: + "source == 'startup' && isFirefoxReinstalled && canResetProfile && !'browser.disableResetPrompt'|preferenceValue && !activeNotifications", + }, { id: "updated-privacy-notice-notification-infobar", groups: ["cfr"], diff --git a/browser/components/asrouter/tests/browser/browser_asrouter_targeting.js b/browser/components/asrouter/tests/browser/browser_asrouter_targeting.js index 6e851ee78d57..5114fa0ef806 100644 --- a/browser/components/asrouter/tests/browser/browser_asrouter_targeting.js +++ b/browser/components/asrouter/tests/browser/browser_asrouter_targeting.js @@ -27,6 +27,8 @@ ChromeUtils.defineESModuleGetters(this, { ProfileAge: "resource://gre/modules/ProfileAge.sys.mjs", QueryCache: "resource:///modules/asrouter/ASRouterTargeting.sys.mjs", Region: "resource://gre/modules/Region.sys.mjs", + ReinstallCheck: "moz-src:///browser/components/ReinstallCheck.sys.mjs", + ResetProfile: "resource://gre/modules/ResetProfile.sys.mjs", SearchService: "moz-src:///toolkit/components/search/SearchService.sys.mjs", SelectableProfileService: "resource:///modules/profiles/SelectableProfileService.sys.mjs", @@ -357,6 +359,83 @@ add_task(async function check_canCreateSelectableProfiles() { await SpecialPowers.popPrefEnv(); }); +add_task(async function check_canResetProfile() { + const sandbox = sinon.createSandbox(); + const resetSupported = sandbox.stub(ResetProfile, "resetSupported"); + + resetSupported.returns(true); + is( + await ASRouterTargeting.Environment.canResetProfile, + true, + "should be true when the profile supports being reset" + ); + + const message = { id: "foo", targeting: "canResetProfile" }; + is( + await ASRouterTargeting.findMatchingMessage({ messages: [message] }), + message, + "should select the right item by canResetProfile" + ); + + resetSupported.returns(false); + is( + await ASRouterTargeting.Environment.canResetProfile, + false, + "should be false when the profile doesn't support being reset" + ); + + sandbox.restore(); +}); + +add_task(async function check_profileLastUse() { + is( + await ASRouterTargeting.Environment.profileLastUse, + Math.max( + Services.appinfo.replacedLockTime, + Services.prefs.userPrefsFileLastModifiedAtStartup + ), + "should be the most recent lock file and prefs.js timestamps" + ); + + const message = { + id: "foo", + targeting: "profileLastUse <= currentDate|date", + }; + is( + await ASRouterTargeting.findMatchingMessage({ messages: [message] }), + message, + "should select correct item by profileLastUse" + ); +}); + +add_task(async function check_isFirefoxReinstalled() { + const sandbox = sinon.createSandbox(); + const wasReinstalled = sandbox.stub(ReinstallCheck, "wasReinstalled"); + + wasReinstalled.get(() => true); + is( + await ASRouterTargeting.Environment.isFirefoxReinstalled, + true, + "should be true when a reinstall was detected" + ); + + const message = { id: "foo", targeting: "isFirefoxReinstalled" }; + is( + await ASRouterTargeting.findMatchingMessage({ messages: [message] }), + message, + "should select correct item by isFirefoxReinstalled" + ); + + wasReinstalled.get(() => false); + is( + await ASRouterTargeting.Environment.isFirefoxReinstalled, + false, + "should be false when no reinstall was detected" + ); + + sandbox.restore(); +}); + add_task(async function check_hasSelectableProfiles() { is( await ASRouterTargeting.Environment.hasSelectableProfiles, diff --git a/browser/components/moz.build b/browser/components/moz.build index 63cfb33a3b11..02e4c1758e68 100644 --- a/browser/components/moz.build +++ b/browser/components/moz.build @@ -124,6 +124,7 @@ MOZ_SRC_FILES += [ "DefaultBrowserCheck.sys.mjs", "DesktopActorRegistry.sys.mjs", "ProfileDataUpgrader.sys.mjs", + "ReinstallCheck.sys.mjs", "StartupTelemetry.sys.mjs", ] diff --git a/browser/locales/en-US/browser/newtab/asrouter.ftl b/browser/locales/en-US/browser/newtab/asrouter.ftl index 817e392bfa0e..19f125c82a74 100644 --- a/browser/locales/en-US/browser/newtab/asrouter.ftl +++ b/browser/locales/en-US/browser/newtab/asrouter.ftl @@ -497,3 +497,14 @@ lapsed-user-toast-title = { -brand-product-name } still has your back lapsed-user-toast-subtitle = Check out new ways you can browse with more choice, privacy, and control. lapsed-user-toast-whats-new-button = See what’s new lapsed-user-toast-dismiss-button = Dismiss + +## Refresh Firefox infobar +## +## Shown at startup when the profile has not been used in over 60 days, or when +## Firefox has just been reinstalled over an existing profile. +## Both offer to reset the profile to a fresh state. + +refresh-unused-profile-infobar-message = It looks like you haven’t started { -brand-short-name } in a while. Do you want to clean it up for a fresh, like-new experience? And by the way, welcome back! +refresh-reinstalled-profile-infobar-message = Looks like you’ve reinstalled { -brand-short-name }. Want us to clean it up for a fresh, like-new experience? +refresh-profile-infobar-button = Refresh { -brand-short-name }… + .accesskey = e diff --git a/python/l10n/fluent_migrations/bug_1901533_refresh_unused_profile_infobar.py b/python/l10n/fluent_migrations/bug_1901533_refresh_unused_profile_infobar.py new file mode 100644 index 000000000000..0fc2235cfb3f --- /dev/null +++ b/python/l10n/fluent_migrations/bug_1901533_refresh_unused_profile_infobar.py @@ -0,0 +1,50 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +import fluent.syntax.ast as FTL +from fluent.migrate import COPY, REPLACE +from fluent.migrate.helpers import TERM_REFERENCE + + +def migrate(ctx): + """Bug 1901533 - Move the Refresh Firefox infobars into the messaging system, part {index}.""" + + source = "toolkit/chrome/global/resetProfile.properties" + target = "browser/browser/newtab/asrouter.ftl" + + ctx.add_transforms( + target, + target, + [ + FTL.Message( + id=FTL.Identifier("refresh-unused-profile-infobar-message"), + value=REPLACE( + source, + "resetUnusedProfile.message", + {"%1$S": TERM_REFERENCE("brand-short-name")}, + ), + ), + FTL.Message( + id=FTL.Identifier("refresh-reinstalled-profile-infobar-message"), + value=REPLACE( + source, + "resetUninstalled.message", + {"%1$S": TERM_REFERENCE("brand-short-name")}, + ), + ), + FTL.Message( + id=FTL.Identifier("refresh-profile-infobar-button"), + value=REPLACE( + source, + "refreshProfile.resetButton.label", + {"%1$S": TERM_REFERENCE("brand-short-name")}, + ), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("accesskey"), + value=COPY(source, "refreshProfile.resetButton.accesskey"), + ) + ], + ), + ], + ) diff --git a/toolkit/components/messaging-system/lib/MessagingSystemBlocklists.sys.mjs b/toolkit/components/messaging-system/lib/MessagingSystemBlocklists.sys.mjs index 76a088c1361e..b29767940ead 100644 --- a/toolkit/components/messaging-system/lib/MessagingSystemBlocklists.sys.mjs +++ b/toolkit/components/messaging-system/lib/MessagingSystemBlocklists.sys.mjs @@ -66,6 +66,9 @@ export const BLOCKED_ACTION_ONLY_ACTIONS = new Set([ "IPPROTECTION_ENROLL", "CREATE_NEW_SELECTABLE_PROFILE", + // Offers to erase the user's profile data and restart the browser. + "RESET_PROFILE", + // Sends data off the device, or fabricates a record that the user consented // to something. "SUMMARIZE_PAGE", diff --git a/toolkit/components/messaging-system/lib/SpecialMessageActions.sys.mjs b/toolkit/components/messaging-system/lib/SpecialMessageActions.sys.mjs index a3275314d593..19629d9e2bdc 100644 --- a/toolkit/components/messaging-system/lib/SpecialMessageActions.sys.mjs +++ b/toolkit/components/messaging-system/lib/SpecialMessageActions.sys.mjs @@ -63,6 +63,7 @@ ChromeUtils.defineESModuleGetters(lazy, { PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs", // eslint-disable-next-line mozilla/no-browser-refs-in-toolkit Referrals: "resource:///modules/referrals/Referrals.sys.mjs", + ResetProfile: "resource://gre/modules/ResetProfile.sys.mjs", // eslint-disable-next-line mozilla/no-browser-refs-in-toolkit SelectableProfileService: "resource:///modules/profiles/SelectableProfileService.sys.mjs", @@ -1220,6 +1221,13 @@ export const SpecialMessageActions = { ); break; } + case "RESET_PROFILE": { + if (!lazy.ResetProfile.resetSupported()) { + throw new Error("Profile reset is not supported for this profile."); + } + await lazy.ResetProfile.openConfirmationDialog(window); + break; + } default: throw new Error( `Special message action with type ${action.type} is unsupported.` diff --git a/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/SpecialMessageActionSchemas.json b/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/SpecialMessageActionSchemas.json index 933d520c6fc1..cd4250b72222 100644 --- a/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/SpecialMessageActionSchemas.json +++ b/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/SpecialMessageActionSchemas.json @@ -879,6 +879,18 @@ "additionalProperties": false, "description": "Creates a new user profile using SelectableProfileService and launches it in a new instance" }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["RESET_PROFILE"] + } + }, + "required": ["type"], + "additionalProperties": false, + "description": "Opens the refresh confirmation dialog, which resets the current profile and restarts the browser" + }, { "type": "object", "properties": { diff --git a/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/index.md b/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/index.md index c8bbcb2eb878..94eaae88993a 100644 --- a/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/index.md +++ b/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/index.md @@ -545,6 +545,14 @@ Any message that uses this action should have `canCreateSelectableProfiles` as p - args: (none) +### `RESET_PROFILE` + +Opens the refresh confirmation dialog, which resets the current profile and restarts the browser + +Any message that uses this action should have `canResetProfile` as part of the targeting, to ensure we don't show a message where the action will not work. + +- args: (none) + ### `SUBMIT_ONBOARDING_OPT_OUT_PING` Submits a Glean `onboarding-opt-out` ping. Should only be used during preonboarding (but this is not enforced). diff --git a/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/test/browser/browser.toml b/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/test/browser/browser.toml index beadd620c911..adbeb02586b0 100644 --- a/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/test/browser/browser.toml +++ b/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/test/browser/browser.toml @@ -88,6 +88,8 @@ run-if = [ "os == 'linux'", ] +["browser_sma_reset_profile.js"] + ["browser_sma_restore_session.js"] ["browser_sma_set_bookmarks_toolbar_visibility.js"] diff --git a/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/test/browser/browser_sma_reset_profile.js b/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/test/browser/browser_sma_reset_profile.js new file mode 100644 index 000000000000..07af8f5fbc5c --- /dev/null +++ b/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/test/browser/browser_sma_reset_profile.js @@ -0,0 +1,44 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { ResetProfile } = ChromeUtils.importESModule( + "resource://gre/modules/ResetProfile.sys.mjs" +); + +add_task(async function test_RESET_PROFILE() { + let sandbox = sinon.createSandbox(); + sandbox.stub(ResetProfile, "resetSupported").returns(true); + let dialogStub = sandbox.stub(ResetProfile, "openConfirmationDialog"); + + await SMATestUtils.executeAndValidateAction({ type: "RESET_PROFILE" }); + + Assert.equal( + dialogStub.callCount, + 1, + "openConfirmationDialog called by the action" + ); + + sandbox.restore(); +}); + +add_task(async function test_RESET_PROFILE_unsupported() { + let sandbox = sinon.createSandbox(); + sandbox.stub(ResetProfile, "resetSupported").returns(false); + let dialogStub = sandbox.stub(ResetProfile, "openConfirmationDialog"); + + await Assert.rejects( + SpecialMessageActions.handleAction({ type: "RESET_PROFILE" }, gBrowser), + /Profile reset is not supported/, + "should reject when reset is not supported" + ); + + Assert.equal( + dialogStub.callCount, + 0, + "openConfirmationDialog not called when reset is unsupported" + ); + + sandbox.restore(); +}); diff --git a/toolkit/locales/en-US/chrome/global/resetProfile.properties b/toolkit/locales/en-US/chrome/global/resetProfile.properties deleted file mode 100644 index c0600729901d..000000000000 --- a/toolkit/locales/en-US/chrome/global/resetProfile.properties +++ /dev/null @@ -1,14 +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/. - -# LOCALIZATION NOTE: These strings are used for profile reset. - -# LOCALIZATION NOTE (resetUnusedProfile.message): %S is brandShortName. -resetUnusedProfile.message=It looks like you haven’t started %S in a while. Do you want to clean it up for a fresh, like-new experience? And by the way, welcome back! -# LOCALIZATION NOTE (resetUninstalled.message): %S is brandShortName. -resetUninstalled.message=Looks like you’ve reinstalled %S. Want us to clean it up for a fresh, like-new experience? - -# LOCALIZATION NOTE (refreshProfile.resetButton.label): %S is brandShortName. -refreshProfile.resetButton.label=Refresh %S… -refreshProfile.resetButton.accesskey=e diff --git a/toolkit/locales/jar.mn b/toolkit/locales/jar.mn index 7f8010ef9082..d0a347d0c753 100644 --- a/toolkit/locales/jar.mn +++ b/toolkit/locales/jar.mn @@ -33,7 +33,6 @@ #if !defined(XP_WIN) && !defined(MOZ_GECKOVIEW) locale/@AB_CD@/global/printdialog.properties (%chrome/global/printdialog.properties) #endif - locale/@AB_CD@/global/resetProfile.properties (%chrome/global/resetProfile.properties) locale/@AB_CD@/global/dialog.properties (%chrome/global/dialog.properties) locale/@AB_CD@/global/viewSource.properties (%chrome/global/viewSource.properties) locale/@AB_CD@/global/wizard.properties (%chrome/global/wizard.properties)