Bug 808263 - Write profile creation time to the profile on creation. r=Mossop
authorRichard Newman <rnewman@mozilla.com>
Fri, 23 Nov 2012 21:13:37 -0800
changeset 114093 964fe8cb452b24a04957c529de4c43c0adcdd529
parent 114092 89e5db8cf62f31f8d20f4b456f5b9d12a7d17c9b
child 114094 d7841347b558575bb837c79334590feb2ba6e684
push id18551
push userrnewman@mozilla.com
push dateSat, 24 Nov 2012 05:13:53 +0000
treeherdermozilla-inbound@964fe8cb452b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMossop
bugs808263
milestone20.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 808263 - Write profile creation time to the profile on creation. r=Mossop
toolkit/profile/nsToolkitProfileService.cpp
toolkit/profile/test/test_create_profile.xul
--- a/toolkit/profile/nsToolkitProfileService.cpp
+++ b/toolkit/profile/nsToolkitProfileService.cpp
@@ -2,16 +2,19 @@
 /* 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/. */
 
 #include "mozilla/Util.h"
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <prlong.h>
+#include <prprf.h>
+#include <prtime.h>
 #include "nsProfileLock.h"
 
 #ifdef XP_WIN
 #include <windows.h>
 #include <shlobj.h>
 #endif
 #ifdef XP_UNIX
 #include <unistd.h>
@@ -117,16 +120,18 @@ private:
     }
     ~nsToolkitProfileService()
     {
         gService = nullptr;
     }
 
     NS_HIDDEN_(nsresult) Init();
 
+    nsresult CreateTimesInternal(nsIFile *profileDir);
+
     nsresult CreateProfileInternal(nsIFile* aRootDir,
                                    nsIFile* aLocalDir,
                                    const nsACString& aName,
                                    const nsACString* aProfileName,
                                    const nsACString* aAppName,
                                    const nsACString* aVendorName,
                                    /*in*/ nsIFile** aProfileDefaultsDir,
                                    bool aForExternalApp,
@@ -808,30 +813,70 @@ nsToolkitProfileService::CreateProfileIn
     rv = localDir->Exists(&exists);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (!exists) {
         rv = localDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
         NS_ENSURE_SUCCESS(rv, rv);
     }
 
+    // We created a new profile dir. Let's store a creation timestamp.
+    // Note that this code path does not apply if the profile dir was
+    // created prior to launching.
+    rv = CreateTimesInternal(rootDir);
+    NS_ENSURE_SUCCESS(rv, rv);
+
     nsToolkitProfile* last = aForExternalApp ? nullptr : mFirst;
     if (last) {
         while (last->mNext)
             last = last->mNext;
     }
 
     nsCOMPtr<nsIToolkitProfile> profile =
         new nsToolkitProfile(aName, rootDir, localDir, last, aForExternalApp);
     if (!profile) return NS_ERROR_OUT_OF_MEMORY;
 
     NS_ADDREF(*aResult = profile);
     return NS_OK;
 }
 
+nsresult
+nsToolkitProfileService::CreateTimesInternal(nsIFile* aProfileDir)
+{
+    nsresult rv = NS_ERROR_FAILURE;
+    nsCOMPtr<nsIFile> creationLog;
+    rv = aProfileDir->Clone(getter_AddRefs(creationLog));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = creationLog->AppendNative(NS_LITERAL_CSTRING("times.json"));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    bool exists = false;
+    creationLog->Exists(&exists);
+    if (exists) {
+      return NS_OK;
+    }
+
+    rv = creationLog->Create(nsIFile::NORMAL_FILE_TYPE, 0700);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // We don't care about microsecond resolution.
+    PRInt64 msec;
+    LL_DIV(msec, PR_Now(), PR_USEC_PER_MSEC);
+
+    // Write it out.
+    PRFileDesc *writeFile;
+    rv = creationLog->OpenNSPRFileDesc(PR_WRONLY, 0700, &writeFile);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    PR_fprintf(writeFile, "{\n\"created\": %lld\n}\n", msec);
+    PR_Close(writeFile);
+    return NS_OK;
+}
+
 NS_IMETHODIMP
 nsToolkitProfileService::GetProfileCount(uint32_t *aResult)
 {
     if (!mFirst)
         *aResult = 0;
     else if (! mFirst->mNext)
         *aResult = 1;
     else
--- a/toolkit/profile/test/test_create_profile.xul
+++ b/toolkit/profile/test/test_create_profile.xul
@@ -24,41 +24,93 @@ https://bugzilla.mozilla.org/show_bug.cg
   SimpleTest.waitForExplicitFinish();
 
   const Cc = Components.classes;
   const Ci = Components.interfaces;
 
   const ASCIIName = "myprofile";
   const UnicodeName = "\u09A0\u09BE\u0995\u09C1\u09B0"; // A Bengali name
 
+  var gIOService;
   var gProfileService;
 
+  gIOService = Cc["@mozilla.org/network/io-service;1"].
+    getService(Ci.nsIIOService);
+
   gProfileService = Cc["@mozilla.org/toolkit/profile-service;1"].
     getService(Ci.nsIToolkitProfileService);
 
   createProfile(ASCIIName);
   createProfile(UnicodeName);
   SimpleTest.finish();
 
-function createProfile(profileName)
-{
-  var profile = gProfileService.createProfile(null, null, profileName);
+/**
+ * Read the contents of an nsIFile. Throws on error.
+
+ * @param file an nsIFile instance.
+ * @return string contents.
+ */
+function readFile(file) {
+  let fstream = Cc["@mozilla.org/network/file-input-stream;1"].
+    createInstance(Ci.nsIFileInputStream);
+  let sstream = Cc["@mozilla.org/scriptableinputstream;1"].
+    createInstance(Components.interfaces.nsIScriptableInputStream);
+
+  const RO = 0x01;
+  const READ_OTHERS = 4;
+
+  fstream.init(file, RO, READ_OTHERS, 0);
+  sstream.init(fstream);
+  let out = sstream.read(sstream.available());
+  sstream.close();
+  fstream.close();
+  return out;
+}
+
+function checkBounds(lowerBound, value, upperBound) {
+  ok(lowerBound <= value, "value " + value +
+                          " is above lower bound " + lowerBound);
+  ok(upperBound >= value, "value " + value +
+                          " is within upper bound " + upperBound);
+}
+
+function createProfile(profileName) {
+  // Filesystem precision is lower than Date precision.
+  let lowerBound = Date.now() - 1000;
+
+  let profile = gProfileService.createProfile(null, null, profileName);
 
   // check that the directory was created
   isnot(profile, null, "Profile " + profileName + " created");
 
-  var profileDir = profile.rootDir;
+  let profileDir = profile.rootDir;
 
   ok(profileDir.exists(), "Profile dir created");
   ok(profileDir.isDirectory(), "Profile dir is a directory");
 
-  var profileDirPath = profileDir.path;
+  let profileDirPath = profileDir.path;
 
   is(profileDirPath.substr(profileDirPath.length - profileName.length),
      profileName, "Profile dir has expected name");
 
-  // clean up the profile
+  // Ensure that our timestamp file was created.
+  let jsonFile = profileDir.clone();
+  jsonFile.append("times.json");
+  ok(jsonFile.path, "Path is " + jsonFile.path);
+  ok(jsonFile.exists(), "Times file was created");
+  ok(jsonFile.isFile(), "Times file is a file");
+  let json = JSON.parse(readFile(jsonFile));
+
+  let upperBound = Date.now() + 1000;
+
+  let created = json.created;
+  ok(created, "created is set");
+
+  // Check against real clock time.
+  checkBounds(lowerBound, created, upperBound);
+
+  // Clean up the profile.
   profile.remove(true);
 }
 
   ]]>
   </script>
 </window>