Bug 1383338 - fetch and run shield studies soon after UI startup r=Gijs,mythmon
authorRobert Helmer <rhelmer@mozilla.com>
Wed, 26 Jul 2017 10:10:47 -0700
changeset 422246 02874c4ecef40f64645fee4eb62f2fa0a1323df9
parent 422245 691af1e600e06fcee33478a39d03c43a24c5d2e8
child 422247 62ccb664f57db3d7992a564af17e6b6cc3e70e6c
push id1517
push userjlorenzo@mozilla.com
push dateThu, 14 Sep 2017 16:50:54 +0000
treeherdermozilla-release@3b41fd564418 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersGijs, mythmon
bugs1383338
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1383338 - fetch and run shield studies soon after UI startup r=Gijs,mythmon MozReview-Commit-ID: CWRQmwKplII
browser/extensions/shield-recipe-client/install.rdf.in
browser/extensions/shield-recipe-client/lib/ClientEnvironment.jsm
browser/extensions/shield-recipe-client/lib/RecipeRunner.jsm
browser/extensions/shield-recipe-client/lib/ShieldRecipeClient.jsm
browser/extensions/shield-recipe-client/test/browser/browser_ClientEnvironment.js
browser/extensions/shield-recipe-client/test/browser/browser_RecipeRunner.js
--- a/browser/extensions/shield-recipe-client/install.rdf.in
+++ b/browser/extensions/shield-recipe-client/install.rdf.in
@@ -3,17 +3,17 @@
 #filter substitution
 
 <RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#">
   <Description about="urn:mozilla:install-manifest">
     <em:id>shield-recipe-client@mozilla.org</em:id>
     <em:type>2</em:type>
     <em:bootstrap>true</em:bootstrap>
     <em:unpack>false</em:unpack>
-    <em:version>55</em:version>
+    <em:version>55.1</em:version>
     <em:name>Shield Recipe Client</em:name>
     <em:description>Client to download and run recipes for SHIELD, Heartbeat, etc.</em:description>
     <em:multiprocessCompatible>true</em:multiprocessCompatible>
 
     <em:targetApplication>
       <Description>
         <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
         <em:minVersion>@MOZ_APP_VERSION@</em:minVersion>
--- a/browser/extensions/shield-recipe-client/lib/ClientEnvironment.jsm
+++ b/browser/extensions/shield-recipe-client/lib/ClientEnvironment.jsm
@@ -192,11 +192,15 @@ this.ClientEnvironment = {
         } else {
           names.active.push(experiment.name);
         }
       }
 
       return names;
     });
 
+    XPCOMUtils.defineLazyGetter(environment, "isFirstRun", () => {
+      return Preferences.get("extensions.shield-recipe-client.first_run");
+    });
+
     return environment;
   },
 };
--- a/browser/extensions/shield-recipe-client/lib/RecipeRunner.jsm
+++ b/browser/extensions/shield-recipe-client/lib/RecipeRunner.jsm
@@ -33,28 +33,53 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 Cu.importGlobalProperties(["fetch"]);
 
 this.EXPORTED_SYMBOLS = ["RecipeRunner"];
 
 const log = LogManager.getLogger("recipe-runner");
 const prefs = Services.prefs.getBranch("extensions.shield-recipe-client.");
 const TIMER_NAME = "recipe-client-addon-run";
 const RUN_INTERVAL_PREF = "run_interval_seconds";
+const FIRST_RUN_PREF = "first_run";
+const UI_AVAILABLE_NOTIFICATION = "sessionstore-windows-restored";
+const SHIELD_INIT_NOTIFICATION = "shield-init-complete";
 
 this.RecipeRunner = {
   init() {
     if (!this.checkPrefs()) {
       return;
     }
 
     if (prefs.getBoolPref("dev_mode")) {
       // Run right now in dev mode
       this.run();
     }
 
+    if (prefs.getBoolPref(FIRST_RUN_PREF)) {
+      // Run once immediately after the UI is available. Do this before adding the
+      // timer so we can't end up racing it.
+      const observer = {
+        observe: (subject, topic, data) => {
+          Services.obs.removeObserver(observer, UI_AVAILABLE_NOTIFICATION);
+
+          this.run();
+          this.registerTimer();
+          prefs.setBoolPref(FIRST_RUN_PREF, false);
+
+          Services.obs.notifyObservers(null, SHIELD_INIT_NOTIFICATION);
+        },
+      };
+      Services.obs.addObserver(observer, UI_AVAILABLE_NOTIFICATION);
+      CleanupManager.addCleanupHandler(() => Services.obs.removeObserver(observer, UI_AVAILABLE_NOTIFICATION));
+    } else {
+      this.registerTimer();
+    }
+  },
+
+  registerTimer() {
     this.updateRunInterval();
     CleanupManager.addCleanupHandler(() => timerManager.unregisterTimer(TIMER_NAME));
 
     // Watch for the run interval to change, and re-register the timer with the new value
     prefs.addObserver(RUN_INTERVAL_PREF, this);
     CleanupManager.addCleanupHandler(() => prefs.removeObserver(RUN_INTERVAL_PREF, this));
   },
 
--- a/browser/extensions/shield-recipe-client/lib/ShieldRecipeClient.jsm
+++ b/browser/extensions/shield-recipe-client/lib/ShieldRecipeClient.jsm
@@ -34,16 +34,17 @@ const PREF_BRANCH = "extensions.shield-r
 const DEFAULT_PREFS = {
   api_url: "https://normandy.cdn.mozilla.net/api/v1",
   dev_mode: false,
   enabled: true,
   startup_delay_seconds: 300,
   "logging.level": Log.Level.Warn,
   user_id: "",
   run_interval_seconds: 86400, // 24 hours
+  first_run: true,
 };
 const PREF_DEV_MODE = "extensions.shield-recipe-client.dev_mode";
 const PREF_SELF_SUPPORT_ENABLED = "browser.selfsupport.enabled";
 const PREF_LOGGING_LEVEL = PREF_BRANCH + "logging.level";
 
 let log = null;
 
 /**
--- a/browser/extensions/shield-recipe-client/test/browser/browser_ClientEnvironment.js
+++ b/browser/extensions/shield-recipe-client/test/browser/browser_ClientEnvironment.js
@@ -107,8 +107,18 @@ add_task(async function testExperiments(
   Assert.deepEqual(
     experiments.expired,
     ["expired"],
     "experiments.expired returns all expired experiment names",
   );
 
   getAll.restore();
 });
+
+add_task(async function isFirstRun() {
+  let environment = ClientEnvironment.getEnvironment();
+
+  // isFirstRun is read from a preference
+  await SpecialPowers.pushPrefEnv({set: [["extensions.shield-recipe-client.first_run", true]]});
+  environment = ClientEnvironment.getEnvironment();
+  ok(environment.isFirstRun, "isFirstRun is read from preferences");
+});
+
--- a/browser/extensions/shield-recipe-client/test/browser/browser_RecipeRunner.js
+++ b/browser/extensions/shield-recipe-client/test/browser/browser_RecipeRunner.js
@@ -195,28 +195,40 @@ add_task(withMockNormandyApi(async funct
 }));
 
 add_task(async function testStartup() {
   const runStub = sinon.stub(RecipeRunner, "run");
   const addCleanupHandlerStub = sinon.stub(CleanupManager, "addCleanupHandler");
   const updateRunIntervalStub = sinon.stub(RecipeRunner, "updateRunInterval");
 
   // in dev mode
-  await SpecialPowers.pushPrefEnv({set: [["extensions.shield-recipe-client.dev_mode", true]]});
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      ["extensions.shield-recipe-client.dev_mode", true],
+      ["extensions.shield-recipe-client.first_run", false],
+    ],
+  });
+
   RecipeRunner.init();
   ok(runStub.called, "RecipeRunner.run is called immediately when in dev mode");
   ok(addCleanupHandlerStub.called, "A cleanup function is registered when in dev mode");
   ok(updateRunIntervalStub.called, "A timer is registered when in dev mode");
 
   runStub.reset();
   addCleanupHandlerStub.reset();
   updateRunIntervalStub.reset();
 
   // not in dev mode
-  await SpecialPowers.pushPrefEnv({set: [["extensions.shield-recipe-client.dev_mode", false]]});
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      ["extensions.shield-recipe-client.dev_mode", false],
+      ["extensions.shield-recipe-client.first_run", false],
+    ],
+  });
+
   RecipeRunner.init();
   ok(!runStub.called, "RecipeRunner.run is not called immediately when not in dev mode");
   ok(addCleanupHandlerStub.called, "A cleanup function is registered when not in dev mode");
   ok(updateRunIntervalStub.called, "A timer is registered when not in dev mode");
 
   runStub.restore();
   addCleanupHandlerStub.restore();
   updateRunIntervalStub.restore();