Bug 1367813 - 1) Add telemetry for prefs.js not existing or being corrupted, and the presence of a user.js file. 2) Rename and change the nsIPrefService.readUserPrefs API. The new API reads user prefs from a file but doesn't remember that location or save changed preferences to that location. r=milan data-r=rweiss
authorBenjamin Smedberg <benjamin@smedbergs.us>
Wed, 21 Jun 2017 15:26:10 -0400
changeset 415392 66aba634f34d7d62d661e958f6eacd6879343a57
parent 415391 913a0f2ce37f79a2e2ff21e798fb4c30b7819e2b
child 415393 17153099b556f1aefcf9c48582f34a68f9528e9a
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmilan
bugs1367813
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 1367813 - 1) Add telemetry for prefs.js not existing or being corrupted, and the presence of a user.js file. 2) Rename and change the nsIPrefService.readUserPrefs API. The new API reads user prefs from a file but doesn't remember that location or save changed preferences to that location. r=milan data-r=rweiss MozReview-Commit-ID: FD5npJlB24W
modules/libpref/Preferences.cpp
modules/libpref/Preferences.h
modules/libpref/nsIPrefService.idl
modules/libpref/test/unit/head_libPrefs.js
modules/libpref/test/unit/test_bug506224.js
modules/libpref/test/unit/test_dirtyPrefs.js
modules/libpref/test/unit/test_extprefs.js
modules/libpref/test/unit/test_libPrefs.js
modules/libpref/test/unit/test_stickyprefs.js
services/sync/tests/unit/test_prefs_store.js
toolkit/components/telemetry/Scalars.yaml
--- a/modules/libpref/Preferences.cpp
+++ b/modules/libpref/Preferences.cpp
@@ -6,16 +6,17 @@
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/PContent.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/HashFunctions.h"
 #include "mozilla/ServoStyleSet.h"
+#include "mozilla/Telemetry.h"
 #include "mozilla/UniquePtrExtensions.h"
 
 #include "nsXULAppAPI.h"
 
 #include "mozilla/Preferences.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsDataHashtable.h"
 #include "nsDirectoryServiceDefs.h"
@@ -49,16 +50,18 @@
 #include "nsZipArchive.h"
 
 #include "nsTArray.h"
 #include "nsRefPtrHashtable.h"
 #include "nsIMemoryReporter.h"
 #include "nsThreadUtils.h"
 #include "GeckoProfiler.h"
 
+using namespace mozilla;
+
 #ifdef DEBUG
 #define ENSURE_MAIN_PROCESS(message, pref) do {                                \
   if (MOZ_UNLIKELY(!XRE_IsParentProcess())) {                                  \
     nsPrintfCString msg("ENSURE_MAIN_PROCESS failed. %s %s", message, pref);   \
     NS_WARNING(msg.get());                                                     \
     return NS_ERROR_NOT_AVAILABLE;                                             \
   }                                                                            \
 } while (0);
@@ -86,16 +89,22 @@ namespace mozilla {
 
 // Definitions
 #define INITIAL_PREF_FILES 10
 static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID);
 
 void
 Preferences::DirtyCallback()
 {
+  if (!XRE_IsParentProcess()) {
+    // TODO: this should really assert because you can't set prefs in a
+    // content process. But so much code currently does this that we just
+    // ignore it for now.
+    return;
+  }
   if (gHashTable && sPreferences && !sPreferences->mDirty) {
     sPreferences->mDirty = true;
   }
 }
 
 // Prototypes
 static nsresult openPrefFile(nsIFile* aFile);
 static nsresult pref_InitInitialObjects(void);
@@ -760,17 +769,30 @@ Preferences::Init()
   return(rv);
 }
 
 // static
 nsresult
 Preferences::ResetAndReadUserPrefs()
 {
   sPreferences->ResetUserPrefs();
-  return sPreferences->ReadUserPrefs(nullptr);
+
+  MOZ_ASSERT(!sPreferences->mCurrentFile, "Should only initialize prefs once");
+
+  nsresult rv = sPreferences->UseDefaultPrefFile();
+  sPreferences->UseUserPrefFile();
+
+  // Migrate the old prerelease telemetry pref
+  if (!Preferences::GetBool(kOldTelemetryPref, true)) {
+    Preferences::SetBool(kTelemetryPref, false);
+    Preferences::ClearUser(kOldTelemetryPref);
+  }
+
+  sPreferences->NotifyServiceObservers(NS_PREFSERVICE_READ_TOPIC_ID);
+  return rv;
 }
 
 NS_IMETHODIMP
 Preferences::Observe(nsISupports *aSubject, const char *aTopic,
                      const char16_t *someData)
 {
   if (MOZ_UNLIKELY(!XRE_IsParentProcess())) {
     return NS_ERROR_NOT_AVAILABLE;
@@ -795,49 +817,29 @@ Preferences::Observe(nsISupports *aSubje
     // from the suspended state, we save preferences before suspending.
     rv = SavePrefFileBlocking();
   }
   return rv;
 }
 
 
 NS_IMETHODIMP
-Preferences::ReadUserPrefs(nsIFile *aFile)
+Preferences::ReadUserPrefsFromFile(nsIFile *aFile)
 {
   if (MOZ_UNLIKELY(!XRE_IsParentProcess())) {
     NS_ERROR("must load prefs from parent process");
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  nsresult rv;
-
-  if (nullptr == aFile) {
-    // We should not be re-reading the user preferences, but if we
-    // are going to try, make sure there are no outstanding saves
-    if (AllowOffMainThreadSave()) {
-      PreferencesWriter::Flush();
-    }
-
-    rv = UseDefaultPrefFile();
-    // A user pref file is optional.
-    // Ignore all errors related to it, so we retain 'rv' value :-|
-    (void) UseUserPrefFile();
-
-    // Migrate the old prerelease telemetry pref
-    if (!Preferences::GetBool(kOldTelemetryPref, true)) {
-      Preferences::SetBool(kTelemetryPref, false);
-      Preferences::ClearUser(kOldTelemetryPref);
-    }
-
-    NotifyServiceObservers(NS_PREFSERVICE_READ_TOPIC_ID);
-  } else {
-    rv = ReadAndOwnUserPrefFile(aFile);
+  if (!aFile) {
+    NS_ERROR("ReadUserPrefsFromFile requires a parameter");
+    return NS_ERROR_INVALID_ARG;
   }
 
-  return rv;
+  return openPrefFile(aFile);
 }
 
 NS_IMETHODIMP
 Preferences::ResetPrefs()
 {
   if (MOZ_UNLIKELY(!XRE_IsParentProcess())) {
     NS_ERROR("must reset prefs from parent process");
     return NS_ERROR_NOT_AVAILABLE;
@@ -1058,56 +1060,58 @@ Preferences::NotifyServiceObservers(cons
   observerService->NotifyObservers(subject, aTopic, nullptr);
   
   return NS_OK;
 }
 
 nsresult
 Preferences::UseDefaultPrefFile()
 {
-  nsCOMPtr<nsIFile> aFile;
-  nsresult rv = NS_GetSpecialDirectory(NS_APP_PREFS_50_FILE, getter_AddRefs(aFile));
+  nsCOMPtr<nsIFile> file;
+  nsresult rv = NS_GetSpecialDirectory(NS_APP_PREFS_50_FILE,
+                                       getter_AddRefs(file));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  mCurrentFile = file;
 
-  if (NS_SUCCEEDED(rv)) {
-    rv = ReadAndOwnUserPrefFile(aFile);
-    // Most likely cause of failure here is that the file didn't
-    // exist, so save a new one. mUserPrefReadFailed will be
-    // used to catch an error in actually reading the file.
-    if (NS_FAILED(rv)) {
-      if (NS_FAILED(SavePrefFileInternal(aFile, SaveMethod::Blocking)))
-        NS_ERROR("Failed to save new shared pref file");
-      else
-        rv = NS_OK;
-    }
+  rv = openPrefFile(file);
+  if (rv == NS_ERROR_FILE_NOT_FOUND) {
+    // this is a normal case for new users
+    Telemetry::ScalarSet(Telemetry::ScalarID::PREFERENCES_CREATED_NEW_USER_PREFS_FILE, true);
+    rv = NS_OK;
+  } else if (NS_FAILED(rv)) {
+    // Save a backup copy of the current (invalid) prefs file, since all prefs
+    // from the error line to the end of the file will be lost (bug 361102).
+    // TODO we should notify the user about it (bug 523725).
+    Telemetry::ScalarSet(Telemetry::ScalarID::PREFERENCES_PREFS_FILE_WAS_INVALID, true);
+    MakeBackupPrefFile(file);
   }
 
   return rv;
 }
 
-nsresult
+void
 Preferences::UseUserPrefFile()
 {
-  nsresult rv = NS_OK;
   nsCOMPtr<nsIFile> aFile;
-  nsDependentCString prefsDirProp(NS_APP_PREFS_50_DIR);
+  nsresult rv = NS_GetSpecialDirectory(NS_APP_PREFS_50_DIR,
+                                       getter_AddRefs(aFile));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
 
-  rv = NS_GetSpecialDirectory(prefsDirProp.get(), getter_AddRefs(aFile));
-  if (NS_SUCCEEDED(rv) && aFile) {
-    rv = aFile->AppendNative(NS_LITERAL_CSTRING("user.js"));
-    if (NS_SUCCEEDED(rv)) {
-      bool exists = false;
-      aFile->Exists(&exists);
-      if (exists) {
-        rv = openPrefFile(aFile);
-      } else {
-        rv = NS_ERROR_FILE_NOT_FOUND;
-      }
-    }
+  aFile->AppendNative(NS_LITERAL_CSTRING("user.js"));
+  rv = openPrefFile(aFile);
+  if (rv != NS_ERROR_FILE_NOT_FOUND) {
+    // If the file exists and was at least partially read, record that
+    // in telemetry as it may be a sign of pref injection.
+    Telemetry::ScalarSet(Telemetry::ScalarID::PREFERENCES_READ_USER_JS, true);
   }
-  return rv;
 }
 
 nsresult
 Preferences::MakeBackupPrefFile(nsIFile *aFile)
 {
   // Example: this copies "prefs.js" to "Invalidprefs.js" in the same directory.
   // "Invalidprefs.js" is removed if it exists, prior to making the copy.
   nsAutoString newFilename;
@@ -1126,50 +1130,16 @@ Preferences::MakeBackupPrefFile(nsIFile 
     NS_ENSURE_SUCCESS(rv, rv);
   }
   rv = aFile->CopyTo(nullptr, newFilename);
   NS_ENSURE_SUCCESS(rv, rv);
   return rv;
 }
 
 nsresult
-Preferences::ReadAndOwnUserPrefFile(nsIFile *aFile)
-{
-  NS_ENSURE_ARG(aFile);
-  
-  if (mCurrentFile == aFile)
-    return NS_OK;
-
-  // Since we're changing the pref file, we may have to make
-  // sure the outstanding writes are handled first.
-  if (AllowOffMainThreadSave()) {
-    PreferencesWriter::Flush();
-  }
-
-  mCurrentFile = aFile;
-
-  nsresult rv = NS_OK;
-  bool exists = false;
-  mCurrentFile->Exists(&exists);
-  if (exists) {
-    rv = openPrefFile(mCurrentFile);
-    if (NS_FAILED(rv)) {
-      // Save a backup copy of the current (invalid) prefs file, since all prefs
-      // from the error line to the end of the file will be lost (bug 361102).
-      // TODO we should notify the user about it (bug 523725).
-      MakeBackupPrefFile(mCurrentFile);
-    }
-  } else {
-    rv = NS_ERROR_FILE_NOT_FOUND;
-  }
-
-  return rv;
-}
-
-nsresult
 Preferences::SavePrefFileInternal(nsIFile *aFile, SaveMethod aSaveMethod)
 {
   if (MOZ_UNLIKELY(!XRE_IsParentProcess())) {
     NS_ERROR("must save pref file from parent process");
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   // We allow different behavior here when aFile argument is not null,
--- a/modules/libpref/Preferences.h
+++ b/modules/libpref/Preferences.h
@@ -437,19 +437,17 @@ protected:
   nsresult NotifyServiceObservers(const char *aSubject);
   /**
    * Reads the default pref file or, if that failed, try to save a new one.
    *
    * @return NS_OK if either action succeeded,
    *         or the error code related to the read attempt.
    */
   nsresult UseDefaultPrefFile();
-  nsresult UseUserPrefFile();
-  nsresult ReadAndOwnUserPrefFile(nsIFile *aFile);
-  nsresult ReadAndOwnSharedUserPrefFile(nsIFile *aFile);
+  void UseUserPrefFile();
   nsresult MakeBackupPrefFile(nsIFile *aFile);
 
   // Default pref file save can be blocking or not.
   enum class SaveMethod {
     Blocking,
     Asynchronous
   };
 
--- a/modules/libpref/nsIPrefService.idl
+++ b/modules/libpref/nsIPrefService.idl
@@ -26,32 +26,16 @@ interface nsIFile;
  *
  * @see nsIPrefBranch
  */
 
 [scriptable, uuid(1f84fd56-3956-40df-b86a-1ea01402ee96)]
 interface nsIPrefService : nsISupports
 {
   /**
-   * Called to read in the preferences specified in a user preference file.
-   *
-   * @param aFile The file to be read.
-   *
-   * @note
-   * If nullptr is passed in for the aFile parameter the default preferences
-   * file(s) [prefs.js, user.js] will be read and processed.
-   *
-   * @throws Error File failed to read or contained invalid data.
-   *
-   * @see savePrefFile
-   * @see nsIFile
-   */
-  void readUserPrefs(in nsIFile aFile);
-
-  /**
    * Called to completely flush and re-initialize the preferences system.
    *
    * @throws Error The preference service failed to restart correctly.
    */
   void resetPrefs();
 
   /**
    * Called to reset all preferences with user set values back to the
@@ -120,16 +104,27 @@ interface nsIPrefService : nsISupports
    */
   nsIPrefBranch getDefaultBranch(in string aPrefRoot);
 
   /**
    * The preference service is 'dirty' if there are changes to user preferences
    * that have not been written to disk
    */
   readonly attribute boolean dirty;
+
+  /**
+   * Read in the preferences specified in a user preference file. This method
+   * does not clear user preferences that were already set.
+   *
+   * @param aFile The file to be read.
+   *
+   * @throws Error File failed to read or contained invalid data.
+   * @note This method is intended for internal unit testing only!
+   */
+  void readUserPrefsFromFile(in nsIFile aFile);
 };
 
 %{C++
 
 #define NS_PREFSERVICE_CID                             \
   { /* {1cd91b88-1dd2-11b2-92e1-ed22ed298000} */       \
     0x91ca2441,                                        \
     0x050f,                                            \
--- a/modules/libpref/test/unit/head_libPrefs.js
+++ b/modules/libpref/test/unit/head_libPrefs.js
@@ -12,21 +12,20 @@ var Cu = Components.utils;
 function do_check_throws(f, result, stack)
 {
   if (!stack)
     stack = Components.stack.caller;
 
   try {
     f();
   } catch (exc) {
-    if (exc.result == result)
-      return;
-    do_throw("expected result " + result + ", caught " + exc, stack);
+    equal(exc.result, result, "Correct exception was thrown");
+    return;
   }
-  do_throw("expected result " + result + ", none thrown", stack);
+  ok(false, "expected result " + result + ", none thrown");
 }
 
 var dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
 
 // Register current test directory as provider for the profile directory.
 var provider = {
   getFile: function(prop, persistent) {
     persistent.value = true;
--- a/modules/libpref/test/unit/test_bug506224.js
+++ b/modules/libpref/test/unit/test_bug506224.js
@@ -17,13 +17,13 @@ function run_test() {
   do_check_eq(false, userprefs.prefHasUserValue(PREF_NAME));
 
   var file = do_get_profile();
   file.append("prefs.js");
   ps.savePrefFile(file);
 
   prefs.unlockPref(PREF_NAME);
   prefs.setCharPref(PREF_NAME, "test1");
-  ps.readUserPrefs(file);
+  ps.readUserPrefsFromFile(file);
 
   do_check_eq("test1", userprefs.getCharPref(PREF_NAME));
   do_check_eq(false, userprefs.prefHasUserValue(PREF_NAME));
 }
--- a/modules/libpref/test/unit/test_dirtyPrefs.js
+++ b/modules/libpref/test/unit/test_dirtyPrefs.js
@@ -15,27 +15,16 @@ function run_test() {
             getService(Ci.nsIPrefService);
 
   let defaultBranch = ps.getDefaultBranch("");
   let userBranch = ps.getBranch("");
 
   let prefFile = do_get_profile();
   prefFile.append("prefs.js");
 
-  // dirty flag only applies to the default pref file save, not all of them,
-  // so we need to set the default pref file first.  Chances are, the file
-  // does not exist, but we don't need it to - we just want to set the
-  // name/location to match
-  //
-  try {
-    ps.readUserPrefs(prefFile);
-  } catch (e) {
-    // we're fine if the file isn't there
-  }
-
   //**************************************************************************//
   // prefs are not dirty after a write
   ps.savePrefFile(null);
   do_check_false(ps.dirty);
 
   // set a new a user value, we should become dirty
   userBranch.setBoolPref("DirtyTest.new.bool", true);
   do_check_true(ps.dirty);
--- a/modules/libpref/test/unit/test_extprefs.js
+++ b/modules/libpref/test/unit/test_extprefs.js
@@ -45,17 +45,17 @@ function run_test() {
 
   do_check_throws(function() {
     ps.getBoolPref("testExtPref.bool");
   }, Cr.NS_ERROR_UNEXPECTED);
   do_check_throws(function() {
     ps.getBoolPref("testPref.bool1");
   }, Cr.NS_ERROR_UNEXPECTED);
   
-  ps.readUserPrefs(prefFile);
+  ps.readUserPrefsFromFile(prefFile);
 
   do_check_true(ps.getBoolPref("testPref.bool1"));
   ps.setBoolPref("testPref.bool1", false);
   do_check_false(ps.getBoolPref("testPref.bool1"));
   
   dirSvc.registerProvider(extProvider);
   Services.obs.notifyObservers(null, "load-extension-defaults");
 
--- a/modules/libpref/test/unit/test_libPrefs.js
+++ b/modules/libpref/test/unit/test_libPrefs.js
@@ -304,25 +304,26 @@ function run_test() {
   ps.setBoolPref("ReadPref.bool", true);
   ps.setIntPref("ReadPref.int", 230);
   ps.setCharPref("ReadPref.char", "hello");
 
   // save those prefs in a file
   let savePrefFile = do_get_cwd();
   savePrefFile.append("data");
   savePrefFile.append("savePref.js");
+
   if (savePrefFile.exists())
     savePrefFile.remove(false);
   savePrefFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
   ps.savePrefFile(savePrefFile);
   ps.resetPrefs();
 
   // load a preexisting pref file
   let prefFile = do_get_file("data/testPref.js");
-  ps.readUserPrefs(prefFile);
+  ps.readUserPrefsFromFile(prefFile);
 
   // former prefs should have been replaced/lost
   do_check_throws(function() {
     do_check_eq(pb.getBoolPref("ReadPref.bool"));}, Cr.NS_ERROR_UNEXPECTED);
   do_check_throws(function() {
     do_check_eq(pb.getIntPref("ReadPref.int"));}, Cr.NS_ERROR_UNEXPECTED);
   do_check_throws(function() {
     do_check_eq(pb.getCharPref("ReadPref.char"));}, Cr.NS_ERROR_UNEXPECTED);
@@ -332,17 +333,22 @@ function run_test() {
   do_check_eq(pb.getBoolPref("bool1"), true);
   do_check_eq(pb.getBoolPref("bool2"), false);
   do_check_eq(pb.getIntPref("int1"), 23);
   do_check_eq(pb.getIntPref("int2"), -1236);
   do_check_eq(pb.getCharPref("char1"), "_testPref");
   do_check_eq(pb.getCharPref("char2"), "älskar");
 
   // loading our former savePrefFile should allow us to read former prefs
-  ps.readUserPrefs(savePrefFile);
+
+  // Hack alert: on Windows nsLocalFile caches the size of savePrefFile from
+  // the .create() call above as 0. We call .exists() to reset the cache.
+  savePrefFile.exists();
+
+  ps.readUserPrefsFromFile(savePrefFile);
   // cleanup the file now we don't need it
   savePrefFile.remove(false);
   do_check_eq(ps.getBoolPref("ReadPref.bool"), true);
   do_check_eq(ps.getIntPref("ReadPref.int"), 230);
   do_check_eq(ps.getCharPref("ReadPref.char"), "hello");
 
   // ... and still be able to access "prior-to-readUserPrefs" preferences
   do_check_eq(pb.getBoolPref("bool1"), true);
--- a/modules/libpref/test/unit/test_stickyprefs.js
+++ b/modules/libpref/test/unit/test_stickyprefs.js
@@ -13,32 +13,37 @@ const ps = Services.prefs;
 // We avoid this by ensuring our "temp" prefs.js is the current prefs file.
 do_get_profile();
 do_register_cleanup(saveAndReload);
 
 // A little helper to reset the service and load some pref files
 function resetAndLoad(filenames) {
   ps.resetPrefs();
   for (let filename of filenames) {
-    ps.readUserPrefs(do_get_file(filename));
+    ps.readUserPrefsFromFile(do_get_file(filename));
   }
 }
 
 // A little helper that saves the current state to a file in the profile
 // dir, then resets the service and re-reads the file it just saved.
 // Used to test what gets actually written - things the pref service decided
 // not to write don't exist at all after this call.
 function saveAndReload() {
   let file = do_get_profile();
   file.append("prefs.js");
   ps.savePrefFile(file);
 
   // Now reset the pref service and re-read what we saved.
   ps.resetPrefs();
-  ps.readUserPrefs(file);
+
+  // Hack alert: on Windows nsLocalFile caches the size of savePrefFile from
+  // the .create() call above as 0. We call .exists() to reset the cache.
+  file.exists();
+
+  ps.readUserPrefsFromFile(file);
 }
 
 function run_test() {
   run_next_test();
 }
 
 // A sticky pref should not be written if the value is unchanged.
 add_test(function notWrittenWhenUnchanged() {
--- a/services/sync/tests/unit/test_prefs_store.js
+++ b/services/sync/tests/unit/test_prefs_store.js
@@ -20,25 +20,17 @@ function makePersona(id) {
     name: Math.random().toString(),
     headerURL: "http://localhost:1234/a"
   };
 }
 
 function run_test() {
   _("Test fixtures.");
   // read our custom prefs file before doing anything.
-  Services.prefs.readUserPrefs(do_get_file("prefs_test_prefs_store.js"));
-  // Now we've read from this file, any writes the pref service makes will be
-  // back to this prefs_test_prefs_store.js directly in the obj dir. This
-  // upsets things in confusing ways :) We avoid this by explicitly telling the
-  // pref service to use a file in our profile dir.
-  let prefFile = do_get_profile();
-  prefFile.append("prefs.js");
-  Services.prefs.savePrefFile(prefFile);
-  Services.prefs.readUserPrefs(prefFile);
+  Services.prefs.readUserPrefsFromFile(do_get_file("prefs_test_prefs_store.js"));
 
   let store = Service.engineManager.get("prefs")._store;
   let prefs = new Preferences();
   try {
 
     _("The GUID corresponds to XUL App ID.");
     let allIDs = store.getAllIDs();
     let ids = Object.keys(allIDs);
--- a/toolkit/components/telemetry/Scalars.yaml
+++ b/toolkit/components/telemetry/Scalars.yaml
@@ -312,16 +312,55 @@ security:
     kind: boolean
     keyed: true
     notification_emails:
       - seceng-telemetry@mozilla.com
     release_channel_collection: opt-out
     record_in_processes:
       - main
 
+preferences:
+  created_new_user_prefs_file:
+    bug_numbers:
+      - 1367813
+    description: >-
+      A boolean indicating that profile/prefs.js was not found and it is being
+      created for the first time in this session.
+    expires: "62"
+    kind: boolean
+    notification_emails:
+      - bsmedberg@mozilla.com
+    release_channel_collection: opt-out
+    record_in_processes:
+      - main
+  prefs_file_was_invalid:
+    bug_numbers:
+      - 1367813
+    description: >-
+      Set to true if a failure occurred reading profile/prefs.js.
+    expires: "62"
+    kind: boolean
+    notification_emails:
+      - bsmedberg@mozilla.com
+    release_channel_collection: opt-out
+    record_in_processes:
+      - main
+  read_user_js:
+    bug_numbers:
+      - 1367813
+    description: >-
+      Set to true if user.js exists and was read.
+    expires: "62"
+    kind: boolean
+    notification_emails:
+      - bsmedberg@mozilla.com
+    release_channel_collection: opt-out
+    record_in_processes:
+      - main
+
 # The following section contains WebRTC nICEr scalars
 # For more info on ICE, see https://tools.ietf.org/html/rfc5245
 # For more info on STUN, see https://tools.ietf.org/html/rfc5389
 # For more info on TURN, see https://tools.ietf.org/html/rfc5766
 webrtc.nicer:
   stun_retransmits:
     bug_numbers:
       - 1325536