Bug 756131 - API for creating default profiles for webapps, r=benjamin
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Thu, 24 May 2012 17:29:52 +0300
changeset 94654 4f66e59eaa773e1e61d56ba08dff8eba5009447b
parent 94653 480e89cf2e9bb935e1e3c85e3b7f6bb033d0c722
child 94773 2552b0541f87e2a0ad1eaa9b3baa443d1efcd4a0
push idunknown
push userunknown
push dateunknown
reviewersbenjamin
bugs756131
milestone15.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 756131 - API for creating default profiles for webapps, r=benjamin
toolkit/profile/nsIToolkitProfileService.idl
toolkit/profile/nsToolkitProfileService.cpp
toolkit/xre/nsXREDirProvider.cpp
toolkit/xre/nsXREDirProvider.h
--- a/toolkit/profile/nsIToolkitProfileService.idl
+++ b/toolkit/profile/nsIToolkitProfileService.idl
@@ -1,21 +1,22 @@
 /* -*- Mode: IDL; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 "nsISupports.idl"
 
 interface nsISimpleEnumerator;
+interface nsIFile;
 interface nsILocalFile;
 interface nsIToolkitProfile;
 interface nsIProfileLock;
 
-[scriptable, uuid(9b434f48-438c-4f85-89de-b7f321a45341)]
+[scriptable, uuid(b619f83d-8317-473c-b342-67905993fdc7)]
 interface nsIToolkitProfileService : nsISupports
 {
     attribute boolean startWithLastProfile;
     attribute boolean startOffline;
 
     readonly attribute nsISimpleEnumerator /*nsIToolkitProfile*/ profiles;
 
     attribute nsIToolkitProfile selectedProfile;
@@ -48,16 +49,45 @@ interface nsIToolkitProfileService : nsI
      * @param aName
      *        The profile name.
      */
     nsIToolkitProfile createProfile(in nsILocalFile aRootDir,
                                     in nsILocalFile aTempDir,
                                     in AUTF8String aName);
 
     /**
+     * Create the default profile for an application.
+     *
+     * The profile will be typically in
+     * {Application Data}/.profilename/{salt}.default or
+     * {Application Data}/.appname/{salt}.default
+     * or if aVendorName is provided
+     * {Application Data}/.vendor/appname/{salt}.default
+     *
+     * @note Either aProfileName or aAppName must be non-empty
+     *
+     * The contents of aProfileDefaultsDir will be copied to the
+     * new profile directory.
+     * 
+     * @param  aProfileName
+     *         The name of the profile
+     * @param  aAppName
+     *         The name of the application
+     * @param  aVendorName
+     *         The name of the vendor
+     * @param  aProfileDefaultsDir
+     *         The location where the profile defaults are.
+     * @return The created profile.
+     */                                    
+    nsIToolkitProfile createDefaultProfileForApp(in AUTF8String aProfileName,
+                                                 in AUTF8String aAppName,
+                                                 in AUTF8String aVendorName,
+                                                 [optional] in nsIFile aProfileDefaultsDir);
+
+    /**
      * Returns the number of profiles.
      * @return 0, 1, or 2. More than 2 profiles will always return 2.
      */
     readonly attribute unsigned long profileCount;
 
     /**
      * Flush the profiles list file.
      */
--- a/toolkit/profile/nsToolkitProfileService.cpp
+++ b/toolkit/profile/nsToolkitProfileService.cpp
@@ -51,24 +51,26 @@ public:
     nsToolkitProfile          *mPrev;
 
     ~nsToolkitProfile() { }
 
 private:
     nsToolkitProfile(const nsACString& aName,
                      nsILocalFile* aRootDir,
                      nsILocalFile* aLocalDir,
-                     nsToolkitProfile* aPrev);
+                     nsToolkitProfile* aPrev,
+                     bool aForExternalApp);
 
     friend class nsToolkitProfileLock;
 
     nsCString                  mName;
     nsCOMPtr<nsILocalFile>     mRootDir;
     nsCOMPtr<nsILocalFile>     mLocalDir;
     nsIProfileLock*            mLock;
+    bool                       mForExternalApp;
 };
 
 class nsToolkitProfileLock : public nsIProfileLock
 {
 public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIPROFILELOCK
 
@@ -114,16 +116,26 @@ private:
     }
     ~nsToolkitProfileService()
     {
         gService = nsnull;
     }
 
     NS_HIDDEN_(nsresult) Init();
 
+    nsresult CreateProfileInternal(nsILocalFile* aRootDir,
+                                   nsILocalFile* aLocalDir,
+                                   const nsACString& aName,
+                                   const nsACString* aProfileName,
+                                   const nsACString* aAppName,
+                                   const nsACString* aVendorName,
+                                   /*in*/ nsIFile** aProfileDefaultsDir,
+                                   bool aForExternalApp,
+                                   nsIToolkitProfile** aResult);
+
     nsRefPtr<nsToolkitProfile>  mFirst;
     nsCOMPtr<nsIToolkitProfile> mChosen;
     nsCOMPtr<nsILocalFile>      mAppData;
     nsCOMPtr<nsILocalFile>      mTempData;
     nsCOMPtr<nsILocalFile>      mListFile;
     bool mDirty;
     bool mStartWithLast;
     bool mStartOffline;
@@ -142,29 +154,34 @@ private:
         ~ProfileEnumerator() { }
         nsCOMPtr<nsToolkitProfile> mCurrent;
     };
 };
 
 nsToolkitProfile::nsToolkitProfile(const nsACString& aName,
                                    nsILocalFile* aRootDir,
                                    nsILocalFile* aLocalDir,
-                                   nsToolkitProfile* aPrev) :
+                                   nsToolkitProfile* aPrev,
+                                   bool aForExternalApp) :
     mPrev(aPrev),
     mName(aName),
     mRootDir(aRootDir),
     mLocalDir(aLocalDir),
-    mLock(nsnull)
+    mLock(nsnull),
+    mForExternalApp(aForExternalApp)
 {
     NS_ASSERTION(aRootDir, "No file!");
 
-    if (aPrev)
-        aPrev->mNext = this;
-    else
-        nsToolkitProfileService::gService->mFirst = this;
+    if (!aForExternalApp) {
+        if (aPrev) {
+            aPrev->mNext = this;
+        } else {
+            nsToolkitProfileService::gService->mFirst = this;
+        }
+    }
 }
 
 NS_IMPL_ISUPPORTS1(nsToolkitProfile, nsIToolkitProfile)
 
 NS_IMETHODIMP
 nsToolkitProfile::GetRootDir(nsILocalFile* *aResult)
 {
     NS_ADDREF(*aResult = mRootDir);
@@ -185,29 +202,32 @@ nsToolkitProfile::GetName(nsACString& aR
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsToolkitProfile::SetName(const nsACString& aName)
 {
     NS_ASSERTION(nsToolkitProfileService::gService,
                  "Where did my service go?");
+    NS_ENSURE_TRUE(!mForExternalApp, NS_ERROR_NOT_IMPLEMENTED);
 
     mName = aName;
     nsToolkitProfileService::gService->mDirty = true;
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsToolkitProfile::Remove(bool removeFiles)
 {
     NS_ASSERTION(nsToolkitProfileService::gService,
                  "Whoa, my service is gone.");
 
+    NS_ENSURE_TRUE(!mForExternalApp, NS_ERROR_NOT_IMPLEMENTED);
+
     if (mLock)
         return NS_ERROR_FILE_IS_LOCKED;
 
     if (removeFiles) {
         bool equals;
         nsresult rv = mRootDir->Equals(mLocalDir, &equals);
         if (NS_FAILED(rv))
             return rv;
@@ -442,17 +462,17 @@ nsToolkitProfileService::Init()
 
             rv = localDir->SetRelativeDescriptor(mTempData, filePath);
         } else {
             localDir = rootDir;
         }
 
         currentProfile = new nsToolkitProfile(buffer,
                                               rootDir, localDir,
-                                              currentProfile);
+                                              currentProfile, false);
         NS_ENSURE_TRUE(currentProfile, NS_ERROR_OUT_OF_MEMORY);
 
         rv = parser.GetString(profileID.get(), "Default", buffer);
         if (NS_SUCCEEDED(rv) && buffer.EqualsLiteral("1"))
             mChosen = currentProfile;
     }
     if (!mChosen && mFirst && !mFirst->mNext) // only one profile
         mChosen = mFirst;
@@ -602,34 +622,120 @@ static void SaltProfileName(nsACString& 
         salt[i] = kTable[rand() % ArrayLength(kTable)];
 
     salt[8] = '.';
 
     aName.Insert(salt, 0, 9);
 }
 
 NS_IMETHODIMP
+nsToolkitProfileService::CreateDefaultProfileForApp(const nsACString& aProfileName,
+                                                    const nsACString& aAppName,
+                                                    const nsACString& aVendorName,
+                                                    nsIFile* aProfileDefaultsDir,
+                                                    nsIToolkitProfile** aResult)
+{
+    NS_ENSURE_STATE(!aProfileName.IsEmpty() || !aAppName.IsEmpty());
+    nsCOMPtr<nsILocalFile> appData;
+    nsresult rv =
+        gDirServiceProvider->GetUserDataDirectory(getter_AddRefs(appData),
+                                                  false,
+                                                  &aProfileName,
+                                                  &aAppName,
+                                                  &aVendorName);
+    NS_ENSURE_SUCCESS(rv, rv);
+ 
+    nsCOMPtr<nsIFile> profilesini;
+    appData->Clone(getter_AddRefs(profilesini));
+    rv = profilesini->AppendNative(NS_LITERAL_CSTRING("profiles.ini"));
+    NS_ENSURE_SUCCESS(rv, rv);
+  
+    bool exists = false;
+    profilesini->Exists(&exists);
+    NS_ENSURE_FALSE(exists, NS_ERROR_ALREADY_INITIALIZED);
+    
+    nsIFile* profileDefaultsDir = aProfileDefaultsDir;
+    rv = CreateProfileInternal(nsnull, nsnull,
+                               NS_LITERAL_CSTRING("default"),
+                               &aProfileName, &aAppName, &aVendorName,
+                               &profileDefaultsDir, true, aResult);
+    NS_ENSURE_SUCCESS(rv, rv);
+    NS_ENSURE_STATE(*aResult);
+  
+    nsCOMPtr<nsILocalFile> rootDir;
+    (*aResult)->GetRootDir(getter_AddRefs(rootDir));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCAutoString profileDir;
+    rv = rootDir->GetRelativeDescriptor(appData, profileDir);
+    NS_ENSURE_SUCCESS(rv, rv);
+  
+    nsCString ini;
+    ini.SetCapacity(512);
+    ini.AppendASCII("[General]\n");
+    ini.AppendASCII("StartWithLastProfile=1\n\n");
+  
+    ini.AppendASCII("[Profile0]\n");
+    ini.AppendASCII("Name=default\n");
+    ini.AppendASCII("IsRelative=1\n");
+    ini.AppendASCII("Path=");
+    ini.Append(profileDir);
+    ini.AppendASCII("\n");
+    ini.AppendASCII("Default=1\n\n");
+  
+    FILE* writeFile;
+    rv = profilesini->OpenANSIFileDesc("w", &writeFile);
+    NS_ENSURE_SUCCESS(rv, rv);
+    
+    if (fwrite(ini.get(), sizeof(char), ini.Length(), writeFile) !=
+        ini.Length()) {
+        rv = NS_ERROR_UNEXPECTED;
+    }
+    fclose(writeFile);
+    return rv;
+}
+
+NS_IMETHODIMP
 nsToolkitProfileService::CreateProfile(nsILocalFile* aRootDir,
                                        nsILocalFile* aLocalDir,
                                        const nsACString& aName,
-                                       nsIToolkitProfile* *aResult)
+                                       nsIToolkitProfile** aResult)
 {
-    nsresult rv;
+    return CreateProfileInternal(aRootDir, aLocalDir, aName,
+                                 nsnull, nsnull, nsnull, nsnull, false, aResult);
+}
 
-    rv = GetProfileByName(aName, aResult);
-    if (NS_SUCCEEDED(rv)) return rv;
+nsresult
+nsToolkitProfileService::CreateProfileInternal(nsILocalFile* aRootDir,
+                                               nsILocalFile* aLocalDir,
+                                               const nsACString& aName,
+                                               const nsACString* aProfileName,
+                                               const nsACString* aAppName,
+                                               const nsACString* aVendorName,
+                                               nsIFile** aProfileDefaultsDir,
+                                               bool aForExternalApp,
+                                               nsIToolkitProfile** aResult)
+{
+    nsresult rv = NS_ERROR_FAILURE;
+
+    if (!aForExternalApp) {
+        rv = GetProfileByName(aName, aResult);
+        if (NS_SUCCEEDED(rv)) {
+            return rv;
+        }
+    }
 
     nsCOMPtr<nsILocalFile> rootDir (aRootDir);
 
     nsCAutoString dirName;
     if (!rootDir) {
         nsCOMPtr<nsIFile> file;
-        bool dummy;
-        rv = gDirServiceProvider->GetFile(NS_APP_USER_PROFILES_ROOT_DIR, &dummy,
-                                          getter_AddRefs(file));
+        rv = gDirServiceProvider->GetUserProfilesRootDir(getter_AddRefs(file),
+                                                         aProfileName, aAppName,
+                                                         aVendorName);
         NS_ENSURE_SUCCESS(rv, rv);
 
         rootDir = do_QueryInterface(file);
         NS_ENSURE_TRUE(rootDir, NS_ERROR_UNEXPECTED);
 
         dirName = aName;
         SaltProfileName(dirName);
 
@@ -644,18 +750,20 @@ nsToolkitProfileService::CreateProfile(n
 
     if (!localDir) {
         if (aRootDir) {
             localDir = aRootDir;
         }
         else {
             nsCOMPtr<nsIFile> file;
             bool dummy;
-            rv = gDirServiceProvider->GetFile(NS_APP_USER_PROFILES_LOCAL_ROOT_DIR,
-                                              &dummy, getter_AddRefs(file));
+            rv = gDirServiceProvider->GetUserProfilesLocalDir(getter_AddRefs(file),
+                                                              aProfileName,
+                                                              aAppName,
+                                                              aVendorName);
             NS_ENSURE_SUCCESS(rv, rv);
 
             localDir = do_QueryInterface(file);
             NS_ENSURE_TRUE(localDir, NS_ERROR_UNEXPECTED);
 
             // use same salting
             if (NS_IsNativeUTF8()) {
                 localDir->AppendNative(dirName);
@@ -682,24 +790,28 @@ nsToolkitProfileService::CreateProfile(n
         nsAutoString profileDirName;
 
         rv = rootDir->GetParent(getter_AddRefs(profileDirParent));
         NS_ENSURE_SUCCESS(rv, rv);
 
         rv = rootDir->GetLeafName(profileDirName);
         NS_ENSURE_SUCCESS(rv, rv);
 
-        bool dummy;
-        rv = gDirServiceProvider->GetFile(NS_APP_PROFILE_DEFAULTS_50_DIR, &dummy,
-                                          getter_AddRefs(profileDefaultsDir));
+        if (aProfileDefaultsDir) {
+            profileDefaultsDir = *aProfileDefaultsDir;
+        } else {
+            bool dummy;
+            rv = gDirServiceProvider->GetFile(NS_APP_PROFILE_DEFAULTS_50_DIR, &dummy,
+                                              getter_AddRefs(profileDefaultsDir));
+        }
 
-        if (NS_SUCCEEDED(rv))
+        if (NS_SUCCEEDED(rv) && profileDefaultsDir)
             rv = profileDefaultsDir->CopyTo(profileDirParent,
                                             profileDirName);
-        if (NS_FAILED(rv)) {
+        if (NS_FAILED(rv) || !profileDefaultsDir) {
             // if copying failed, lets just ensure that the profile directory exists.
             rv = rootDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
             NS_ENSURE_SUCCESS(rv, rv);
         }
         rv = rootDir->SetPermissions(0700);
 #ifndef ANDROID      
         // If the profile is on the sdcard, this will fail but its non-fatal
         NS_ENSURE_SUCCESS(rv, rv);
@@ -709,24 +821,24 @@ nsToolkitProfileService::CreateProfile(n
     rv = localDir->Exists(&exists);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (!exists) {
         rv = localDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
         NS_ENSURE_SUCCESS(rv, rv);
     }
 
-    nsToolkitProfile* last = mFirst;
+    nsToolkitProfile* last = aForExternalApp ? nsnull : mFirst;
     if (last) {
         while (last->mNext)
             last = last->mNext;
     }
 
     nsCOMPtr<nsIToolkitProfile> profile =
-        new nsToolkitProfile(aName, rootDir, localDir, last);
+        new nsToolkitProfile(aName, rootDir, localDir, last, aForExternalApp);
     if (!profile) return NS_ERROR_OUT_OF_MEMORY;
 
     NS_ADDREF(*aResult = profile);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsToolkitProfileService::GetProfileCount(PRUint32 *aResult)
--- a/toolkit/xre/nsXREDirProvider.cpp
+++ b/toolkit/xre/nsXREDirProvider.cpp
@@ -150,16 +150,62 @@ nsXREDirProvider::AddRef()
 }
 
 NS_IMETHODIMP_(nsrefcnt)
 nsXREDirProvider::Release()
 {
   return 0;
 }
 
+nsresult
+nsXREDirProvider::GetUserProfilesRootDir(nsIFile** aResult,
+                                         const nsACString* aProfileName,
+                                         const nsACString* aAppName,
+                                         const nsACString* aVendorName)
+{
+  nsCOMPtr<nsIFile> file;
+  nsresult rv = GetUserDataDirectory((nsILocalFile**)(nsIFile**)
+                                      getter_AddRefs(file),
+                                     false,
+                                     aProfileName, aAppName, aVendorName);
+
+  if (NS_SUCCEEDED(rv)) {
+#if !defined(XP_UNIX) || defined(XP_MACOSX)
+    rv = file->AppendNative(NS_LITERAL_CSTRING("Profiles"));
+#endif
+    // We must create the profile directory here if it does not exist.
+    rv |= EnsureDirectoryExists(file);
+  }
+  file.swap(*aResult);
+  return rv;
+}
+
+nsresult
+nsXREDirProvider::GetUserProfilesLocalDir(nsIFile** aResult,
+                                          const nsACString* aProfileName,
+                                          const nsACString* aAppName,
+                                          const nsACString* aVendorName)
+{
+  nsCOMPtr<nsIFile> file;
+  nsresult rv = GetUserDataDirectory((nsILocalFile**)(nsIFile**)
+                                     getter_AddRefs(file),
+                                     true,
+                                     aProfileName, aAppName, aVendorName);
+
+  if (NS_SUCCEEDED(rv)) {
+#if !defined(XP_UNIX) || defined(XP_MACOSX)
+    rv = file->AppendNative(NS_LITERAL_CSTRING("Profiles"));
+#endif
+    // We must create the profile directory here if it does not exist.
+    rv |= EnsureDirectoryExists(file);
+  }
+  file.swap(*aResult);
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 nsXREDirProvider::GetFile(const char* aProperty, bool* aPersistent,
 			  nsIFile** aFile)
 {
   nsresult rv;
 
   bool gettingProfile = false;
 
@@ -240,38 +286,20 @@ nsXREDirProvider::GetFile(const char* aP
 #endif
   }
   else if (!strcmp(aProperty, NS_APP_APPLICATION_REGISTRY_FILE)) {
     rv = GetUserAppDataDirectory((nsILocalFile**)(nsIFile**) getter_AddRefs(file));
     if (NS_SUCCEEDED(rv))
       rv = file->AppendNative(NS_LITERAL_CSTRING(APP_REGISTRY_NAME));
   }
   else if (!strcmp(aProperty, NS_APP_USER_PROFILES_ROOT_DIR)) {
-    rv = GetUserAppDataDirectory((nsILocalFile**)(nsIFile**) getter_AddRefs(file));
-
-    if (NS_SUCCEEDED(rv)) {
-#if !defined(XP_UNIX) || defined(XP_MACOSX)
-      rv = file->AppendNative(NS_LITERAL_CSTRING("Profiles"));
-#endif
-
-      // We must create the profile directory here if it does not exist.
-      rv |= EnsureDirectoryExists(file);
-    }
+    rv = GetUserProfilesRootDir(getter_AddRefs(file), nsnull, nsnull, nsnull);
   }
   else if (!strcmp(aProperty, NS_APP_USER_PROFILES_LOCAL_ROOT_DIR)) {
-    rv = GetUserLocalDataDirectory((nsILocalFile**)(nsIFile**) getter_AddRefs(file));
-
-    if (NS_SUCCEEDED(rv)) {
-#if !defined(XP_UNIX) || defined(XP_MACOSX)
-      rv = file->AppendNative(NS_LITERAL_CSTRING("Profiles"));
-#endif
-
-      // We must create the profile directory here if it does not exist.
-      rv |= EnsureDirectoryExists(file);
-    }
+    rv = GetUserProfilesLocalDir(getter_AddRefs(file), nsnull, nsnull, nsnull);
   }
   else if (!strcmp(aProperty, XRE_EXECUTABLE_FILE) && gArgv[0]) {
     nsCOMPtr<nsILocalFile> lf;
     rv = XRE_GetBinaryPath(gArgv[0], getter_AddRefs(lf));
     if (NS_SUCCEEDED(rv))
       file = lf;
   }
 
@@ -1161,23 +1189,26 @@ nsXREDirProvider::GetSystemExtensionsDir
 #endif
 
   NS_ADDREF(*aFile = localDir);
   return NS_OK;
 }
 #endif
 
 nsresult
-nsXREDirProvider::GetUserDataDirectory(nsILocalFile** aFile, bool aLocal)
+nsXREDirProvider::GetUserDataDirectory(nsILocalFile** aFile, bool aLocal,
+                                       const nsACString* aProfileName,
+                                       const nsACString* aAppName,
+                                       const nsACString* aVendorName)
 {
   nsCOMPtr<nsILocalFile> localDir;
   nsresult rv = GetUserDataDirectoryHome(getter_AddRefs(localDir), aLocal);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = AppendProfilePath(localDir);
+  rv = AppendProfilePath(localDir, aProfileName, aAppName, aVendorName);
   NS_ENSURE_SUCCESS(rv, rv);
 
 #ifdef DEBUG_jungshik
   nsCAutoString cwd;
   localDir->GetNativePath(cwd);
   printf("nsXREDirProvider::GetUserDataDirectory: %s\n", cwd.get());
 #endif
   rv = EnsureDirectoryExists(localDir);
@@ -1285,88 +1316,112 @@ nsXREDirProvider::AppendSysUserExtension
 #else
 #error "Don't know how to get XRE user extension path on your platform"
 #endif
   return NS_OK;
 }
 
 
 nsresult
-nsXREDirProvider::AppendProfilePath(nsIFile* aFile)
+nsXREDirProvider::AppendProfilePath(nsIFile* aFile,
+                                    const nsACString* aProfileName,
+                                    const nsACString* aAppName,
+                                    const nsACString* aVendorName)
 {
   NS_ASSERTION(aFile, "Null pointer!");
+  
+  if (!gAppData) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCAutoString profile;
+  nsCAutoString appName;
+  nsCAutoString vendor;
+  if (aProfileName && !aProfileName->IsEmpty()) {
+    profile = *aProfileName;
+  } else if (aAppName) {
+    appName = *aAppName;
+    if (aVendorName) {
+      vendor = *aVendorName;
+    }
+  } else if (gAppData->profile) {
+    profile = gAppData->profile;
+  } else {
+    appName = gAppData->name;
+    vendor = gAppData->vendor;
+  }
 
   nsresult rv;
 
-  if (!gAppData)
-    return NS_ERROR_FAILURE;
-
 #if defined (XP_MACOSX)
-  if (gAppData->profile) {
-    rv = AppendProfileString(aFile, gAppData->profile);
+  if (!profile.IsEmpty()) {
+    rv = AppendProfileString(aFile, profile.get());
   }
   else {
     // Note that MacOS ignores the vendor when creating the profile hierarchy -
     // all application preferences directories live alongside one another in
     // ~/Library/Application Support/
-    rv = aFile->AppendNative(nsDependentCString(gAppData->name));
+    rv = aFile->AppendNative(appName);
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
 #elif defined(XP_WIN) || defined(XP_OS2)
-  if (gAppData->profile) {
-    rv = AppendProfileString(aFile, gAppData->profile);
+  if (!profile.IsEmpty()) {
+    rv = AppendProfileString(aFile, profile.get());
   }
   else {
-    if (gAppData->vendor) {
-      rv = aFile->AppendNative(nsDependentCString(gAppData->vendor));
+    if (!vendor.IsEmpty()) {
+      rv = aFile->AppendNative(vendor);
       NS_ENSURE_SUCCESS(rv, rv);
     }
-    rv = aFile->AppendNative(nsDependentCString(gAppData->name));
+    rv = aFile->AppendNative(appName);
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
 #elif defined(ANDROID)
   // The directory used for storing profiles
   // The parent of this directory is set in GetUserDataDirectoryHome
   // XXX: handle gAppData->profile properly
+  // XXXsmaug ...and the rest of the profile creation!
+  MOZ_ASSERT(!aAppName,
+             "Profile creation for external applications is not implemented!");
   rv = aFile->AppendNative(nsDependentCString("mozilla"));
   NS_ENSURE_SUCCESS(rv, rv);
 #elif defined(XP_UNIX)
   // Make it hidden (i.e. using the ".")
   nsCAutoString folder(".");
 
-  if (gAppData->profile) {
+  if (!profile.IsEmpty()) {
     // Skip any leading path characters
-    const char* profileStart = gAppData->profile;
+    const char* profileStart = profile.get();
     while (*profileStart == '/' || *profileStart == '\\')
       profileStart++;
 
     // On the off chance that someone wanted their folder to be hidden don't
     // let it become ".."
     if (*profileStart == '.')
       profileStart++;
 
     folder.Append(profileStart);
     ToLowerCase(folder);
 
     rv = AppendProfileString(aFile, folder.BeginReading());
   }
   else {
-    if (gAppData->vendor) {
-      folder.Append(gAppData->vendor);
+    if (!vendor.IsEmpty()) {
+      folder.Append(vendor);
       ToLowerCase(folder);
 
       rv = aFile->AppendNative(folder);
       NS_ENSURE_SUCCESS(rv, rv);
 
       folder.Truncate();
     }
 
-    folder.Append(gAppData->name);
+    folder.Append(appName);
     ToLowerCase(folder);
 
     rv = aFile->AppendNative(folder);
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
 #else
 #error "Don't know how to get profile path on your platform"
--- a/toolkit/xre/nsXREDirProvider.h
+++ b/toolkit/xre/nsXREDirProvider.h
@@ -31,33 +31,49 @@ public:
   // if aXULAppDir is null, use gArgv[0]
   nsresult Initialize(nsIFile *aXULAppDir,
                       nsILocalFile *aGREDir,
                       nsIDirectoryServiceProvider* aAppProvider = nsnull);
   ~nsXREDirProvider();
 
   static nsXREDirProvider* GetSingleton();
 
+  nsresult GetUserProfilesRootDir(nsIFile** aResult,
+                                  const nsACString* aProfileName,
+                                  const nsACString* aAppName,
+                                  const nsACString* aVendorName);
+  nsresult GetUserProfilesLocalDir(nsIFile** aResult,
+                                   const nsACString* aProfileName,
+                                   const nsACString* aAppName,
+                                   const nsACString* aVendorName);
+
   // We only set the profile dir, we don't ensure that it exists;
   // that is the responsibility of the toolkit profile service.
   // We also don't fire profile-changed notifications... that is
   // the responsibility of the apprunner.
   nsresult SetProfile(nsIFile* aProfileDir, nsIFile* aProfileLocalDir);
 
   void DoShutdown();
 
   nsresult GetProfileDefaultsDir(nsIFile* *aResult);
 
   static nsresult GetUserAppDataDirectory(nsILocalFile* *aFile) {
-    return GetUserDataDirectory(aFile, false);
+    return GetUserDataDirectory(aFile, false, nsnull, nsnull, nsnull);
   }
   static nsresult GetUserLocalDataDirectory(nsILocalFile* *aFile) {
-    return GetUserDataDirectory(aFile, true);
+    return GetUserDataDirectory(aFile, true, nsnull, nsnull, nsnull);
   }
 
+  // By default GetUserDataDirectory gets profile path from gAppData,
+  // but that can be overridden by using aProfileName/aAppName/aVendorName.
+  static nsresult GetUserDataDirectory(nsILocalFile** aFile, bool aLocal,
+                                       const nsACString* aProfileName,
+                                       const nsACString* aAppName,
+                                       const nsACString* aVendorName);
+
   /* make sure you clone it, if you need to do stuff to it */
   nsIFile* GetGREDir() { return mGREDir; }
   nsIFile* GetAppDir() {
     if (mXULAppDir)
       return mXULAppDir;
     return mGREDir;
   }
 
@@ -79,28 +95,30 @@ public:
    * Get the profile directory as determined by this class or by an
    * embedder-provided XPCOM directory provider. Only call this method
    * when XPCOM is initialized! aResult is a clone, it may be modified.
    */
   nsresult GetProfileDir(nsIFile* *aResult);
 
 protected:
   nsresult GetFilesInternal(const char* aProperty, nsISimpleEnumerator** aResult);
-  static nsresult GetUserDataDirectory(nsILocalFile* *aFile, bool aLocal);
   static nsresult GetUserDataDirectoryHome(nsILocalFile* *aFile, bool aLocal);
   static nsresult GetSysUserExtensionsDirectory(nsILocalFile* *aFile);
 #if defined(XP_UNIX) || defined(XP_MACOSX)
   static nsresult GetSystemExtensionsDirectory(nsILocalFile** aFile);
 #endif
   static nsresult EnsureDirectoryExists(nsIFile* aDirectory);
   void EnsureProfileFileExists(nsIFile* aFile);
 
   // Determine the profile path within the UAppData directory. This is different
   // on every major platform.
-  static nsresult AppendProfilePath(nsIFile* aFile);
+  static nsresult AppendProfilePath(nsIFile* aFile,
+                                    const nsACString* aProfileName,
+                                    const nsACString* aAppName,
+                                    const nsACString* aVendorName);
 
   static nsresult AppendSysUserExtensionPath(nsIFile* aFile);
 
   // Internal helper that splits a path into components using the '/' and '\\'
   // delimiters.
   static inline nsresult AppendProfileString(nsIFile* aFile, const char* aPath);
 
   // Calculate and register extension and theme bundle directories.