fune/toolkit/components/nimbus/FeatureManifest.js
Drew Willcoxon f0b457683b Bug 1730618 - Allow Nimbus to enable Firefox Suggest online and override the offline default. r=mythmon,preferences-reviewers,mstriemer
This reworks the fix to bug 1729776.

Currently Nimbus doesn't have a way to force the two suggestions prefs [1] on or
off. That might seem surprising since we've run experiments already. Initially
we defaulted the two prefs to true but we defaulted the separate feature-gate
pref [2] to false, and it was the feature-gate pref we controlled via Nimbus. At
some point we changed the defaults to false and then in Firefox flipped them to
true after showing the onboarding dialog. As a result we've never needed to
override the two suggestion prefs via Nimbus.

The problem now is that we default-enable offline (for US en users), so we set
all three prefs to true. For the online rollout, we need to keep the
feature-gate pref enabled but disable the suggestion prefs, and there's no way
to do that.

My first idea was to add new Nimbus variables to override the two suggestion
prefs. The prefs would keep their default true values but be overridden by
Nimbus. But that doesn't work because there's no way for Firefox to tell whether
the prefs are true because the user has opted in (overriding Nimbus) or because
they still have their default values. Setting the prefs to true on the user
branch doesn't have any effect because they're also true on the default branch.
Or maybe there's a way I don't know about to force them to true on the user
branch, but even if there were, it seems brittle to rely on a value being set on
the user branch to distinguish between the two cases. (This is a potential
problem for any prefs that are controlled by both Nimbus and the user. So far
the prefs we've been using via Nimbus have all been hidden feature-gate-type
things and implementation details.)

We already have a `quickSuggestScenario` variable. We currently use it only to
tell what we should send in the telemetry ping (bug 1729576) and whether some
parts of the prefs UI should be shown. This revision makes it much more
important by treating it as the source of truth for the user's scenario. It now
determines the default values of related prefs, including the two suggestions
prefs.

The logic is:

```
If quickSuggestScenario is non-null:
  scenario = quickSuggestScenario
Else (e.g., there's no rollout):
  If the user is US en:
    scenario = offline
  Else:
    scenario = history
```

After determining the scenario, it's set it as
`browser.urlbar.quicksuggest.scenario` on the default branch. There's an
existing pref observer in UrlbarPrefs, and I added a case for this pref so that
when it's updated we also update all the other prefs that depend on the
scenario. This way when the pref is set -- either due to Nimbus update or by
changing it on about:config -- all the other prefs stay in sync.

I kept the default value of `browser.urlbar.quicksuggest.scenario` but removed
it as the fallback for `quickSuggestScenario`. If it still both had a default
and remained the fallback, then it would be impossible to tell when Nimbus is
trying to override it, because any fetch of the value from Nimbus would just
return the fallback pref's value if there is no override.

I considered instead removing the default value and keeping it as the fallback.
The drawback of that is that unenrollments would not take effect until restart.
I actually tried this approach first, and in tests, after mock experiments were
unenrolled, the pref values remained what they were when the experiment was
active.

It might also be possible to not have the `browser.urlbar.quicksuggest.scenario`
pref at all. We could call NimbusFeatures directly to get the scenario. However,
currently we cache and access Nimbus variables through UrlbarPrefs, as we do
with prefs, and I don't want to add an inconsistency.

This revision also fixes bug 1730596 since it was easy to do given that I needed
a way to prevent indirect recursive updates to the scenario, and I can use that
for bug 1730596 too (the `_updatingFirefoxSuggestScenario` bool).

[1] `browser.urlbar.suggest.quicksuggest` and `browser.urlbar.suggest.quicksuggest.sponsored`
[2] `browser.urlbar.quicksuggest.enabled`

Differential Revision: https://phabricator.services.mozilla.com/D125511
2021-09-16 18:28:02 +00:00

297 lines
9.5 KiB
JavaScript

/* 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 EXPORTED_SYMBOLS = ["FeatureManifest"];
/**
* Features must be added here to be accessible through the NimbusFeature API.
* !! Don't forget to validate changes with ./mach test toolkit/components/nimbus/test/unit/test_FeatureManifest.js
*/
const FeatureManifest = {
urlbar: {
description: "The Address Bar",
hasExposure: true,
exposureDescription:
"Exposure is sent once per browsing session after the first urlbar query is made.",
variables: {
merinoEnabled: {
type: "boolean",
fallbackPref: "browser.urlbar.merino.enabled",
description: "Whether Merino is enabled as a quick suggest source",
},
quickSuggestEnabled: {
type: "boolean",
fallbackPref: "browser.urlbar.quicksuggest.enabled",
description: "Global toggle for the QuickSuggest feature",
},
quickSuggestNonSponsoredIndex: {
type: "int",
fallbackPref: "browser.urlbar.quicksuggest.nonSponsoredIndex",
description:
"The index of non-sponsored QuickSuggest results within the general group. A negative index is relative to the end of the group",
},
quickSuggestRemoteSettingsEnabled: {
type: "boolean",
fallbackPref: "browser.urlbar.quicksuggest.remoteSettings.enabled",
description:
"Whether Remote Settings is enabled as a quick suggest source",
},
quickSuggestScenario: {
// IMPORTANT: This should not have a fallbackPref. See UrlbarPrefs.jsm.
type: "string",
description:
"The Firefox Suggest scenario in which the user is enrolled",
enum: ["history", "offline", "online"],
},
quickSuggestShouldShowOnboardingDialog: {
type: "boolean",
fallbackPref: "browser.urlbar.quicksuggest.shouldShowOnboardingDialog",
description:
"Whether or not to show the QuickSuggest onboarding dialog",
},
quickSuggestShowOnboardingDialogAfterNRestarts: {
type: "int",
fallbackPref:
"browser.urlbar.quicksuggest.showOnboardingDialogAfterNRestarts",
description:
"Show QuickSuggest onboarding dialog after N browser restarts",
},
quickSuggestSponsoredIndex: {
type: "int",
fallbackPref: "browser.urlbar.quicksuggest.sponsoredIndex",
description:
"The index of sponsored QuickSuggest results within the general group. A negative index is relative to the end of the group",
},
},
},
aboutwelcome: {
description: "The about:welcome page",
hasExposure: true,
exposureDescription:
"Exposure is sent once per browsing session when the about:welcome URL is first accessed.",
isEarlyStartup: true,
variables: {
enabled: {
type: "boolean",
fallbackPref: "browser.aboutwelcome.enabled",
description:
"Should users see about:welcome? If this is false, users will see a regular new tab instead.",
},
screens: {
type: "json",
fallbackPref: "browser.aboutwelcome.screens",
description: "Content to show in the onboarding flow",
},
skipFocus: {
type: "boolean",
fallbackPref: "browser.aboutwelcome.skipFocus",
description:
"Should the urlbar should be focused when users first land on about:welcome?",
},
transitions: {
type: "boolean",
description: "Enable transition effect between screens",
},
},
},
abouthomecache: {
description: "The startup about:home cache.",
hasExposure: false,
isEarlyStartup: true,
variables: {
enabled: {
type: "boolean",
fallbackPref: "browser.startup.homepage.abouthome_cache.enabled",
description: "Is the feature enabled?",
},
},
},
firefox100: {
description: "Firefox User-Agent version",
hasExposure: false,
isEarlyStartup: true,
variables: {
firefoxVersion: {
type: "int",
description: "Firefox version to spoof (or `0` to use default version)",
},
},
},
newtab: {
description: "The about:newtab page",
hasExposure: true,
exposureDescription:
"Exposure is sent once per browsing session when the first newtab page loads (either about:newtab or about:home).",
isEarlyStartup: true,
variables: {
newTheme: {
type: "boolean",
description: "Enable the new theme",
},
customizationMenuEnabled: {
type: "boolean",
fallbackPref:
"browser.newtabpage.activity-stream.customizationMenu.enabled",
description: "Enable the customization panel inside of the newtab",
},
prefsButtonIcon: {
type: "string",
description: "Icon url to use for the preferences button",
},
},
},
pocketNewtab: {
description: "The Pocket section in newtab",
hasExposure: false,
isEarlyStartup: true,
variables: {
spocPositions: {
type: "string",
fallbackPref:
"browser.newtabpage.activity-stream.discoverystream.spoc-positions",
description: "CSV string of spoc position indexes on newtab grid",
},
compactLayout: {
type: "boolean",
fallbackPref:
"browser.newtabpage.activity-stream.discoverystream.compactLayout.enabled",
description: "Enable compact cards on newtab grid",
},
},
},
"password-autocomplete": {
description: "A special autocomplete UI for password fields.",
hasExposure: false,
variables: {
directMigrateSingleProfile: {
type: "boolean",
description: "Enable direct migration?",
},
},
},
shellService: {
description: "Interface with OS, e.g., pinning and set default",
hasExposure: false,
isEarlyStartup: true,
variables: {
disablePin: {
type: "boolean",
description: "Disable pin to taskbar feature",
},
setDefaultBrowserUserChoice: {
type: "boolean",
fallbackPref: "browser.shell.setDefaultBrowserUserChoice",
description: "Should it set as default browser",
},
},
},
upgradeDialog: {
description: "The dialog shown for major upgrades",
hasExposure: false,
isEarlyStartup: true,
variables: {
enabled: {
type: "boolean",
fallbackPref: "browser.startup.upgradeDialog.enabled",
description: "Is the feature enabled?",
},
},
},
privatebrowsing: {
description: "about:privatebrowsing",
hasExposure: true,
exposureDescription:
"Exposure is sent once per browsing session the first time the PB page loads",
variables: {
infoEnabled: {
type: "boolean",
description: "Should we show the info section.",
},
infoIcon: {
type: "string",
description:
"Icon shown in the left side of the info section. Default is the private browsing icon.",
},
infoTitle: {
type: "string",
description: "Is the title in the info section enabled.",
},
infoTitleEnabled: {
type: "boolean",
description: "Is the title in the info section enabled.",
},
infoBody: {
type: "string",
description: "Text content in the info section.",
},
infoLinkText: {
type: "string",
description: "Text for the link in the info section.",
},
infoLinkUrl: {
type: "string",
description: "URL for the info section link.",
},
promoEnabled: {
type: "boolean",
description: "Should we show the promo section.",
},
promoSectionStyle: {
type: "string",
description:
"Sets the position of the promo section. Possible values are: top, below-search, bottom. Default bottom.",
enum: ["top", "below-search", "bottom"],
},
promoTitle: {
type: "string",
description: "The text content of the promo section.",
},
promoTitleEnabled: {
type: "boolean",
description: "Should we show text content in the promo section.",
},
promoLinkText: {
type: "string",
description: "The text of the link in the promo box.",
},
promoHeader: {
type: "string",
description: "The title of the promo section.",
},
promoLinkUrl: {
type: "string",
description: "URL for link in the promo box.",
},
promoLinkType: {
type: "string",
description:
"Type of promo link type. Possible values: link, button. Default is link.",
enum: ["link", "button"],
},
promoImageLarge: {
type: "string",
description:
"URL for image used on the left side of the promo box, larger, showcases some feature. Default off.",
},
promoImageSmall: {
type: "string",
description:
"URL for image used on the right side of the promo box, smaller, usually a logo. Default off.",
},
},
},
readerMode: {
description: "Firefox Reader Mode",
hasExposure: false,
isEarlyStartup: true,
variables: {
pocketCTAVersion: {
type: "string",
fallbackPref: "reader.pocket.ctaVersion",
description:
"What version of Pocket CTA to show in Reader Mode (Empty string is no CTA)",
},
},
},
};