Backed out changeset d402c8657d3f (bug 1699701) for failing newtab's activity-stream:AboutPreferences Feed and more. CLOSED TREE

This commit is contained in:
Sebastian Hengst 2021-04-09 15:35:48 +02:00
Родитель 480efa657d
Коммит ebd583fd80
14 изменённых файлов: 264 добавлений и 46 удалений

Просмотреть файл

@ -19,7 +19,7 @@ const { PrivateBrowsingUtils } = ChromeUtils.import(
);
XPCOMUtils.defineLazyModuleGetters(this, {
NimbusFeatures: "resource://nimbus/ExperimentAPI.jsm",
ExperimentFeature: "resource://nimbus/ExperimentAPI.jsm",
});
XPCOMUtils.defineLazyPreferenceGetter(
@ -29,13 +29,23 @@ XPCOMUtils.defineLazyPreferenceGetter(
false
);
XPCOMUtils.defineLazyGetter(this, "awExperimentFeature", () => {
return new ExperimentFeature("aboutwelcome");
});
// Note: newtab feature info is currently being loaded in PrefsFeed.jsm,
// But we're recording exposure events here.
XPCOMUtils.defineLazyGetter(this, "newtabExperimentFeature", () => {
return new ExperimentFeature("newtab");
});
class AboutNewTabChild extends JSWindowActorChild {
handleEvent(event) {
if (event.type == "DOMContentLoaded") {
// If the separate about:welcome page is enabled, we can skip all of this,
// since that mode doesn't load any of the Activity Stream bits.
if (
NimbusFeatures.aboutwelcome.isEnabled({ defaultValue: true }) &&
awExperimentFeature.isEnabled({ defaultValue: true }) &&
this.contentWindow.location.pathname.includes("welcome")
) {
return;
@ -81,9 +91,8 @@ class AboutNewTabChild extends JSWindowActorChild {
) {
this.sendAsyncMessage("DefaultBrowserNotification");
// Note: newtab feature info is currently being loaded in PrefsFeed.jsm,
// But we're recording exposure events here.
NimbusFeatures.newtab.recordExposureEvent();
// Send an exposure event to record when we have an experiment active
newtabExperimentFeature.recordExposureEvent();
}
}
}

Просмотреть файл

@ -38,7 +38,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
DownloadsCommon: "resource:///modules/DownloadsCommon.jsm",
DownloadUtils: "resource://gre/modules/DownloadUtils.jsm",
E10SUtils: "resource://gre/modules/E10SUtils.jsm",
NimbusFeatures: "resource://nimbus/ExperimentAPI.jsm",
ExtensionsUI: "resource:///modules/ExtensionsUI.jsm",
HomePage: "resource:///modules/HomePage.jsm",
LightweightThemeConsumer:
@ -614,6 +613,15 @@ XPCOMUtils.defineLazyPreferenceGetter(
false
);
/* Import aboutWelcomeFeature from Nimbus Experiment API
to access experiment values */
XPCOMUtils.defineLazyGetter(this, "aboutWelcomeFeature", () => {
const { ExperimentFeature } = ChromeUtils.import(
"resource://nimbus/ExperimentAPI.jsm"
);
return new ExperimentFeature("aboutwelcome");
});
customElements.setElementCreationCallback("translation-notification", () => {
Services.scriptloader.loadSubScript(
"chrome://browser/content/translation-notification.js",
@ -2224,7 +2232,7 @@ var gBrowserInit = {
// property set to true, if yes remove focus from urlbar for about:welcome
const aboutWelcomeSkipUrlBarFocus =
uriToLoad == "about:welcome" &&
NimbusFeatures.aboutwelcome.getValue()?.skipFocus;
aboutWelcomeFeature.getValue()?.skipFocus;
if (
(isBlankPageURL(uriToLoad) && !aboutWelcomeSkipUrlBarFocus) ||

Просмотреть файл

@ -41,8 +41,11 @@ const { E10SUtils } = ChromeUtils.import(
"resource://gre/modules/E10SUtils.jsm"
);
XPCOMUtils.defineLazyModuleGetters(this, {
NimbusFeatures: "resource://nimbus/ExperimentAPI.jsm",
XPCOMUtils.defineLazyGetter(this, "awExperimentFeature", () => {
const { ExperimentFeature } = ChromeUtils.import(
"resource://nimbus/ExperimentAPI.jsm"
);
return new ExperimentFeature("aboutwelcome");
});
/**
@ -446,7 +449,7 @@ class BaseAboutNewTabService {
*/
if (
NimbusFeatures.aboutwelcome.isEnabled({
awExperimentFeature.isEnabled({
defaultValue: true,
sendExposureEvent: true,
})

Просмотреть файл

@ -17,7 +17,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
TippyTopProvider: "resource://activity-stream/lib/TippyTopProvider.jsm",
AboutWelcomeDefaults:
"resource://activity-stream/aboutwelcome/lib/AboutWelcomeDefaults.jsm",
NimbusFeatures: "resource://nimbus/ExperimentAPI.jsm",
});
XPCOMUtils.defineLazyGetter(this, "log", () => {
@ -27,6 +26,13 @@ XPCOMUtils.defineLazyGetter(this, "log", () => {
return new Logger("AboutWelcomeChild");
});
XPCOMUtils.defineLazyGetter(this, "aboutWelcomeFeature", () => {
const { ExperimentFeature } = ChromeUtils.import(
"resource://nimbus/ExperimentAPI.jsm"
);
return new ExperimentFeature("aboutwelcome");
});
XPCOMUtils.defineLazyGetter(this, "tippyTopProvider", () =>
(async () => {
const provider = new TippyTopProvider();
@ -216,7 +222,7 @@ class AboutWelcomeChild extends JSWindowActorChild {
ExperimentAPI.getExperimentMetaData({
featureId: "aboutwelcome",
}) || {};
let featureConfig = NimbusFeatures.aboutwelcome.getValue() || {};
let featureConfig = aboutWelcomeFeature.getValue() || {};
if (experimentMetadata?.slug) {
log.debug(

Просмотреть файл

@ -14,8 +14,11 @@ const { actionTypes: at, actionCreators: ac } = ChromeUtils.import(
const HTML_NS = "http://www.w3.org/1999/xhtml";
const PREFERENCES_LOADED_EVENT = "home-pane-loaded";
XPCOMUtils.defineLazyModuleGetters(this, {
NimbusFeatures: "resource://nimbus/ExperimentAPI.jsm",
XPCOMUtils.defineLazyGetter(this, "aboutNewTabFeature", () => {
const { ExperimentFeature } = ChromeUtils.import(
"resource://nimbus/ExperimentAPI.jsm"
);
return new ExperimentFeature("newtab");
});
// These "section" objects are formatted in a way to be similar to the ones from
@ -148,7 +151,7 @@ this.AboutPreferences = class AboutPreferences {
sections = this.handleDiscoverySettings(sections);
}
const featureConfig = NimbusFeatures.newtab.getValue() || {};
const featureConfig = aboutNewTabFeature.getValue() || {};
this.renderPreferences(window, [
...PREFS_BEFORE_SECTIONS(featureConfig),

Просмотреть файл

@ -14,13 +14,31 @@ const { Prefs } = ChromeUtils.import(
);
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetters(this, {
AppConstants: "resource://gre/modules/AppConstants.jsm",
NimbusFeatures: "resource://nimbus/ExperimentAPI.jsm",
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
Region: "resource://gre/modules/Region.jsm",
ChromeUtils.defineModuleGetter(
this,
"PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"AppConstants",
"resource://gre/modules/AppConstants.jsm"
);
XPCOMUtils.defineLazyGetter(this, "aboutNewTabFeature", () => {
const { ExperimentFeature } = ChromeUtils.import(
"resource://nimbus/ExperimentAPI.jsm"
);
return new ExperimentFeature("newtab");
});
ChromeUtils.defineModuleGetter(
this,
"Region",
"resource://gre/modules/Region.jsm"
);
this.PrefsFeed = class PrefsFeed {
constructor(prefMap) {
this._prefMap = prefMap;
@ -65,7 +83,7 @@ this.PrefsFeed = class PrefsFeed {
* Handler for when experiment data updates.
*/
onExperimentUpdated(event, reason) {
const value = NimbusFeatures.newtab.getValue() || {};
const value = aboutNewTabFeature.getValue() || {};
this.store.dispatch(
ac.BroadcastToContent({
type: at.PREF_CHANGED,
@ -79,7 +97,7 @@ this.PrefsFeed = class PrefsFeed {
init() {
this._prefs.observeBranch(this);
NimbusFeatures.newtab.onUpdate(this.onExperimentUpdated);
aboutNewTabFeature.onUpdate(this.onExperimentUpdated);
this._storage = this.store.dbStorage.getDbTable("sectionPrefs");
@ -154,7 +172,7 @@ this.PrefsFeed = class PrefsFeed {
});
// Add experiment values and default values
values.featureConfig = NimbusFeatures.newtab.getValue() || {};
values.featureConfig = aboutNewTabFeature.getValue() || {};
this._setBoolPref(values, "logowordmark.alwaysVisible", false);
this._setBoolPref(values, "feeds.section.topstories", false);
this._setBoolPref(values, "discoverystream.enabled", false);
@ -191,7 +209,7 @@ this.PrefsFeed = class PrefsFeed {
removeListeners() {
this._prefs.ignoreBranch(this);
NimbusFeatures.newtab.off(this.onExperimentUpdated);
aboutNewTabFeature.off(this.onExperimentUpdated);
if (this.geo === "") {
Services.obs.removeObserver(this, Region.REGION_TOPIC);
}

Просмотреть файл

@ -17,9 +17,18 @@ const { getDefaultOptions } = ChromeUtils.import(
"resource://activity-stream/lib/ActivityStreamStorage.jsm"
);
XPCOMUtils.defineLazyModuleGetters(this, {
NimbusFeatures: "resource://nimbus/ExperimentAPI.jsm",
PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
ChromeUtils.defineModuleGetter(
this,
"PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm"
);
XPCOMUtils.defineLazyGetter(this, "aboutNewTabFeature", () => {
const { ExperimentFeature } = ChromeUtils.import(
"resource://nimbus/ExperimentAPI.jsm"
);
return new ExperimentFeature("newtab");
});
/*
@ -205,7 +214,7 @@ const SectionsManager = {
sections: new Map(),
async init(prefs = {}, storage) {
this._storage = storage;
const featureConfig = NimbusFeatures.newtab.getValue() || {};
const featureConfig = aboutNewTabFeature.getValue() || {};
for (const feedPrefName of Object.keys(BUILT_IN_SECTIONS(featureConfig))) {
const optionsPrefName = `${feedPrefName}.options`;
@ -254,7 +263,7 @@ const SectionsManager = {
async addBuiltInSection(feedPrefName, optionsPrefValue = "{}") {
let options;
let storedPrefs;
const featureConfig = NimbusFeatures.newtab.getValue() || {};
const featureConfig = aboutNewTabFeature.getValue() || {};
try {
options = JSON.parse(optionsPrefValue);
} catch (e) {

Просмотреть файл

@ -27,7 +27,7 @@ ChromeUtils.defineModuleGetter(
);
ChromeUtils.defineModuleGetter(
this,
"NimbusFeatures",
"ExperimentFeature",
"resource://nimbus/ExperimentAPI.jsm"
);
@ -134,9 +134,10 @@ var gSearchPane = {
this._updateSuggestionCheckboxes();
this._showAddEngineButton();
this._updateQuickSuggest = this._updateQuickSuggest.bind(this);
NimbusFeatures.urlbar.onUpdate(this._updateQuickSuggest);
this.urlbarExperimentFeature = new ExperimentFeature("urlbar");
this.urlbarExperimentFeature.onUpdate(this._updateQuickSuggest);
window.addEventListener("unload", () => {
NimbusFeatures.urlbar.off(this._updateQuickSuggest);
this.urlbarExperimentFeature.off(this._updateQuickSuggest);
});
this._updateQuickSuggest(true);
},
@ -248,7 +249,7 @@ var gSearchPane = {
let container = document.getElementById("showQuickSuggestContainer");
let desc = document.getElementById("searchSuggestionsDesc");
if (!NimbusFeatures.urlbar.getValue().quickSuggestEnabled) {
if (!this.urlbarExperimentFeature.getValue().quickSuggestEnabled) {
// The experiment is not enabled. This is the default, so to avoid
// accidentally messing anything up, only modify the doc if we're being
// called due to a change in the experiment enabled status.

Просмотреть файл

@ -13,7 +13,6 @@ const { XPCOMUtils } = ChromeUtils.import(
XPCOMUtils.defineLazyModuleGetters(this, {
CONTEXTUAL_SERVICES_PING_TYPES:
"resource:///modules/PartnerLinkAttribution.jsm",
NimbusFeatures: "resource://nimbus/ExperimentAPI.jsm",
PartnerLinkAttribution: "resource:///modules/PartnerLinkAttribution.jsm",
Services: "resource://gre/modules/Services.jsm",
UrlbarPrefs: "resource:///modules/UrlbarPrefs.jsm",
@ -23,6 +22,13 @@ XPCOMUtils.defineLazyModuleGetters(this, {
UrlbarUtils: "resource:///modules/UrlbarUtils.jsm",
});
XPCOMUtils.defineLazyGetter(this, "urlbarExperimentFeature", () => {
const { ExperimentFeature } = ChromeUtils.import(
"resource://nimbus/ExperimentAPI.jsm"
);
return new ExperimentFeature("urlbar");
});
// These prefs are relative to the `browser.urlbar` branch.
const SUGGEST_PREF = "suggest.quicksuggest";
@ -45,7 +51,7 @@ class ProviderQuickSuggest extends UrlbarProvider {
super(...args);
this._updateExperimentState();
UrlbarPrefs.addObserver(this);
NimbusFeatures.urlbar.onUpdate(this._updateExperimentState);
urlbarExperimentFeature.onUpdate(this._updateExperimentState);
}
/**
@ -96,7 +102,7 @@ class ProviderQuickSuggest extends UrlbarProvider {
return (
queryContext.trimmedSearchString &&
!queryContext.searchMode &&
NimbusFeatures.urlbar.getValue().quickSuggestEnabled &&
urlbarExperimentFeature.getValue().quickSuggestEnabled &&
UrlbarPrefs.get(SUGGEST_PREF) &&
UrlbarPrefs.get("suggest.searches") &&
UrlbarPrefs.get("browser.search.suggest.enabled") &&
@ -277,12 +283,12 @@ class ProviderQuickSuggest extends UrlbarProvider {
_updateExperimentState() {
Services.telemetry.setEventRecordingEnabled(
TELEMETRY_EVENT_CATEGORY,
NimbusFeatures.urlbar.getValue().quickSuggestEnabled
urlbarExperimentFeature.getValue().quickSuggestEnabled
);
// QuickSuggest is only loaded by the UrlBar on it's first query, however
// there is work it can preload when idle instead of starting it on user
// input. Referencing it here will trigger its import and init.
if (NimbusFeatures.urlbar.getValue().quickSuggestEnabled) {
if (urlbarExperimentFeature.getValue().quickSuggestEnabled) {
UrlbarQuickSuggest; // eslint-disable-line no-unused-expressions
}
}

Просмотреть файл

@ -15,7 +15,7 @@ const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetters(this, {
RemoteSettings: "resource://services-settings/remote-settings.js",
UrlbarPrefs: "resource:///modules/UrlbarPrefs.jsm",
NimbusFeatures: "resource://nimbus/ExperimentAPI.jsm",
ExperimentFeature: "resource://nimbus/ExperimentAPI.jsm",
});
XPCOMUtils.defineLazyGlobalGetters(this, ["TextDecoder"]);
@ -53,13 +53,14 @@ class Suggestions {
this._initPromise = Promise.resolve();
this._rs = RemoteSettings(RS_COLLECTION);
this.onFeatureUpdate = this.onFeatureUpdate.bind(this);
if (NimbusFeatures.urlbar.getValue().quickSuggestEnabled) {
this.urlbarExperimentFeature = new ExperimentFeature("urlbar");
if (this.urlbarExperimentFeature.getValue().quickSuggestEnabled) {
this._initPromise = new Promise(resolve => (this._initResolve = resolve));
Services.tm.idleDispatchToMainThread(
this._setupRemoteSettings.bind(this)
);
} else {
NimbusFeatures.urlbar.onUpdate(this.onFeatureUpdate);
this.urlbarExperimentFeature.onUpdate(this.onFeatureUpdate);
}
return this._initPromise;
}
@ -154,7 +155,7 @@ class Suggestions {
* Called if a Urlbar Experiment Feature is changed.
*/
onFeatureUpdate() {
if (NimbusFeatures.urlbar.getValue().quickSuggestEnabled) {
if (this.urlbarExperimentFeature.getValue().quickSuggestEnabled) {
this._setupRemoteSettings();
}
}

Просмотреть файл

@ -5,4 +5,12 @@ About
------
Nimbus is an automated experimentation system.
Our API docs are hosted with the `Experimenter Docs <https://mozilla.github.io/experimenter-docs/desktop-feature-api>`_.
Learn more
---------------------
.. toctree::
:maxdepth: 2
integration.md
testing.md

Просмотреть файл

@ -0,0 +1,80 @@
# Nimbus Integration and Migration Guide for Desktop Front-End Experiments
This guide will help you integrate `ExperimentAPI.jsm` in your Desktop front-end code to run Nimbus experiments, while still being able to use preferences for default values and local development.
In order to use `ExperimentAPI.jsm` your code must be able to import `jsm`s in the parent process or a privileged child process.
## Register a new feature
A feature is just some area of code instrumented for experiments – it can be as small as a single function or as complex as a whole about: page. You need to choose an identifier for your feature (e.g. "aboutnewtab").
```javascript
// In ExperimentAPI.jsm
const MANIFEST = {
// Our feature name
aboutwelcome: {
description: "The about:welcome page",
// Control if the feature is on or off
enabledFallbackPref: "browser.aboutwelcome.enabled",
variables: {
// Additional (optional) values that we can control
// The name of these variables is up to you
skipFocus: {
type: "boolean",
fallbackPref: "browser.aboutwelcome.skipFocus",
},
},
},
};
// In firefox.js
pref("browser.aboutwelcome.enable", true);
pref("skipFocus", false);
```
> By setting fallback preferences for Nimbus features, you will be able to still run Normandy roll-outs and experiments while you are partially migrated. We do not recommend running Nimbus and Normandy experiments on the same feature/preference simultaneously.
## How to use an Experiment Feature
Import `ExperimentFeature` from `ExperimentAPI.jsm` and instantiate an instance
```jsx
XPCOMUtils.defineLazyGetter(this, "feature", () => {
const { ExperimentFeature } = ChromeUtils.import(
"resource://nimbus/ExperimentAPI.jsm"
);
// Here we use the same name we defined in the MANIFEST
return new ExperimentFeature("aboutwelcome");
});
```
Access feature values:
```jsx
if (feature.isEnabled() {
// Do something!
// This is controllbed by the `enabledFallbackPref` defined in the MANIFEST
}
// props: { skipFocus: boolean }
const props = feature.getValue();
renderSomeUI(props);
```
Defaults values inline:
```jsx
feature.isEnabled({ defaultValue: true });
const { skipFocus } = feature.getValue() || {};
```
Listen to changes:
```jsx
// Listen to changes, including to fallback prefs.
feature.on(() => {
updateUI(feature.getValue());
});
```

Просмотреть файл

@ -0,0 +1,61 @@
# Nimbus Testing Helpers
In order to make testing easier we created some helpers that can be accessed by including
```js
const { ExperimentFakes } = ChromeUtils.import(
"resource://testing-common/NimbusTestUtils.jsm"
);
```
## Testing your feature integrating with Nimbus
1. You need to create a recipe
```js
let recipe = ExperimentFakes.recipe("my-cool-experiment", {
branches: [
{
slug: "treatment-branch",
ratio: 1,
feature: {
featureId: "<YOUR FEATURE>",
// The feature is on
enabled: true,
// If you defined `variables` in the MANIFEST
// the `value` should match that schema
value: null,
},
},
],
bucketConfig: {
start: 0,
// Ensure 100% enrollment
count: 10000,
total: 10000,
namespace: "my-mochitest",
randomizationUnit: "normandy_id",
},
});
```
2. Now with the newly created recipe you want the test to enroll in the experiment
```js
let {
enrollmentPromise,
doExperimentCleanup,
} = ExperimentFakes.enrollmentHelper(recipe);
// Await for enrollment to complete
await enrollmentPromise;
// Now you can assume the feature is enabled so you can
// test and that it's doing the right thing
// Assert.ok(It works!)
// Finishing up
await doExperimentCleanup();
```

Просмотреть файл

@ -15,6 +15,13 @@ const LoginInfo = new Components.Constructor(
"init"
);
XPCOMUtils.defineLazyGetter(this, "autocompleteFeature", () => {
const { ExperimentFeature } = ChromeUtils.import(
"resource://nimbus/ExperimentAPI.jsm"
);
return new ExperimentFeature("password-autocomplete");
});
XPCOMUtils.defineLazyGetter(this, "LoginRelatedRealmsParent", () => {
const { LoginRelatedRealmsParent } = ChromeUtils.import(
"resource://gre/modules/LoginRelatedRealms.jsm"
@ -26,7 +33,6 @@ XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
XPCOMUtils.defineLazyModuleGetters(this, {
ChromeMigrationUtils: "resource:///modules/ChromeMigrationUtils.jsm",
NimbusFeatures: "resource://nimbus/ExperimentAPI.jsm",
LoginHelper: "resource://gre/modules/LoginHelper.jsm",
MigrationUtils: "resource:///modules/MigrationUtils.jsm",
PasswordGenerator: "resource://gre/modules/PasswordGenerator.jsm",
@ -335,8 +341,7 @@ class LoginManagerParent extends JSWindowActorParent {
const profiles = await migrator.getSourceProfiles();
if (
profiles.length == 1 &&
NimbusFeatures["password-autocomplete"].getValue()
?.directMigrateSingleProfile
autocompleteFeature.getValue()?.directMigrateSingleProfile
) {
const loginAdded = new Promise(resolve => {
const obs = (subject, topic, data) => {