Bug 396209 - "Allow applications to specify a profile directory from application.ini". r=luser, a=bsmedberg. Also fixes bugs 396486, 396199, and 386164.
authorbent.mozilla@gmail.com
Wed, 26 Sep 2007 11:35:21 -0700
changeset 6338 39cdf108326220ab37bd27f2287d0f46905f2d6c
parent 6337 6aee59c12a7f7ba3f701053bf0b4bac57b5e13c1
child 6339 23a1bc448cd640d6260245913f87e189b6270afa
push idunknown
push userunknown
push dateunknown
reviewersluser, bsmedberg
bugs396209, 396486, 396199, 386164
milestone1.9a9pre
Bug 396209 - "Allow applications to specify a profile directory from application.ini". r=luser, a=bsmedberg. Also fixes bugs 396486, 396199, and 386164.
profile/dirserviceprovider/src/nsProfileLock.cpp
toolkit/crashreporter/client/crashreporter.cpp
toolkit/crashreporter/client/crashreporter_linux.cpp
toolkit/crashreporter/client/crashreporter_osx.mm
toolkit/crashreporter/client/crashreporter_win.cpp
toolkit/crashreporter/nsExceptionHandler.cpp
toolkit/xre/nsAppData.cpp
toolkit/xre/nsXREDirProvider.cpp
toolkit/xre/nsXREDirProvider.h
toolkit/xre/nsXULAppAPI.h
--- a/profile/dirserviceprovider/src/nsProfileLock.cpp
+++ b/profile/dirserviceprovider/src/nsProfileLock.cpp
@@ -556,27 +556,27 @@ nsresult nsProfileLock::Lock(nsILocalFil
     {
         // If that failed for any reason other than NS_ERROR_FILE_ACCESS_DENIED,
         // assume we tried an NFS that does not support it. Now, try with symlink
         // using the old symlink path
         rv = LockWithSymlink(oldFilePath, PR_FALSE);
     }
 
 #elif defined(XP_WIN)
-    nsCAutoString filePath;
-    rv = lockFile->GetNativePath(filePath);
+    nsAutoString filePath;
+    rv = lockFile->GetPath(filePath);
     if (NS_FAILED(rv))
         return rv;
-    mLockFileHandle = CreateFile(filePath.get(),
-                                 GENERIC_READ | GENERIC_WRITE,
-                                 0, // no sharing - of course
-                                 nsnull,
-                                 OPEN_ALWAYS,
-                                 FILE_FLAG_DELETE_ON_CLOSE,
-                                 nsnull);
+    mLockFileHandle = CreateFileW(filePath.get(),
+                                  GENERIC_READ | GENERIC_WRITE,
+                                  0, // no sharing - of course
+                                  nsnull,
+                                  OPEN_ALWAYS,
+                                  FILE_FLAG_DELETE_ON_CLOSE,
+                                  nsnull);
     if (mLockFileHandle == INVALID_HANDLE_VALUE) {
         // XXXbsmedberg: provide a profile-unlocker here!
         return NS_ERROR_FILE_ACCESS_DENIED;
     }
 #elif defined(XP_OS2)
     nsCAutoString filePath;
     rv = lockFile->GetNativePath(filePath);
     if (NS_FAILED(rv))
--- a/toolkit/crashreporter/client/crashreporter.cpp
+++ b/toolkit/crashreporter/client/crashreporter.cpp
@@ -408,19 +408,32 @@ int main(int argc, char** argv)
     // to be product specific
     RewriteStrings(queryParameters);
 
     if (queryParameters.find("ServerURL") == queryParameters.end()) {
       UIError(gStrings[ST_ERROR_NOSERVERURL]);
       return 0;
     }
 
-    string product = queryParameters["ProductName"];
-    string vendor = queryParameters["Vendor"];
-    if (!UIGetSettingsPath(vendor, product, gSettingsPath)) {
+    // Hopefully the settings path exists in the environment. Try that before
+    // asking the platform-specific code to guess.
+    static const char kDataDirKey[] = "MOZ_CRASHREPORTER_DATA_DIRECTORY";
+    const char *settingsPath = getenv(kDataDirKey);
+    if (settingsPath && *settingsPath) {
+      gSettingsPath = settingsPath;
+    }
+    else {
+      string product = queryParameters["ProductName"];
+      string vendor = queryParameters["Vendor"];
+      if (!UIGetSettingsPath(vendor, product, gSettingsPath)) {
+        gSettingsPath.clear();
+      }
+    }
+
+    if (gSettingsPath.empty() || !UIEnsurePathExists(gSettingsPath)) {
       UIError(gStrings[ST_ERROR_NOSETTINGSPATH]);
       return 0;
     }
 
     if (!UIFileExists(gDumpFile)) {
       UIError(gStrings[ST_ERROR_DUMPFILEEXISTS]);
       return 0;
     }
--- a/toolkit/crashreporter/client/crashreporter_linux.cpp
+++ b/toolkit/crashreporter/client/crashreporter_linux.cpp
@@ -291,17 +291,17 @@ static void TryInitGnome()
 {
   gnomeLib = dlopen("libgnome-2.so.0", RTLD_LAZY);
   if (!gnomeLib)
     return;
 
   gnomeuiLib = dlopen("libgnomeui-2.so.0", RTLD_LAZY);
   if (!gnomeuiLib)
     return;
- 
+
   _gnome_program_init_fn gnome_program_init =
     (_gnome_program_init_fn)(dlsym(gnomeLib, "gnome_program_init"));
   _libgnomeui_module_info_get_fn libgnomeui_module_info_get =
     (_libgnomeui_module_info_get_fn)(dlsym(gnomeuiLib, "libgnomeui_module_info_get"));
 
   if (gnome_program_init && libgnomeui_module_info_get) {
     gnome_program_init("crashreporter", "1.0", libgnomeui_module_info_get(),
                        gArgc, gArgv, NULL);
@@ -490,33 +490,33 @@ bool UIGetIniPath(string& path)
  * Settings are stored in ~/.vendor/product, or
  * ~/.product if vendor is empty.
  */
 bool UIGetSettingsPath(const string& vendor,
                        const string& product,
                        string& settingsPath)
 {
   char* home = getenv("HOME");
-  
+
   if (!home)
     return false;
 
   settingsPath = home;
   settingsPath += "/.";
   if (!vendor.empty()) {
     string lc_vendor;
     std::transform(vendor.begin(), vendor.end(), back_inserter(lc_vendor),
-		   (int(*)(int)) std::tolower);
+                   (int(*)(int)) std::tolower);
     settingsPath += lc_vendor + "/";
   }
   string lc_product;
   std::transform(product.begin(), product.end(), back_inserter(lc_product),
-		 (int(*)(int)) std::tolower);
+                 (int(*)(int)) std::tolower);
   settingsPath += lc_product + "/Crash Reports";
-  return UIEnsurePathExists(settingsPath);
+  return true;
 }
 
 bool UIEnsurePathExists(const string& path)
 {
   int ret = mkdir(path.c_str(), S_IRWXU);
   int e = errno;
   if (ret == -1 && e != EEXIST)
     return false;
@@ -548,9 +548,8 @@ std::ifstream* UIOpenRead(const string& 
 {
   return new std::ifstream(filename.c_str(), std::ios::in);
 }
 
 std::ofstream* UIOpenWrite(const string& filename)
 {
   return new std::ofstream(filename.c_str(), std::ios::out);
 }
-
--- a/toolkit/crashreporter/client/crashreporter_osx.mm
+++ b/toolkit/crashreporter/client/crashreporter_osx.mm
@@ -489,19 +489,16 @@ bool UIGetSettingsPath(const string& ven
   string tempPath = [destPath UTF8String];
   if (!UIEnsurePathExists(tempPath))
     return false;
 
   destPath = [destPath stringByAppendingPathComponent: @"Crash Reports"];
 
   settingsPath = [destPath UTF8String];
 
-  if (!UIEnsurePathExists(settingsPath))
-    return false;
-
   return true;
 }
 
 bool UIEnsurePathExists(const string& path)
 {
   int ret = mkdir(path.c_str(), S_IRWXU);
   int e = errno;
   if (ret == -1 && e != EEXIST)
--- a/toolkit/crashreporter/client/crashreporter_win.cpp
+++ b/toolkit/crashreporter/client/crashreporter_win.cpp
@@ -692,19 +692,21 @@ void UIShowCrashUI(const string& dumpFil
 
   for (StringTable::const_iterator i = queryParameters.begin();
        i != queryParameters.end();
        i++) {
     gQueryParameters[UTF8ToWide(i->first)] = UTF8ToWide(i->second);
   }
 
   if (gQueryParameters.find(L"Vendor") != gQueryParameters.end()) {
-    gCrashReporterKey = L"Software\\" +
-                        gQueryParameters[L"Vendor"] +
-                        L"\\Crash Reporter";
+    gCrashReporterKey = L"Software\\";
+    if (!gQueryParameters[L"Vendor"].empty()) {
+      gCrashReporterKey += gQueryParameters[L"Vendor"] + L"\\";
+    }
+    gCrashReporterKey += gQueryParameters[L"Name"] + L"\\Crash Reporter";
   }
 
   gRestartArgs = restartArgs;
 
   DialogBoxParam(NULL, MAKEINTRESOURCE(IDD_SENDDIALOG), NULL,
                  (DLGPROC)CrashReporterDialogProc, 0);
 }
 
@@ -744,18 +746,16 @@ bool UIGetSettingsPath(const string& ven
                                NULL,
                                0,
                                path)))  {
     if (!vendor.empty()) {
       PathAppend(path, UTF8ToWide(vendor).c_str());
     }
     PathAppend(path, UTF8ToWide(product).c_str());
     PathAppend(path, L"Crash Reports");
-    // in case it doesn't exist
-    CreateDirectory(path, NULL);
     settings_path = WideToUTF8(path);
     return true;
   }
   return false;
 }
 
 bool UIEnsurePathExists(const string& path)
 {
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -207,17 +207,17 @@ bool MinidumpCallback(const XP_CHAR* dum
                   O_WRONLY | O_CREAT | O_TRUNC,
                   0600);
     if (fd != -1) {
       write(fd, crashTimeString, crashTimeStringLen);
       close(fd);
     }
 #endif
   }
-  
+
 #if defined(XP_WIN32)
   XP_CHAR cmdLine[CMDLINE_SIZE];
   size = CMDLINE_SIZE;
   p = Concat(cmdLine, L"\"", &size);
   p = Concat(p, crashReporterPath, &size);
   p = Concat(p, L"\" \"", &size);
   p = Concat(p, minidumpPath, &size);
   Concat(p, L"\"", &size);
@@ -479,17 +479,17 @@ GetFileContents(nsIFile* aFile, nsACStri
 
 // Function typedef for initializing a piece of data that we
 // don't already have.
 typedef nsresult (*InitDataFunc)(nsACString&);
 
 // Attempt to read aFile's contents into aContents, if aFile
 // does not exist, create it and initialize its contents
 // by calling aInitFunc for the data.
-static nsresult 
+static nsresult
 GetOrInit(nsIFile* aDir, const nsACString& filename,
           nsACString& aContents, InitDataFunc aInitFunc)
 {
   PRBool exists;
 
   nsCOMPtr<nsIFile> dataFile;
   nsresult rv = aDir->Clone(getter_AddRefs(dataFile));
   NS_ENSURE_SUCCESS(rv, rv);
@@ -532,46 +532,46 @@ InitUserID(nsACString& aUserID)
 #if defined(XP_WIN)
   HRESULT hr = CoCreateGuid((GUID*)&id);
   if (NS_FAILED(hr))
     return NS_ERROR_FAILURE;
 #elif defined(XP_MACOSX)
   CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
   if (!uuid)
     return NS_ERROR_FAILURE;
-  
+
   CFUUIDBytes bytes = CFUUIDGetUUIDBytes(uuid);
   memcpy(&id, &bytes, sizeof(nsID));
 
   CFRelease(uuid);
 #else
   // UNIX or some such thing
   id.m0 = random();
   id.m1 = random();
   id.m2 = random();
   *reinterpret_cast<PRUint32*>(&id.m3[0]) = random();
   *reinterpret_cast<PRUint32*>(&id.m3[4]) = random();
 #endif
 
   nsCAutoString id_str(id.ToString());
   aUserID = Substring(id_str, 1, id_str.Length()-2);
-  
+
   return NS_OK;
 }
 
 // Init the "install time" data.  We're taking an easy way out here
 // and just setting this to "the time when this version was first run".
 static nsresult
 InitInstallTime(nsACString& aInstallTime)
 {
   time_t t = time(NULL);
   char buf[16];
   sprintf(buf, "%ld", t);
   aInstallTime = buf;
-  
+
   return NS_OK;
 }
 
 // Annotate the crash report with a Unique User ID and time
 // since install.  Also do some prep work for recording
 // time since last crash, which must be calculated at
 // crash time.
 // If any piece of data doesn't exist, initialize it first.
@@ -589,16 +589,38 @@ nsresult SetupExtraData(nsILocalFile* aA
   rv = dataDirectory->Exists(&exists);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!exists) {
     rv = dataDirectory->Create(nsIFile::DIRECTORY_TYPE, 0700);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
+  // Save this path in the environment for the crash reporter application.
+  nsCAutoString dataDirEnv("MOZ_CRASHREPORTER_DATA_DIRECTORY=");
+
+#if defined(XP_WIN32)
+  nsAutoString dataDirectoryPath;
+  rv = dataDirectory->GetPath(dataDirectoryPath);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  AppendUTF16toUTF8(dataDirectoryPath, dataDirEnv);
+#else
+  nsCAutoString dataDirectoryPath;
+  rv = dataDirectory->GetNativePath(dataDirectoryPath);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  dataDirEnv.Append(dataDirectoryPath);
+#endif
+
+  char* env = ToNewCString(dataDirEnv);
+  NS_ENSURE_TRUE(env, NS_ERROR_OUT_OF_MEMORY);
+
+  PR_SetEnv(env);
+
   nsCAutoString data;
   if(NS_SUCCEEDED(GetOrInit(dataDirectory, NS_LITERAL_CSTRING("UserID"),
                             data, InitUserID)))
     AnnotateCrashReport(NS_LITERAL_CSTRING("UserID"), data);
 
   if(NS_SUCCEEDED(GetOrInit(dataDirectory,
                             NS_LITERAL_CSTRING("InstallTime") + aBuildID,
                             data, InitInstallTime)))
@@ -663,17 +685,17 @@ nsresult UnsetExceptionHandler()
 
   return NS_OK;
 }
 
 static void ReplaceChar(nsCString& str, const nsACString& character,
                         const nsACString& replacement)
 {
   nsCString::const_iterator start, end;
-  
+
   str.BeginReading(start);
   str.EndReading(end);
 
   while (FindInReadable(character, start, end)) {
     PRInt32 pos = end.size_backward();
     str.Replace(pos - 1, 1, replacement);
 
     str.BeginReading(start);
@@ -708,17 +730,17 @@ nsresult AnnotateCrashReport(const nsACS
   if (DoFindInReadable(key, NS_LITERAL_CSTRING("=")) ||
       DoFindInReadable(key, NS_LITERAL_CSTRING("\n")))
     return NS_ERROR_INVALID_ARG;
 
   if (DoFindInReadable(data, NS_LITERAL_CSTRING("\0")))
     return NS_ERROR_INVALID_ARG;
 
   nsCString escapedData(data);
-  
+
   // escape backslashes
   ReplaceChar(escapedData, NS_LITERAL_CSTRING("\\"),
               NS_LITERAL_CSTRING("\\\\"));
   // escape newlines
   ReplaceChar(escapedData, NS_LITERAL_CSTRING("\n"),
               NS_LITERAL_CSTRING("\\n"));
 
   nsresult rv = crashReporterAPIData_Hash->Put(key, escapedData);
--- a/toolkit/xre/nsAppData.cpp
+++ b/toolkit/xre/nsAppData.cpp
@@ -73,16 +73,17 @@ ScopedAppData::ScopedAppData(const nsXRE
   this->size = aAppData->size;
 
   SetAllocatedString(this->vendor, aAppData->vendor);
   SetAllocatedString(this->name, aAppData->name);
   SetAllocatedString(this->version, aAppData->version);
   SetAllocatedString(this->buildID, aAppData->buildID);
   SetAllocatedString(this->ID, aAppData->ID);
   SetAllocatedString(this->copyright, aAppData->copyright);
+  SetAllocatedString(this->profile, aAppData->profile);
   SetStrongPtr(this->directory, aAppData->directory);
   this->flags = aAppData->flags;
 
   if (aAppData->size > offsetof(nsXREAppData, xreDirectory)) {
     SetStrongPtr(this->xreDirectory, aAppData->xreDirectory);
     SetAllocatedString(this->minVersion, aAppData->minVersion);
     SetAllocatedString(this->maxVersion, aAppData->maxVersion);
   }
@@ -95,16 +96,17 @@ ScopedAppData::ScopedAppData(const nsXRE
 ScopedAppData::~ScopedAppData()
 {
   SetAllocatedString(this->vendor, nsnull);
   SetAllocatedString(this->name, nsnull);
   SetAllocatedString(this->version, nsnull);
   SetAllocatedString(this->buildID, nsnull);
   SetAllocatedString(this->ID, nsnull);
   SetAllocatedString(this->copyright, nsnull);
+  SetAllocatedString(this->profile, nsnull);
 
   NS_IF_RELEASE(this->directory);
 
   SetStrongPtr(this->xreDirectory, (nsILocalFile*) nsnull);
   SetAllocatedString(this->minVersion, nsnull);
   SetAllocatedString(this->maxVersion, nsnull);
 
   SetAllocatedString(this->crashReporterURL, nsnull);
@@ -203,16 +205,17 @@ XRE_ParseAppData(nsILocalFile* aINIFile,
 
   ReadString strings[] = {
     { "App", "Vendor",    &aAppData->vendor },
     { "App", "Name",      &aAppData->name },
     { "App", "Version",   &aAppData->version },
     { "App", "BuildID",   &aAppData->buildID },
     { "App", "ID",        &aAppData->ID },
     { "App", "Copyright", &aAppData->copyright },
+    { "App", "Profile",   &aAppData->profile },
     { nsnull }
   };
   ReadStrings(parser, strings);
 
   ReadFlag flags[] = {
     { "XRE", "EnableProfileMigrator", NS_XRE_ENABLE_PROFILE_MIGRATOR },
     { "XRE", "EnableExtensionManager", NS_XRE_ENABLE_EXTENSION_MANAGER },
     { nsnull }
--- a/toolkit/xre/nsXREDirProvider.cpp
+++ b/toolkit/xre/nsXREDirProvider.cpp
@@ -57,23 +57,24 @@
 #include "nsDirectoryServiceUtils.h"
 #include "nsXULAppAPI.h"
 
 #include "nsINIParser.h"
 #include "nsDependentString.h"
 #include "nsCOMArray.h"
 #include "nsArrayEnumerator.h"
 #include "nsEnumeratorUtils.h"
+#include "nsReadableUtils.h"
 
 #include <stdlib.h>
 
 #ifdef XP_WIN
 #include <windows.h>
 #include <shlobj.h>
-// This is not defined by VC6. 
+// This is not defined by VC6.
 #ifndef CSIDL_LOCAL_APPDATA
 #define CSIDL_LOCAL_APPDATA             0x001C
 #endif
 #endif
 #ifdef XP_MACOSX
 #include "nsILocalFileMac.h"
 #endif
 #ifdef XP_BEOS
@@ -127,17 +128,17 @@ nsXREDirProvider::Initialize(nsIFile *aX
 }
 
 nsresult
 nsXREDirProvider::SetProfile(nsIFile* aDir, nsIFile* aLocalDir)
 {
   NS_ASSERTION(aDir && aLocalDir, "We don't support no-profile apps yet!");
 
   nsresult rv;
-  
+
   rv = EnsureDirectoryExists(aDir);
   if (NS_FAILED(rv))
     return rv;
 
   rv = EnsureDirectoryExists(aLocalDir);
   if (NS_FAILED(rv))
     return rv;
 
@@ -502,17 +503,17 @@ nsXREDirProvider::GetFiles(const char* a
   if (NS_FAILED(rv))
     return rv;
 
   return NS_SUCCESS_AGGREGATE_RESULT;
 }
 
 static void
 LoadExtensionDirectories(nsINIParser &parser,
-                         const char *aSection, 
+                         const char *aSection,
                          nsCOMArray<nsIFile> &aDirectories)
 {
   nsresult rv;
   PRInt32 i = 0;
   do {
     nsCAutoString buf("Extension");
     buf.AppendInt(i++);
 
@@ -578,17 +579,17 @@ nsXREDirProvider::LoadAppBundleDirs()
 {
   if (!mXULAppDir)
     return;
 
   nsCOMPtr<nsIFile> dir;
   nsresult rv = mXULAppDir->Clone(getter_AddRefs(dir));
   if (NS_FAILED(rv))
     return;
-  
+
   dir->AppendNative(NS_LITERAL_CSTRING("distribution"));
   dir->AppendNative(NS_LITERAL_CSTRING("bundles"));
 
   PRBool exists;
   if (NS_FAILED(dir->Exists(&exists)) || !exists)
     return;
 
   nsCOMPtr<nsISimpleEnumerator> e;
@@ -628,17 +629,17 @@ nsresult
 nsXREDirProvider::GetFilesInternal(const char* aProperty,
                                    nsISimpleEnumerator** aResult)
 {
   nsresult rv = NS_OK;
   *aResult = nsnull;
 
   if (!strcmp(aProperty, XRE_EXTENSIONS_DIR_LIST)) {
     nsCOMArray<nsIFile> directories;
-    
+
     static const char *const kAppendNothing[] = { nsnull };
 
     LoadBundleDirectories();
     LoadDirsIntoArray(mAppBundleDirectories,
                       kAppendNothing, directories);
     LoadDirsIntoArray(mExtensionDirectories,
                       kAppendNothing, directories);
 
@@ -707,17 +708,17 @@ nsXREDirProvider::GetFilesInternal(const
     LoadDirsIntoArray(mAppBundleDirectories,
                       kAppendChromeManifests,
                       manifests);
     LoadDirsIntoArray(mExtensionDirectories,
                       kAppendChromeManifests,
                       manifests);
 
     rv = NS_NewArrayEnumerator(aResult, manifests);
-  }  
+  }
   else if (!strcmp(aProperty, NS_SKIN_MANIFESTS_FILE_LIST)) {
     nsCOMArray<nsIFile> manifests;
 
     LoadBundleDirectories();
     LoadDirsIntoArray(mThemeDirectories,
                       kAppendChromeManifests, manifests);
 
     rv = NS_NewArrayEnumerator(aResult, manifests);
@@ -845,89 +846,90 @@ nsXREDirProvider::DoShutdown()
 
       // Phase 3: Notify observers of a profile change
       obssvc->NotifyObservers(cs, "profile-before-change", kShutdownPersist);
     }
     mProfileNotified = PR_FALSE;
   }
 }
 
-static void 
-GetProfileFolderName(char* aProfileFolderName, const char* aSource)
-{
-  const char* reading = aSource;
-
-  while (*reading) {
-    *aProfileFolderName = tolower(*reading);
-    ++aProfileFolderName; ++reading;
-  }
-  *aProfileFolderName = '\0';
-}
-
 #ifdef XP_WIN
 static nsresult
-GetShellFolderPath(int folder, char result[MAXPATHLEN])
+GetShellFolderPath(int folder, nsAString& _retval)
 {
   LPITEMIDLIST pItemIDList = NULL;
 
+  PRUnichar* buf;
+  PRUint32 bufLength = _retval.GetMutableData(&buf, MAXPATHLEN);
+  NS_ENSURE_TRUE(bufLength >= MAXPATHLEN, NS_ERROR_OUT_OF_MEMORY);
+
   nsresult rv;
   if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, folder, &pItemIDList)) &&
-      SUCCEEDED(SHGetPathFromIDList(pItemIDList, result))) {
+      SHGetPathFromIDListW(pItemIDList, buf)) {
+    // We're going to use wcslen (wcsnlen not available in msvc7.1) so make
+    // sure to null terminate.
+    buf[bufLength - 1] = L'\0';
+    _retval.SetLength(wcslen(buf));
     rv = NS_OK;
   } else {
+    _retval.SetLength(0);
     rv = NS_ERROR_NOT_AVAILABLE;
   }
 
   CoTaskMemFree(pItemIDList);
 
   return rv;
 }
 
 nsresult
 nsXREDirProvider::GetUpdateRootDir(nsIFile* *aResult)
 {
   nsCOMPtr<nsIFile> appDir = GetAppDir();
-  nsCAutoString appPath;
-  nsresult rv = appDir->GetNativePath(appPath);
+
+  nsAutoString appPath;
+  nsresult rv = appDir->GetPath(appPath);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // AppDir may be a short path. Convert to long path to make sure
   // the consistency of the update folder location
-  nsCString longPath;
-  char *buf;
-  longPath.GetMutableData(&buf, MAXPATHLEN);
-  DWORD len = GetLongPathName(appPath.get(), buf, MAXPATHLEN);
+  nsString longPath;
+  PRUnichar* buf;
+
+  PRUint32 bufLength = longPath.GetMutableData(&buf, MAXPATHLEN);
+  NS_ENSURE_TRUE(bufLength >= MAXPATHLEN, NS_ERROR_OUT_OF_MEMORY);
+
+  DWORD len = GetLongPathNameW(appPath.get(), buf, bufLength);
+
   // Failing GetLongPathName() is not fatal.
-  if (len <= 0 || len >= MAXPATHLEN)
+  if (len <= 0 || len >= bufLength)
     longPath.Assign(appPath);
   else
     longPath.SetLength(len);
 
   // Use <UserLocalDataDir>\updates\<relative path to app dir from
   // Program Files> if app dir is under Program Files to avoid the
   // folder virtualization mess on Windows Vista
-  char programFiles[MAXPATHLEN];
+  nsAutoString programFiles;
   rv = GetShellFolderPath(CSIDL_PROGRAM_FILES, programFiles);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  PRUint32 programFilesLen = strlen(programFiles);
-  programFiles[programFilesLen++] = '\\';
-  programFiles[programFilesLen] = '\0';
+  programFiles.AppendLiteral("\\");
+  PRUint32 programFilesLen = programFiles.Length();
 
   if (longPath.Length() < programFilesLen)
     return NS_ERROR_FAILURE;
 
-  if (_strnicmp(programFiles, longPath.get(), programFilesLen) != 0)
+  if (_wcsnicmp(programFiles.get(), longPath.get(), programFilesLen) != 0)
     return NS_ERROR_FAILURE;
 
   nsCOMPtr<nsILocalFile> updRoot;
   rv = GetUserLocalDataDirectory(getter_AddRefs(updRoot));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = updRoot->AppendRelativeNativePath(Substring(longPath, programFilesLen));
+  rv = updRoot->AppendRelativePath(Substring(longPath, programFilesLen));
   NS_ENSURE_SUCCESS(rv, rv);
 
   NS_ADDREF(*aResult = updRoot);
   return NS_OK;
 }
 #endif
 
 nsresult
@@ -983,67 +985,44 @@ nsXREDirProvider::GetUserDataDirectory(n
   nsCOMPtr<nsILocalFile> localDir;
 
 #if defined(XP_MACOSX)
   FSRef fsRef;
   OSType folderType;
   if (aLocal) {
     folderType = kCachedDataFolderType;
   } else {
-#ifdef MOZ_THUNDERBIRD 
+#ifdef MOZ_THUNDERBIRD
     folderType = kDomainLibraryFolderType;
 #else
     folderType = kApplicationSupportFolderType;
-#endif 
+#endif
   }
   OSErr err = ::FSFindFolder(kUserDomain, folderType, kCreateFolder, &fsRef);
   NS_ENSURE_FALSE(err, NS_ERROR_FAILURE);
 
   rv = NS_NewNativeLocalFile(EmptyCString(), PR_TRUE, getter_AddRefs(localDir));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsILocalFileMac> dirFileMac = do_QueryInterface(localDir);
   NS_ENSURE_TRUE(dirFileMac, NS_ERROR_UNEXPECTED);
 
   rv = dirFileMac->InitWithFSRef(&fsRef);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // Note that MacOS ignores the vendor when creating the profile hierarchy - all
-  // application preferences directories live alongside one another in 
-  // ~/Library/Application Support/
-  rv = dirFileMac->AppendNative(nsDependentCString(gAppData->name));
-  NS_ENSURE_SUCCESS(rv, rv);
+  localDir = do_QueryInterface(dirFileMac, &rv);
 #elif defined(XP_WIN)
-  char path[MAXPATHLEN];
-
-  // CSIDL_LOCAL_APPDATA is only defined on newer versions of Windows.  If the
-  // OS does not understand it, then we'll fallback to the regular APPDATA
-  // location.  If neither is defined, then we fallback to the Windows folder.
-
+  nsString path;
   if (aLocal)
     rv = GetShellFolderPath(CSIDL_LOCAL_APPDATA, path);
   if (!aLocal || NS_FAILED(rv))
     rv = GetShellFolderPath(CSIDL_APPDATA, path);
-
-  if (NS_FAILED(rv) && !GetWindowsDirectory(path, sizeof(path))) {
-    NS_WARNING("Aaah, no windows directory!");
-    return NS_ERROR_FAILURE;
-  }
-
-  rv = NS_NewNativeLocalFile(nsDependentCString(path),
-                             PR_TRUE, getter_AddRefs(localDir));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (gAppData->vendor) {
-    rv = localDir->AppendNative(nsDependentCString(gAppData->vendor));
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-  rv = localDir->AppendNative(nsDependentCString(gAppData->name));
-  NS_ENSURE_SUCCESS(rv, rv);
-
+  rv = NS_NewLocalFile(path, PR_TRUE, getter_AddRefs(localDir));
 #elif defined(XP_OS2)
 #if 0 /* For OS/2 we want to always use MOZILLA_HOME */
   // we want an environment variable of the form
   // FIREFOX_HOME, etc
   nsDependentCString envVar(nsDependentCString(gAppData->name));
   envVar.Append("_HOME");
   char *pHome = getenv(envVar.get());
 #endif
@@ -1056,89 +1035,51 @@ nsXREDirProvider::GetUserDataDirectory(n
     PTIB ptib;
     char appDir[CCHMAXPATH];
 
     DosGetInfoBlocks(&ptib, &ppib);
     DosQueryModuleName(ppib->pib_hmte, CCHMAXPATH, appDir);
     *strrchr(appDir, '\\') = '\0';
     rv = NS_NewNativeLocalFile(nsDependentCString(appDir), PR_TRUE, getter_AddRefs(localDir));
   }
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (gAppData->vendor) {
-    rv = localDir->AppendNative(nsDependentCString(gAppData->vendor));
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-  rv = localDir->AppendNative(nsDependentCString(gAppData->name));
-  NS_ENSURE_SUCCESS(rv, rv);
-
 #elif defined(XP_BEOS)
   char appDir[MAXPATHLEN];
   if (find_directory(B_USER_SETTINGS_DIRECTORY, NULL, true, appDir, MAXPATHLEN))
     return NS_ERROR_FAILURE;
 
   int len = strlen(appDir);
   appDir[len]   = '/';
   appDir[len+1] = '\0';
 
   rv = NS_NewNativeLocalFile(nsDependentCString(appDir), PR_TRUE,
                              getter_AddRefs(localDir));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (gAppData->vendor) {
-    rv = localDir->AppendNative(nsDependentCString(gAppData->vendor));
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-  rv = localDir->AppendNative(nsDependentCString(gAppData->name));
-  NS_ENSURE_SUCCESS(rv, rv);
-
 #elif defined(XP_UNIX)
   const char* homeDir = getenv("HOME");
   if (!homeDir || !*homeDir)
     return NS_ERROR_FAILURE;
 
   rv = NS_NewNativeLocalFile(nsDependentCString(homeDir), PR_TRUE,
                              getter_AddRefs(localDir));
+#else
+#error "Don't know how to get product dir on your platform"
+#endif
   NS_ENSURE_SUCCESS(rv, rv);
- 
-  char* appNameFolder = nsnull;
-  char profileFolderName[MAXPATHLEN] = ".";
- 
-  // Offset 1 for the outermost folder to make it hidden (i.e. using the ".")
-  char* writing = profileFolderName + 1;
-  if (gAppData->vendor) {
-    GetProfileFolderName(writing, gAppData->vendor);
-    
-    rv = localDir->AppendNative(nsDependentCString(profileFolderName));
-    NS_ENSURE_SUCCESS(rv, rv);
- 
-    char temp[MAXPATHLEN];
-    GetProfileFolderName(temp, gAppData->name);
-    appNameFolder = temp;
-  }
-  else {
-    GetProfileFolderName(writing, gAppData->name);
-    appNameFolder = profileFolderName;
-  }
-  rv = localDir->AppendNative(nsDependentCString(appNameFolder));
+
+  rv = AppendProfilePath(localDir);
   NS_ENSURE_SUCCESS(rv, rv);
-#else
-#error dont_know_how_to_get_product_dir_on_your_platform
-#endif
 
 #ifdef DEBUG_jungshik
   nsCAutoString cwd;
   localDir->GetNativePath(cwd);
   printf("nsXREDirProvider::GetUserDataDirectory: %s\n", cwd.get());
 #endif
   rv = EnsureDirectoryExists(localDir);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  *aFile = localDir;
-  NS_ADDREF(*aFile);
+  NS_ADDREF(*aFile = localDir);
   return NS_OK;
 }
 
 nsresult
 nsXREDirProvider::EnsureDirectoryExists(nsIFile* aDirectory)
 {
   PRBool exists;
   nsresult rv = aDirectory->Exists(&exists);
@@ -1160,46 +1101,143 @@ nsXREDirProvider::EnsureDirectoryExists(
   return rv;
 }
 
 void
 nsXREDirProvider::EnsureProfileFileExists(nsIFile *aFile)
 {
   nsresult rv;
   PRBool exists;
-    
+
   rv = aFile->Exists(&exists);
   if (NS_FAILED(rv) || exists) return;
-  
+
   nsCAutoString leafName;
   rv = aFile->GetNativeLeafName(leafName);
   if (NS_FAILED(rv)) return;
 
   nsCOMPtr<nsIFile> defaultsFile;
   rv = GetProfileDefaultsDir(getter_AddRefs(defaultsFile));
   if (NS_FAILED(rv)) return;
 
   rv = defaultsFile->AppendNative(leafName);
   if (NS_FAILED(rv)) return;
-  
+
   defaultsFile->CopyToNative(mProfileDir, EmptyCString());
 }
 
 nsresult
 nsXREDirProvider::GetProfileDefaultsDir(nsIFile* *aResult)
 {
   NS_ASSERTION(mGREDir, "nsXREDirProvider not initialized.");
   NS_PRECONDITION(aResult, "Null out-param");
 
   nsresult rv;
   nsCOMPtr<nsIFile> defaultsDir;
 
   rv = GetAppDir()->Clone(getter_AddRefs(defaultsDir));
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = defaultsDir->AppendNative(NS_LITERAL_CSTRING("defaults"));
-  rv |= defaultsDir->AppendNative(NS_LITERAL_CSTRING("profile"));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = defaultsDir->AppendNative(NS_LITERAL_CSTRING("profile"));
   NS_ENSURE_SUCCESS(rv, rv);
 
   NS_ADDREF(*aResult = defaultsDir);
   return NS_OK;
 }
 
+nsresult
+nsXREDirProvider::AppendProfilePath(nsIFile* aFile)
+{
+  NS_ASSERTION(aFile, "Null pointer!");
+
+  nsresult rv;
+
+#if defined (XP_MACOSX)
+  if (gAppData->profile) {
+    rv = AppendProfileString(aFile, gAppData->profile);
+  }
+  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));
+  }
+  NS_ENSURE_SUCCESS(rv, rv);
+
+#elif defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS)
+  if (gAppData->profile) {
+    rv = AppendProfileString(aFile, gAppData->profile);
+  }
+  else {
+    if (gAppData->vendor) {
+      rv = aFile->AppendNative(nsDependentCString(gAppData->vendor));
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+    rv = aFile->AppendNative(nsDependentCString(gAppData->name));
+  }
+  NS_ENSURE_SUCCESS(rv, rv);
+
+#elif defined(XP_UNIX)
+  // Make it hidden (i.e. using the ".")
+  nsCAutoString folder(".");
+
+  if (gAppData->profile) {
+    // Skip any leading path characters
+    const char* profileStart = gAppData->profile;
+    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);
+      ToLowerCase(folder);
+
+      rv = aFile->AppendNative(folder);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      folder.Truncate();
+    }
+
+    folder.Append(gAppData->name);
+    ToLowerCase(folder);
+
+    rv = aFile->AppendNative(folder);
+  }
+  NS_ENSURE_SUCCESS(rv, rv);
+
+#else
+#error "Don't know how to get profile path on your platform"
+#endif
+  return NS_OK;
+}
+
+nsresult
+nsXREDirProvider::AppendProfileString(nsIFile* aFile, const char* aPath)
+{
+  NS_ASSERTION(aFile, "Null file!");
+  NS_ASSERTION(aPath, "Null path!");
+
+  nsCAutoString pathDup(aPath);
+
+  char* path = pathDup.BeginWriting();
+
+  nsresult rv;
+  char* subdir;
+  while ((subdir = NS_strtok("/\\", &path))) {
+    rv = aFile->AppendNative(nsDependentCString(subdir));
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  return NS_OK;
+}
--- a/toolkit/xre/nsXREDirProvider.h
+++ b/toolkit/xre/nsXREDirProvider.h
@@ -81,17 +81,17 @@ public:
     return GetUserDataDirectory(aFile, PR_FALSE);
   }
   static nsresult GetUserLocalDataDirectory(nsILocalFile* *aFile) {
     return GetUserDataDirectory(aFile, PR_TRUE);
   }
 
   /* make sure you clone it, if you need to do stuff to it */
   nsIFile* GetGREDir() { return mGREDir; }
-  nsIFile* GetAppDir() { 
+  nsIFile* GetAppDir() {
     if (mXULAppDir)
       return mXULAppDir;
     return mGREDir;
   }
 
   /**
    * Get the directory under which update directory is created.
    * This method may be called before XPCOM is started. aResult
@@ -114,16 +114,24 @@ public:
   nsresult GetProfileDir(nsIFile* *aResult);
 
 protected:
   nsresult GetFilesInternal(const char* aProperty, nsISimpleEnumerator** aResult);
   static nsresult GetUserDataDirectory(nsILocalFile* *aFile, PRBool aLocal);
   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);
+
+  // Internal helper that splits a path into components using the '/' and '\\'
+  // delimiters.
+  static inline nsresult AppendProfileString(nsIFile* aFile, const char* aPath);
+
   // Calculate all bundle directories, including distribution bundles,
   // extensions, and themes
   void LoadBundleDirectories();
   void LoadAppBundleDirs();
 
   void Append(nsIFile* aDirectory);
 
   nsCOMPtr<nsIDirectoryServiceProvider> mAppProvider;
--- a/toolkit/xre/nsXULAppAPI.h
+++ b/toolkit/xre/nsXULAppAPI.h
@@ -89,17 +89,17 @@ struct nsXREAppData
 
   /**
    * The major version, e.g. "0.8.0+". Optional (may be null), but
    * required for advanced application features such as the extension
    * manager and update service. Must not be the empty string.
    */
   const char *version;
 
-  /** 
+  /**
    * The application's build identifier, e.g. "2004051604"
    */
   const char *buildID;
 
   /**
    * The application's UUID. Used by the extension manager to determine
    * compatible extensions. Optional, but required for advanced application
    * features such as the extension manager and update service.
@@ -133,16 +133,33 @@ struct nsXREAppData
    */
   const char *minVersion;
   const char *maxVersion;
 
   /**
    * The server URL to send crash reports to.
    */
   const char *crashReporterURL;
+
+  /**
+   * The profile directory that will be used. Optional (may be null). Must not
+   * be the empty string, must be ASCII. The path is split into components
+   * along the path separator characters '/' and '\'.
+   *
+   * The application data directory ("UAppData", see below) is normally
+   * composed as follows, where $HOME is platform-specific:
+   *
+   *   UAppData = $HOME[/$vendor]/$name
+   *
+   * If present, the 'profile' string will be used instead of the combination of
+   * vendor and name as follows:
+   *
+   *   UAppData = $HOME/$profile
+   */
+  const char *profile;
 };
 
 /**
  * Indicates whether or not the profile migrator service may be
  * invoked at startup when creating a profile.
  */
 #define NS_XRE_ENABLE_PROFILE_MIGRATOR (1 << 1)
 
@@ -159,21 +176,43 @@ struct nsXREAppData
 
 /**
  * The contract id for the nsIXULAppInfo service.
  */
 #define XULAPPINFO_SERVICE_CONTRACTID \
   "@mozilla.org/xre/app-info;1"
 
 /**
- * A directory service key which provides the platform-correct
- * "application data" directory.
- * Windows: Documents and Settings\<User>\Application Data\<Vendor>\<Application>
- * Unix: ~/.<vendor>/<application>
- * Mac: ~/Library/Application Supports/<Application>
+ * A directory service key which provides the platform-correct "application
+ * data" directory as follows, where $name and $vendor are as defined above and
+ * $vendor is optional:
+ *
+ * Windows:
+ *   HOME = Documents and Settings\$USER\Application Data
+ *   UAppData = $HOME[\$vendor]\$name
+ *
+ * Unix:
+ *   HOME = ~
+ *   UAppData = $HOME/.[$vendor/]$name
+ *
+ * Mac:
+ *   HOME = ~
+ *   UAppData = $HOME/Library/Application Support/$name
+ *
+ * Note that the "profile" member above will change the value of UAppData as
+ * follows:
+ *
+ * Windows:
+ *   UAppData = $HOME\$profile
+ *
+ * Unix:
+ *   UAppData = $HOME/.$profile
+ *
+ * Mac:
+ *   UAppData = $HOME/Library/Application Support/$profile
  */
 #define XRE_USER_APP_DATA_DIR "UAppData"
 
 /**
  * A directory service key which provides a list of all enabled extension
  * directories. The list includes compatible platform-specific extension
  * subdirectories.
  *
@@ -217,17 +256,17 @@ struct nsXREAppData
  *
  * @param argc/argv Command-line parameters to pass to the application. These
  *                  are in the "native" character set.
  *
  * @param aAppData  Information about the application to be run.
  *
  * @return         A native result code suitable for returning from main().
  *
- * @note           If the binary is linked against the  standalone XPCOM glue,
+ * @note           If the binary is linked against the standalone XPCOM glue,
  *                 XPCOMGlueStartup() should be called before this method.
  *
  * @note           XXXbsmedberg Nobody uses the glue yet, but there is a
  *                 potential problem: on windows, the standalone glue calls
  *                 SetCurrentDirectory, and relative paths on the command line
  *                 won't be correct.
  */
 XRE_API(int,