From 949bd03217d1e6e405af39571a6a74e4a96dbb2d Mon Sep 17 00:00:00 2001 From: Olli Pettay Date: Thu, 24 May 2012 17:29:52 +0300 Subject: [PATCH] Bug 756131 - API for creating default profiles for webapps, r=benjamin --- toolkit/profile/nsIToolkitProfileService.idl | 32 +++- toolkit/profile/nsToolkitProfileService.cpp | 160 ++++++++++++++++--- toolkit/xre/nsXREDirProvider.cpp | 133 ++++++++++----- toolkit/xre/nsXREDirProvider.h | 26 ++- 4 files changed, 283 insertions(+), 68 deletions(-) diff --git a/toolkit/profile/nsIToolkitProfileService.idl b/toolkit/profile/nsIToolkitProfileService.idl index 66ee63558a0a..98cbc0a56824 100644 --- a/toolkit/profile/nsIToolkitProfileService.idl +++ b/toolkit/profile/nsIToolkitProfileService.idl @@ -6,11 +6,12 @@ #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; @@ -52,6 +53,35 @@ interface nsIToolkitProfileService : nsISupports 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. diff --git a/toolkit/profile/nsToolkitProfileService.cpp b/toolkit/profile/nsToolkitProfileService.cpp index 82883a59f149..5ca3b3b54cea 100644 --- a/toolkit/profile/nsToolkitProfileService.cpp +++ b/toolkit/profile/nsToolkitProfileService.cpp @@ -56,7 +56,8 @@ private: nsToolkitProfile(const nsACString& aName, nsILocalFile* aRootDir, nsILocalFile* aLocalDir, - nsToolkitProfile* aPrev); + nsToolkitProfile* aPrev, + bool aForExternalApp); friend class nsToolkitProfileLock; @@ -64,6 +65,7 @@ private: nsCOMPtr mRootDir; nsCOMPtr mLocalDir; nsIProfileLock* mLock; + bool mForExternalApp; }; class nsToolkitProfileLock : public nsIProfileLock @@ -119,6 +121,16 @@ private: 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 mFirst; nsCOMPtr mChosen; nsCOMPtr mAppData; @@ -147,19 +159,24 @@ private: 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) @@ -190,6 +207,7 @@ 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; @@ -203,6 +221,8 @@ 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; @@ -447,7 +467,7 @@ nsToolkitProfileService::Init() 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); @@ -606,25 +626,111 @@ static void SaltProfileName(nsACString& aName) 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 appData; + nsresult rv = + gDirServiceProvider->GetUserDataDirectory(getter_AddRefs(appData), + false, + &aProfileName, + &aAppName, + &aVendorName); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr 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 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 rootDir (aRootDir); nsCAutoString dirName; if (!rootDir) { nsCOMPtr 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); @@ -649,8 +755,10 @@ nsToolkitProfileService::CreateProfile(nsILocalFile* aRootDir, else { nsCOMPtr 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); @@ -687,14 +795,18 @@ nsToolkitProfileService::CreateProfile(nsILocalFile* aRootDir, 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); @@ -714,14 +826,14 @@ nsToolkitProfileService::CreateProfile(nsILocalFile* aRootDir, NS_ENSURE_SUCCESS(rv, rv); } - nsToolkitProfile* last = mFirst; + nsToolkitProfile* last = aForExternalApp ? nsnull : mFirst; if (last) { while (last->mNext) last = last->mNext; } nsCOMPtr 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); diff --git a/toolkit/xre/nsXREDirProvider.cpp b/toolkit/xre/nsXREDirProvider.cpp index 086c784c9bbf..2e21b1efa5d2 100644 --- a/toolkit/xre/nsXREDirProvider.cpp +++ b/toolkit/xre/nsXREDirProvider.cpp @@ -155,6 +155,52 @@ nsXREDirProvider::Release() return 0; } +nsresult +nsXREDirProvider::GetUserProfilesRootDir(nsIFile** aResult, + const nsACString* aProfileName, + const nsACString* aAppName, + const nsACString* aVendorName) +{ + nsCOMPtr 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 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) @@ -245,28 +291,10 @@ nsXREDirProvider::GetFile(const char* aProperty, bool* aPersistent, 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 lf; @@ -1166,13 +1194,16 @@ nsXREDirProvider::GetSystemExtensionsDirectory(nsILocalFile** aFile) #endif nsresult -nsXREDirProvider::GetUserDataDirectory(nsILocalFile** aFile, bool aLocal) +nsXREDirProvider::GetUserDataDirectory(nsILocalFile** aFile, bool aLocal, + const nsACString* aProfileName, + const nsACString* aAppName, + const nsACString* aVendorName) { nsCOMPtr 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 @@ -1290,37 +1321,58 @@ nsXREDirProvider::AppendSysUserExtensionPath(nsIFile* aFile) 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); @@ -1328,15 +1380,18 @@ nsXREDirProvider::AppendProfilePath(nsIFile* aFile) // 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++; @@ -1351,8 +1406,8 @@ nsXREDirProvider::AppendProfilePath(nsIFile* aFile) 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); @@ -1361,7 +1416,7 @@ nsXREDirProvider::AppendProfilePath(nsIFile* aFile) folder.Truncate(); } - folder.Append(gAppData->name); + folder.Append(appName); ToLowerCase(folder); rv = aFile->AppendNative(folder); diff --git a/toolkit/xre/nsXREDirProvider.h b/toolkit/xre/nsXREDirProvider.h index 4e73bb176656..062941bb7e1d 100644 --- a/toolkit/xre/nsXREDirProvider.h +++ b/toolkit/xre/nsXREDirProvider.h @@ -36,6 +36,15 @@ public: 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 @@ -47,12 +56,19 @@ public: 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() { @@ -84,7 +100,6 @@ public: 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) @@ -95,7 +110,10 @@ protected: // 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);