Bug 1462725 - Add study expiration and testing override pref; r=rhelmer
The study will shutdown on its own after a specified period of time.
The testing override pref allows for changing the study duration from the default, which is 4 weeks.
MozReview-Commit-ID: GEWpCaJX3Mr
--- a/browser/modules/SavantShieldStudy.jsm
+++ b/browser/modules/SavantShieldStudy.jsm
@@ -26,25 +26,28 @@ XPCOMUtils.defineLazyGetter(this, "log",
return new ConsoleAPI(consoleOptions);
});
class SavantShieldStudyClass {
constructor() {
this.STUDY_PREF = "shield.savant.enabled";
this.STUDY_TELEMETRY_CATEGORY = "savant";
this.ALWAYS_PRIVATE_BROWSING_PREF = "browser.privatebrowsing.autostart";
+ this.STUDY_DURATION_OVERRIDE_PREF = "shield.savant.duration_override";
+ this.STUDY_EXPIRATION_DATE_PREF = "shield.savant.expiration_date";
+ // ms = 'x' weeks * 7 days/week * 24 hours/day * 60 minutes/hour
+ // * 60 seconds/minute * 1000 milliseconds/second
+ this.DEFAULT_STUDY_DURATION_MS = 4 * 7 * 24 * 60 * 60 * 1000;
}
async init() {
this.TELEMETRY_CLIENT_ID = await ClientID.getClientID();
this.TelemetryEvents = new TelemetryEvents(this.STUDY_TELEMETRY_CATEGORY,
this.TELEMETRY_CLIENT_ID);
- // TODO check expiration, add study duration override pref
-
// check the pref in case Normandy flipped it on before we could add the pref listener
this.shouldCollect = Services.prefs.getBoolPref(this.STUDY_PREF);
if (this.shouldCollect) {
this.startupStudy();
}
Services.prefs.addObserver(this.STUDY_PREF, this);
}
@@ -57,53 +60,115 @@ class SavantShieldStudyClass {
} else {
// The pref has been turned off
this.endStudy("study-disabled");
}
}
}
startupStudy() {
+ // enable before any possible calls to endStudy, since it sends an 'end_study' event
+ this.TelemetryEvents.enableCollection();
+
if (!this.isEligible()) {
this.endStudy("ineligible");
return;
}
- this.TelemetryEvents.enableCollection();
+ this.initStudyDuration();
+
+ if (this.isStudyExpired()) {
+ log.debug("Study expired in between this and the previous session.");
+ this.endStudy("expired");
+ }
}
isEligible() {
const isAlwaysPrivateBrowsing = Services.prefs.getBoolPref(this.ALWAYS_PRIVATE_BROWSING_PREF);
if (isAlwaysPrivateBrowsing) {
return false;
}
return true;
}
+ initStudyDuration() {
+ if (Services.prefs.getStringPref(this.STUDY_EXPIRATION_DATE_PREF, "")) {
+ return;
+ }
+ Services.prefs.setStringPref(
+ this.STUDY_EXPIRATION_DATE_PREF,
+ this.getExpirationDateString()
+ );
+ }
+
+ getDurationFromPref() {
+ return Services.prefs.getIntPref(this.STUDY_DURATION_OVERRIDE_PREF, 0);
+ }
+
+ getExpirationDateString() {
+ const now = Date.now();
+ const studyDurationInMs =
+ this.getDurationFromPref()
+ || this.DEFAULT_STUDY_DURATION_MS;
+ const expirationDateInt = now + studyDurationInMs;
+ return new Date(expirationDateInt).toISOString();
+ }
+
+ isStudyExpired() {
+ const expirationDateInt =
+ Date.parse(Services.prefs.getStringPref(
+ this.STUDY_EXPIRATION_DATE_PREF,
+ this.getExpirationDateString()
+ ));
+
+ if (isNaN(expirationDateInt)) {
+ log.error(
+ `The value for the preference ${this.STUDY_EXPIRATION_DATE_PREF} is invalid.`
+ );
+ return false;
+ }
+
+ if (Date.now() > expirationDateInt) {
+ return true;
+ }
+ return false;
+ }
+
sendEvent(method, object, value, extra) {
this.TelemetryEvents.sendEvent(method, object, value, extra);
}
endStudy(reason) {
log.debug(`Ending the study due to reason: ${ reason }`);
- this.TelemetryEvents.disableCollection();
+ const isStudyEnding = true;
// Services.telemetry.recordEvent(this.STUDY_TELEMETRY_CATEGORY, "end_study", reason);
- this.uninit();
- // Study pref needs to persist between restarts, so only reset on endStudy
+ this.TelemetryEvents.disableCollection();
+ this.uninit(isStudyEnding);
+ // These prefs needs to persist between restarts, so only reset on endStudy
Services.prefs.clearUserPref(this.STUDY_PREF);
+ Services.prefs.clearUserPref(this.STUDY_EXPIRATION_DATE_PREF);
}
// Called on every Firefox shutdown and endStudy
- uninit() {
- // TODO: clear study expiration override pref and remove its listener
+ uninit(isStudyEnding = false) {
+ // if just shutting down, check for expiration, so the endStudy event can
+ // be sent along with this session's main ping.
+ if (!isStudyEnding && this.isStudyExpired()) {
+ log.debug("Study expired during this session.");
+ this.endStudy("expired");
+ return;
+ }
+
Services.prefs.removeObserver(this.ALWAYS_PRIVATE_BROWSING_PREF, this);
Services.prefs.removeObserver(this.STUDY_PREF, this);
+ Services.prefs.removeObserver(this.STUDY_DURATION_OVERRIDE_PREF, this);
Services.prefs.clearUserPref(PREF_LOG_LEVEL);
+ Services.prefs.clearUserPref(this.STUDY_DURATION_OVERRIDE_PREF);
}
async getFlowID(str) {
return this.TelemetryEvents.getFlowID(str);
}
}
const SavantShieldStudy = new SavantShieldStudyClass();