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 id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersMossop
bugs808263
milestone20.0a1
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>