diff --git a/mozilla/modules/libpref/src/init/all.js b/mozilla/modules/libpref/src/init/all.js index 16ec0aeca9f..6c57d399525 100644 --- a/mozilla/modules/libpref/src/init/all.js +++ b/mozilla/modules/libpref/src/init/all.js @@ -623,6 +623,15 @@ pref("mousewheel.withaltkey.sysnumlines",false); pref("profile.confirm_automigration",true); +// the amount of time (in seconds) that must elapse +// before we think your mozilla profile is defunct +// and you'd benefit from re-migrating from 4.x +// see bug #137886 for more details +// +// if -1, we never think your profile is defunct +// and users will never see the remigrate UI. +pref("profile.seconds_until_defunct", -1); + // Customizable toolbar stuff pref("custtoolbar.personal_toolbar_folder", ""); diff --git a/mozilla/profile/public/nsIProfileInternal.idl b/mozilla/profile/public/nsIProfileInternal.idl index e5327520bf1..f7d80b9bd6f 100755 --- a/mozilla/profile/public/nsIProfileInternal.idl +++ b/mozilla/profile/public/nsIProfileInternal.idl @@ -87,7 +87,8 @@ interface nsIProfileInternal : nsIProfile { void migrateProfileInfo(); void migrateAllProfiles(); - void migrateProfile(in wstring profileName, in boolean showProgressAsModalWindow); + void migrateProfile(in wstring profileName); + void remigrateProfile(in wstring profileName); void forgetCurrentProfile(); /** @@ -108,6 +109,12 @@ interface nsIProfileInternal : nsIProfile { */ nsILocalFile getOriginalProfileDir(in wstring profileName); + /** + * Returns the date on which a profile was last used. + * value is in milliseconds since midnight Jan 1, 1970 GMT (same as nsIFile) + */ + PRInt64 getProfileLastModTime(in wstring profileName); + attribute boolean automigrate; readonly attribute nsIFile defaultProfileParentDir; readonly attribute wstring firstProfile; diff --git a/mozilla/profile/resources/content/profileManager.js b/mozilla/profile/resources/content/profileManager.js index a418c37b022..ea8f1e39161 100644 --- a/mozilla/profile/resources/content/profileManager.js +++ b/mozilla/profile/resources/content/profileManager.js @@ -76,7 +76,7 @@ function RenameProfile() } } } - profile.migrateProfile( profilename, true ); + profile.migrateProfile( profilename ); } else return false; diff --git a/mozilla/profile/resources/content/profileSelection.js b/mozilla/profile/resources/content/profileSelection.js index 2914b393b37..a4cd1901568 100644 --- a/mozilla/profile/resources/content/profileSelection.js +++ b/mozilla/profile/resources/content/profileSelection.js @@ -183,7 +183,7 @@ function onStart() } } } - profile.migrateProfile( profilename, true ); + profile.migrateProfile( profilename ); } else return false; diff --git a/mozilla/profile/resources/locale/en-US/migration.properties b/mozilla/profile/resources/locale/en-US/migration.properties index e34943f8e2b..b510c3e8e68 100644 --- a/mozilla/profile/resources/locale/en-US/migration.properties +++ b/mozilla/profile/resources/locale/en-US/migration.properties @@ -9,3 +9,7 @@ mailDirName=Mail # newsDirName needs to be set to the same value as in 4.x # see bug #55449 newsDirName=News +# see nsAppShellService::CheckAndRemigrateDefunctProfile() +# for where the confirmRemigration entries are used +# LOCALIZATION NOTE: Do not translate Unicode (\uXXXX) characters. +confirmRemigration=A more recent Netscape 4.5+ version of this profile (which contains your bookmarks, email settings, address books and preferences) was found.\u000aWould you like to use the more recent profile? diff --git a/mozilla/profile/src/nsProfile.cpp b/mozilla/profile/src/nsProfile.cpp index 19b01f18674..b65586b0889 100644 --- a/mozilla/profile/src/nsProfile.cpp +++ b/mozilla/profile/src/nsProfile.cpp @@ -328,7 +328,6 @@ nsProfile::~nsProfile() if (--gInstanceCount == 0) { delete gProfileDataAccess; - delete gLocaleProfiles; NS_IF_RELEASE(sApp_PrefsDirectory50); @@ -1058,9 +1057,43 @@ NS_IMETHODIMP nsProfile::GetOriginalProfileDir(const PRUnichar *profileName, nsI NS_ENSURE_ARG_POINTER(originalDir); *originalDir = nsnull; + Update4xProfileInfo(); return gProfileDataAccess->GetOriginalProfileDir(profileName, originalDir); } +NS_IMETHODIMP nsProfile::GetProfileLastModTime(const PRUnichar *profileName, PRInt64 *_retval) +{ + NS_ENSURE_ARG(profileName); + NS_ENSURE_ARG_POINTER(_retval); + nsresult rv; + + // First, see if we can get the lastModTime from the registry. + // We only started putting it there from mozilla1.0.1 + // The mod time will be zero if it has not been set. + ProfileStruct *profileInfo = nsnull; + rv = gProfileDataAccess->GetValue(profileName, &profileInfo); + if (NS_SUCCEEDED(rv)) { + PRInt64 lastModTime = profileInfo->lastModTime; + delete profileInfo; + if (!LL_IS_ZERO(lastModTime)) { + *_retval = lastModTime; + return NS_OK; + } + } + + // Since we couldn't get a valid mod time from the registry, + // check the date of prefs.js. Since August, 2000 it is always + // written out on quitting the application. + nsCOMPtr profileDir; + rv = GetProfileDir(profileName, getter_AddRefs(profileDir)); + if (NS_FAILED(rv)) + return rv; + rv = profileDir->Append("prefs.js"); + if (NS_FAILED(rv)) + return rv; + return profileDir->GetLastModifiedTime(_retval); +} + NS_IMETHODIMP nsProfile::GetDefaultProfileParentDir(nsIFile **aDefaultProfileParentDir) { NS_ENSURE_ARG_POINTER(aDefaultProfileParentDir); @@ -1172,9 +1205,12 @@ nsProfile::SetCurrentProfile(const PRUnichar * aCurrentProfile) // Phase 3: Notify observers of a profile change observerService->NotifyObservers(subject, "profile-before-change", context.get()); + + UpdateCurrentProfileModTime(PR_FALSE); } // Do the profile switch + mCurrentProfileName.Assign(aCurrentProfile); gProfileDataAccess->SetCurrentProfile(aCurrentProfile); gProfileDataAccess->mProfileDataChanged = PR_TRUE; gProfileDataAccess->UpdateRegistry(nsnull); @@ -1233,31 +1269,37 @@ NS_IMETHODIMP nsProfile::ShutDownCurrentProfile(PRUint32 shutDownType) { nsresult rv; - nsCOMPtr observerService = - do_GetService("@mozilla.org/observer-service;1", &rv); - NS_ENSURE_TRUE(observerService, NS_ERROR_FAILURE); - - nsISupports *subject = (nsISupports *)((nsIProfile *)this); - - NS_NAMED_LITERAL_STRING(cleanseString, "shutdown-cleanse"); - NS_NAMED_LITERAL_STRING(persistString, "shutdown-persist"); - const nsAFlatString& context = (shutDownType == SHUTDOWN_CLEANSE) ? cleanseString : persistString; - - // Phase 1: See if anybody objects to the profile being changed. - mProfileChangeVetoed = PR_FALSE; - observerService->NotifyObservers(subject, "profile-approve-change", context.get()); - if (mProfileChangeVetoed) + // if shutDownType is not a well know value, skip the notifications + // see DoOnShutdown() in nsAppRunner.cpp for where we use this behaviour to our benefit + if (shutDownType == SHUTDOWN_PERSIST || shutDownType == SHUTDOWN_CLEANSE) { + nsCOMPtr observerService = + do_GetService("@mozilla.org/observer-service;1", &rv); + NS_ENSURE_TRUE(observerService, NS_ERROR_FAILURE); + + nsISupports *subject = (nsISupports *)((nsIProfile *)this); + + NS_NAMED_LITERAL_STRING(cleanseString, "shutdown-cleanse"); + NS_NAMED_LITERAL_STRING(persistString, "shutdown-persist"); + const nsAFlatString& context = (shutDownType == SHUTDOWN_CLEANSE) ? cleanseString : persistString; + + // Phase 1: See if anybody objects to the profile being changed. + mProfileChangeVetoed = PR_FALSE; + observerService->NotifyObservers(subject, "profile-approve-change", context.get()); + if (mProfileChangeVetoed) return NS_OK; - - // Phase 2: Send the "teardown" notification - observerService->NotifyObservers(subject, "profile-change-teardown", context.get()); - - // Phase 3: Notify observers of a profile change - observerService->NotifyObservers(subject, "profile-before-change", context.get()); + + // Phase 2: Send the "teardown" notification + observerService->NotifyObservers(subject, "profile-change-teardown", context.get()); + + // Phase 3: Notify observers of a profile change + observerService->NotifyObservers(subject, "profile-before-change", context.get()); + } rv = UndefineFileLocations(); NS_ASSERTION(NS_SUCCEEDED(rv), "Could not undefine file locations"); + UpdateCurrentProfileModTime(PR_TRUE); mCurrentProfileAvailable = PR_FALSE; + mCurrentProfileName.Truncate(0); return NS_OK; } @@ -1440,7 +1482,8 @@ nsresult nsProfile::SetProfileDir(const PRUnichar *profileName, nsIFile *profile rv = profileDir->Exists(&exists); if (NS_SUCCEEDED(rv) && !exists) rv = profileDir->Create(nsIFile::DIRECTORY_TYPE, 0775); - if (NS_FAILED(rv)) return rv; + if (NS_FAILED(rv)) + return rv; nsCOMPtr localFile(do_QueryInterface(profileDir)); NS_ENSURE_TRUE(localFile, NS_ERROR_FAILURE); @@ -1448,12 +1491,16 @@ nsresult nsProfile::SetProfileDir(const PRUnichar *profileName, nsIFile *profile ProfileStruct* aProfile = new ProfileStruct(); NS_ENSURE_TRUE(aProfile, NS_ERROR_OUT_OF_MEMORY); - - aProfile->profileName = profileName; + aProfile->profileName = profileName; aProfile->SetResolvedProfileDir(localFile); aProfile->isMigrated = PR_TRUE; aProfile->isImportType = PR_FALSE; + // convert "now" from microsecs to millisecs + PRInt64 oneThousand = LL_INIT(0, 1000); + PRInt64 nowInMilliSecs = PR_Now(); + LL_DIV(aProfile->creationTime, nowInMilliSecs, oneThousand); + gProfileDataAccess->SetValue(aProfile); delete aProfile; @@ -1771,6 +1818,7 @@ NS_IMETHODIMP nsProfile::ForgetCurrentProfile() gProfileDataAccess->mForgetProfileCalled = PR_TRUE; mCurrentProfileAvailable = PR_FALSE; + mCurrentProfileName.Truncate(0); return rv; } @@ -1943,6 +1991,22 @@ char * nsProfile::GetOldRegLocation() return nsnull; } +nsresult nsProfile::UpdateCurrentProfileModTime(PRBool updateRegistry) +{ + nsresult rv; + + // convert "now" from microsecs to millisecs + PRInt64 oneThousand = LL_INIT(0, 1000); + PRInt64 nowInMilliSecs = PR_Now(); + LL_DIV(nowInMilliSecs, nowInMilliSecs, oneThousand); + + rv = gProfileDataAccess->SetProfileLastModTime(mCurrentProfileName.get(), nowInMilliSecs); + if (NS_SUCCEEDED(rv) && updateRegistry) { + gProfileDataAccess->mProfileDataChanged = PR_TRUE; + gProfileDataAccess->UpdateRegistry(nsnull); + } + return rv; +} // Migrate profile information from the 4x registry to 5x registry. NS_IMETHODIMP nsProfile::MigrateProfileInfo() @@ -2080,41 +2144,22 @@ nsresult nsProfile::UndefineFileLocations() // Set the profile to the current profile....debatable. // Calls PrefMigration service to do the Copy and Diverge // of 4x Profile information -NS_IMETHODIMP -nsProfile::MigrateProfile(const PRUnichar* profileName, PRBool showProgressAsModalWindow) +nsresult +nsProfile::MigrateProfileInternal(const PRUnichar* profileName, + nsIFile* oldProfDir, + nsIFile* newProfDir) { NS_ENSURE_ARG_POINTER(profileName); - nsresult rv = NS_OK; - #if defined(DEBUG_profile) printf("Inside Migrate Profile routine.\n" ); #endif - nsCOMPtr oldProfDir; - nsCOMPtr newProfDir; - - rv = GetProfileDir(profileName, getter_AddRefs(oldProfDir)); - if (NS_FAILED(rv)) return rv; - - rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILES_ROOT_DIR, getter_AddRefs(newProfDir)); - if (NS_FAILED(rv)) return rv; - rv = newProfDir->AppendUnicode(profileName); - if (NS_FAILED(rv)) return rv; - - // This is unfortunate: There is no CreateUniqueUnicode - rv = newProfDir->CreateUnique(nsIFile::DIRECTORY_TYPE, 0775); - if (NS_FAILED(rv)) return rv; - - // always create level indirection when migrating - rv = AddLevelOfIndirection(newProfDir); - if (NS_FAILED(rv)) return rv; - // Call migration service to do the work. nsCOMPtr pPrefMigrator; - rv = nsComponentManager::CreateInstance(kPrefMigrationCID, + nsresult rv = nsComponentManager::CreateInstance(kPrefMigrationCID, nsnull, NS_GET_IID(nsIPrefMigration), getter_AddRefs(pPrefMigrator)); @@ -2137,7 +2182,7 @@ nsProfile::MigrateProfile(const PRUnichar* profileName, PRBool showProgressAsMod // you can do this a bunch of times. rv = pPrefMigrator->AddProfilePaths(oldProfDirStr, newProfDirStr); - rv = pPrefMigrator->ProcessPrefs(showProgressAsModalWindow); + rv = pPrefMigrator->ProcessPrefs(PR_TRUE); // param is ignored if (NS_FAILED(rv)) return rv; // check for diskspace errors @@ -2198,12 +2243,90 @@ nsProfile::MigrateProfile(const PRUnichar* profileName, PRBool showProgressAsMod rv = SetProfileDir(profileName, newProfDir); if (NS_FAILED(rv)) return rv; + gProfileDataAccess->SetMigratedFromDir(profileName, oldProfDirLocal); gProfileDataAccess->mProfileDataChanged = PR_TRUE; gProfileDataAccess->UpdateRegistry(nsnull); return rv; } +NS_IMETHODIMP +nsProfile::MigrateProfile(const PRUnichar* profileName) +{ + NS_ENSURE_ARG(profileName); + + nsresult rv = NS_OK; + + nsCOMPtr oldProfDir; + nsCOMPtr newProfDir; + + rv = GetProfileDir(profileName, getter_AddRefs(oldProfDir)); + if (NS_FAILED(rv)) + return rv; + + rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILES_ROOT_DIR, getter_AddRefs(newProfDir)); + if (NS_FAILED(rv)) + return rv; + + rv = newProfDir->AppendUnicode(profileName); + if (NS_FAILED(rv)) + return rv; + + rv = newProfDir->CreateUnique(nsIFile::DIRECTORY_TYPE, 0775); + if (NS_FAILED(rv)) + return rv; + + // always create level indirection when migrating + rv = AddLevelOfIndirection(newProfDir); + if (NS_FAILED(rv)) + return rv; + + return MigrateProfileInternal(profileName, oldProfDir, newProfDir); +} + +NS_IMETHODIMP +nsProfile::RemigrateProfile(const PRUnichar* profileName) +{ + NS_ENSURE_ARG_POINTER(profileName); + + nsCOMPtr profileDir; + nsresult rv = GetProfileDir(profileName, getter_AddRefs(profileDir)); + NS_ENSURE_SUCCESS(rv,rv); + + nsCOMPtr newProfileDir; + rv = profileDir->Clone(getter_AddRefs(newProfileDir)); + NS_ENSURE_SUCCESS(rv,rv); + + // The profile list used by GetOriginalProfileDir is the one with ALL 4.x + // profiles - even ones for which there's a moz profile of the same name. + nsCOMPtr oldProfileDir; + rv = GetOriginalProfileDir(profileName, getter_AddRefs(oldProfileDir)); + NS_ENSURE_SUCCESS(rv,rv); + + // In case of error, we'll restore what we've renamed. + nsXPIDLCString origDirLeafName; + rv = profileDir->GetLeafName(getter_Copies(origDirLeafName)); + NS_ENSURE_SUCCESS(rv,rv); + + // Backup what we're remigrating by renaming it and leaving in place + // XXX todo: what if .slt-old already exists? + nsCAutoString newDirLeafName(origDirLeafName + NS_LITERAL_CSTRING("-old")); + rv = profileDir->MoveTo(nsnull, newDirLeafName.get()); + NS_ENSURE_SUCCESS(rv,rv); + + // Create a new directory for the remigrated profile + rv = newProfileDir->Create(nsIFile::DIRECTORY_TYPE, 0775); + NS_ASSERTION(NS_SUCCEEDED(rv), "failed to create new directory for the remigrated profile"); + if (NS_SUCCEEDED(rv)) + rv = MigrateProfileInternal(profileName, oldProfileDir, newProfileDir); + + if (NS_FAILED(rv)) { + newProfileDir->Remove(PR_TRUE); + profileDir->MoveTo(nsnull, origDirLeafName); + } + return rv; +} + nsresult nsProfile::ShowProfileWizard(void) { @@ -2266,7 +2389,7 @@ NS_IMETHODIMP nsProfile::MigrateAllProfiles() if (NS_FAILED(rv)) return rv; for (PRUint32 i = 0; i < numOldProfiles; i++) { - rv = MigrateProfile(nameArray[i], PR_FALSE /* don't show progress as modal window */); + rv = MigrateProfile(nameArray[i]); if (NS_FAILED(rv)) break; } NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(numOldProfiles, nameArray); diff --git a/mozilla/profile/src/nsProfile.h b/mozilla/profile/src/nsProfile.h index 51dd87df94d..4caa2b49146 100644 --- a/mozilla/profile/src/nsProfile.h +++ b/mozilla/profile/src/nsProfile.h @@ -87,6 +87,9 @@ private: nsresult UndefineFileLocations(); nsresult Update4xProfileInfo(); char * GetOldRegLocation(); + nsresult UpdateCurrentProfileModTime(PRBool updateRegistry); + nsresult MigrateProfileInternal(const PRUnichar *profileName, + nsIFile *oldProfDir, nsIFile *newProfDir); PRBool mStartingUp; PRBool mAutomigrate; @@ -94,13 +97,14 @@ private: PRBool mDiskSpaceErrorQuitCalled; PRBool mProfileChangeVetoed; + nsString mCurrentProfileName; PRBool mCurrentProfileAvailable; PRBool mIsUILocaleSpecified; - nsAutoString mUILocaleName; + nsString mUILocaleName; PRBool mIsContentLocaleSpecified; - nsAutoString mContentLocaleName; + nsString mContentLocaleName; public: nsProfile(); diff --git a/mozilla/profile/src/nsProfileAccess.cpp b/mozilla/profile/src/nsProfileAccess.cpp index 7dcf69acf4f..8e2a56fee73 100644 --- a/mozilla/profile/src/nsProfileAccess.cpp +++ b/mozilla/profile/src/nsProfileAccess.cpp @@ -42,9 +42,6 @@ #include "nsICharsetConverterManager.h" #include "nsIPlatformCharset.h" -#define MAX_PERSISTENT_DATA_SIZE 1000 -#define NUM_HEX_BYTES 8 -#define ISHEX(c) ( ((c) >= '0' && (c) <= '9') || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F') ) #if defined (XP_UNIX) #define USER_ENVIRONMENT_VARIABLE "USER" @@ -76,7 +73,9 @@ static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CI #define kRegistryDirectoryString (NS_LITERAL_STRING("directory")) #define kRegistryNeedMigrationString (NS_LITERAL_STRING("NeedMigration")) #define kRegistryMozRegDataMovedString (NS_LITERAL_STRING("OldRegDataMoved")) - +#define kRegistryCreationTimeString (NS_LITERAL_CSTRING("CreationTime")) +#define kRegistryLastModTimeString (NS_LITERAL_CSTRING("LastModTime")) +#define kRegistryMigratedFromString (NS_LITERAL_CSTRING("MigFromDir")) #define kRegistryVersionString (NS_LITERAL_STRING("Version")) #define kRegistryVersion_1_0 (NS_LITERAL_STRING("1.0")) #define kRegistryCurrentVersion (NS_LITERAL_STRING("1.0")) @@ -223,10 +222,9 @@ nsProfileAccess::GetValue(const PRUnichar* profileName, ProfileStruct** aProfile nsresult nsProfileAccess::SetValue(ProfileStruct* aProfile) { - NS_ASSERTION(aProfile, "Invalid profile"); + NS_ENSURE_ARG(aProfile); PRInt32 index = 0; - PRBool isNewProfile = PR_FALSE; ProfileStruct* profileItem; index = FindProfileIndex(aProfile->profileName.get(), aProfile->isImportType); @@ -234,43 +232,21 @@ nsProfileAccess::SetValue(ProfileStruct* aProfile) if (index >= 0) { profileItem = (ProfileStruct *) (mProfiles->ElementAt(index)); + *profileItem = *aProfile; + profileItem->updateProfileEntry = PR_TRUE; } else { - isNewProfile = PR_TRUE; - - profileItem = new ProfileStruct(); + profileItem = new ProfileStruct(*aProfile); if (!profileItem) return NS_ERROR_OUT_OF_MEMORY; - - profileItem->profileName = aProfile->profileName; - } - - aProfile->CopyProfileLocation(profileItem); - - profileItem->isMigrated = aProfile->isMigrated; - - profileItem->isImportType = aProfile->isImportType; - profileItem->updateProfileEntry = PR_TRUE; - if (!aProfile->NCProfileName.IsEmpty()) - profileItem->NCProfileName = aProfile->NCProfileName; - - if (!aProfile->NCDeniedService.IsEmpty()) - profileItem->NCDeniedService = aProfile->NCDeniedService; - - if (!aProfile->NCEmailAddress.IsEmpty()) - profileItem->NCEmailAddress = aProfile->NCEmailAddress; - - if (!aProfile->NCHavePregInfo.IsEmpty()) - profileItem->NCHavePregInfo = aProfile->NCHavePregInfo; - - - if (isNewProfile) { + if (!mProfiles) { + mProfiles = new nsVoidArray; if (!mProfiles) - mProfiles = new nsVoidArray(); - + return NS_ERROR_OUT_OF_MEMORY; + } mProfiles->AppendElement((void*)profileItem); } @@ -350,12 +326,6 @@ nsProfileAccess::FillProfileInfo(nsIFile* regName) kRegistryVersionString.get(), getter_Copies(tmpVersion)); - if (tmpVersion == nsnull) - { - fixRegEntries = PR_TRUE; - mProfileDataChanged = PR_TRUE; - } - // Get the preg info rv = registry->GetString(profilesTreeKey, kRegistryHavePREGInfoString.get(), @@ -440,8 +410,23 @@ nsProfileAccess::FillProfileInfo(nsIFile* regName) profileItem->updateProfileEntry = PR_TRUE; profileItem->profileName = NS_STATIC_CAST(const PRUnichar*, profile); - rv = profileItem->InternalizeLocation(registry, profKey, PR_FALSE, fixRegEntries); + PRInt64 tmpLongLong; + rv = registry->GetLongLong(profKey, + kRegistryCreationTimeString.get(), + &tmpLongLong); + if (NS_SUCCEEDED(rv)) + profileItem->creationTime = tmpLongLong; + + rv = registry->GetLongLong(profKey, + kRegistryLastModTimeString.get(), + &tmpLongLong); + if (NS_SUCCEEDED(rv)) + profileItem->lastModTime = tmpLongLong; + + rv = profileItem->InternalizeLocation(registry, profKey, PR_FALSE); NS_ASSERTION(NS_SUCCEEDED(rv), "Internalizing profile location failed"); + // Not checking the error since most won't have this info + profileItem->InternalizeMigratedFromLocation(registry, profKey); profileItem->isMigrated = isMigratedString.Equals(kRegistryYesString); @@ -768,6 +753,14 @@ nsProfileAccess::UpdateRegistry(nsIFile* regName) kRegistryNCHavePREGInfoString.get(), profileItem->NCHavePregInfo.get()); + registry->SetLongLong(profKey, + kRegistryCreationTimeString.get(), + &profileItem->creationTime); + + registry->SetLongLong(profKey, + kRegistryLastModTimeString.get(), + &profileItem->lastModTime); + rv = profileItem->ExternalizeLocation(registry, profKey); if (NS_FAILED(rv)) { NS_ASSERTION(PR_FALSE, "Could not update profile location"); @@ -775,6 +768,7 @@ nsProfileAccess::UpdateRegistry(nsIFile* regName) if (NS_FAILED(rv)) return rv; continue; } + profileItem->ExternalizeMigratedFromLocation(registry, profKey); profileItem->updateProfileEntry = PR_FALSE; } @@ -818,11 +812,20 @@ nsProfileAccess::UpdateRegistry(nsIFile* regName) kRegistryNCHavePREGInfoString.get(), profileItem->NCHavePregInfo.get()); + registry->SetLongLong(profKey, + kRegistryCreationTimeString.get(), + &profileItem->creationTime); + + registry->SetLongLong(profKey, + kRegistryLastModTimeString.get(), + &profileItem->lastModTime); + rv = profileItem->ExternalizeLocation(registry, profKey); if (NS_FAILED(rv)) { NS_ASSERTION(PR_FALSE, "Could not update profile location"); continue; } + profileItem->ExternalizeMigratedFromLocation(registry, profKey); profileItem->updateProfileEntry = PR_FALSE; } @@ -873,6 +876,40 @@ nsProfileAccess::GetOriginalProfileDir(const PRUnichar *profileName, nsILocalFil } return NS_ERROR_FAILURE; } + +nsresult +nsProfileAccess::SetMigratedFromDir(const PRUnichar *profileName, nsILocalFile *originalDir) +{ + NS_ENSURE_ARG(profileName); + NS_ENSURE_ARG(originalDir); + + PRInt32 index = FindProfileIndex(profileName, PR_FALSE); + if (index >= 0) + { + ProfileStruct* profileItem = (ProfileStruct *) (mProfiles->ElementAt(index)); + profileItem->migratedFrom = originalDir; + profileItem->updateProfileEntry = PR_TRUE; + return NS_OK; + } + return NS_ERROR_FAILURE; +} + +nsresult +nsProfileAccess::SetProfileLastModTime(const PRUnichar *profileName, PRInt64 lastModTime) +{ + NS_ENSURE_ARG(profileName); + + PRInt32 index = FindProfileIndex(profileName, PR_FALSE); + if (index >= 0) + { + ProfileStruct* profileItem = (ProfileStruct *) (mProfiles->ElementAt(index)); + profileItem->lastModTime = lastModTime; + profileItem->updateProfileEntry = PR_TRUE; + return NS_OK; + } + return NS_ERROR_FAILURE; +} + // Return the list of profiles, 4x, 5x, or both. nsresult nsProfileAccess::GetProfileList(PRInt32 whichKind, PRUint32 *length, PRUnichar ***result) @@ -1063,7 +1100,7 @@ nsProfileAccess::Get4xProfileInfo(const char *registryName, PRBool fromImport) profileItem->updateProfileEntry = PR_TRUE; profileItem->profileName = convertedProfName; - rv = profileItem->InternalizeLocation(oldReg, key, PR_TRUE, PR_FALSE); + rv = profileItem->InternalizeLocation(oldReg, key, PR_TRUE); NS_ASSERTION(NS_SUCCEEDED(rv), "Could not get 4x profile location"); profileItem->isMigrated = PR_FALSE; profileItem->isImportType = fromImport; @@ -1218,20 +1255,53 @@ nsProfileAccess::DetermineForceMigration(PRBool *forceMigration) // class ProfileStruct // ********************************************************************** -ProfileStruct::ProfileStruct(const ProfileStruct& src) : - profileName(src.profileName), isMigrated(src.isMigrated), - NCProfileName(src.NCProfileName), NCDeniedService(src.NCDeniedService), - NCEmailAddress(src.NCEmailAddress), NCHavePregInfo(src.NCHavePregInfo), - updateProfileEntry(src.updateProfileEntry), - isImportType(src.isImportType), - regLocationData(src.regLocationData) +ProfileStruct::ProfileStruct() : + isMigrated(PR_FALSE), updateProfileEntry(PR_FALSE), + isImportType(PR_FALSE), + creationTime(LL_ZERO), lastModTime(LL_ZERO) { - if (src.resolvedLocation) { +} + +ProfileStruct::ProfileStruct(const ProfileStruct& src) +{ + *this = src; +} + +ProfileStruct& ProfileStruct::operator=(const ProfileStruct& rhs) +{ + profileName = rhs.profileName; + isMigrated = rhs.isMigrated; + NCProfileName = rhs.NCProfileName; + NCDeniedService = rhs.NCDeniedService; + NCEmailAddress = rhs.NCEmailAddress; + NCHavePregInfo = rhs.NCHavePregInfo; + updateProfileEntry = rhs.updateProfileEntry; + isImportType = rhs.isImportType; + creationTime = rhs.creationTime; + lastModTime = rhs.lastModTime; + + nsresult rv; nsCOMPtr file; - nsresult rv = src.resolvedLocation->Clone(getter_AddRefs(file)); + + resolvedLocation = nsnull; + if (rhs.resolvedLocation) { + regLocationData.Truncate(0); + rv = rhs.resolvedLocation->Clone(getter_AddRefs(file)); if (NS_SUCCEEDED(rv)) resolvedLocation = do_QueryInterface(file); + file = nsnull; } + else + regLocationData = rhs.regLocationData; + + migratedFrom = nsnull; + if (rhs.migratedFrom) { + rv = rhs.migratedFrom->Clone(getter_AddRefs(file)); + if (NS_SUCCEEDED(rv)) + migratedFrom = do_QueryInterface(file); + } + + return *this; } nsresult ProfileStruct::GetResolvedProfileDir(nsILocalFile **aDirectory) @@ -1270,7 +1340,7 @@ nsresult ProfileStruct::CopyProfileLocation(ProfileStruct *destStruct) return NS_OK; } -nsresult ProfileStruct::InternalizeLocation(nsIRegistry *aRegistry, nsRegistryKey profKey, PRBool is4x, PRBool isOld50) +nsresult ProfileStruct::InternalizeLocation(nsIRegistry *aRegistry, nsRegistryKey profKey, PRBool is4x) { nsresult rv; nsCOMPtr tempLocal; @@ -1315,90 +1385,24 @@ nsresult ProfileStruct::InternalizeLocation(nsIRegistry *aRegistry, nsRegistryKe { nsXPIDLString regData; - if (isOld50) // Some format which was used around M10-M11. Can we forget about it? - { - - rv = aRegistry->GetString(profKey, - kRegistryDirectoryString.get(), - getter_Copies(regData)); - if (NS_FAILED(rv)) return rv; - - PRBool haveHexBytes = PR_TRUE; - - // Decode the directory name to return the ordinary string - nsCAutoString regDataCString; regDataCString.AssignWithConversion(regData); - nsInputStringStream stream(regDataCString.get()); - - char bigBuffer[MAX_PERSISTENT_DATA_SIZE + 1]; - // The first 8 bytes of the data should be a hex version of the data size to follow. - PRInt32 bytesRead = NUM_HEX_BYTES; - bytesRead = stream.read(bigBuffer, bytesRead); - - if (bytesRead != NUM_HEX_BYTES) - haveHexBytes = PR_FALSE; - - if (haveHexBytes) - { - bigBuffer[NUM_HEX_BYTES] = '\0'; - - for (int i = 0; i < NUM_HEX_BYTES; i++) - { - if (!(ISHEX(bigBuffer[i]))) - { - haveHexBytes = PR_FALSE; - break; - } - } - } - - nsAutoString dirNameString; - if (haveHexBytes) - { - PR_sscanf(bigBuffer, "%x", (PRUint32*)&bytesRead); - if (bytesRead > MAX_PERSISTENT_DATA_SIZE) - { - // Try to tolerate encoded values with no length header - bytesRead = NUM_HEX_BYTES + - stream.read(bigBuffer + NUM_HEX_BYTES, - MAX_PERSISTENT_DATA_SIZE - NUM_HEX_BYTES); - } - else - { - // Now we know how many bytes to read, do it. - bytesRead = stream.read(bigBuffer, bytesRead); - } - - // Make sure we are null terminated - bigBuffer[bytesRead]='\0'; - - dirNameString.AssignWithConversion(nsDependentCString(bigBuffer, bytesRead).get()); - } - else - dirNameString = regData; - - rv = NS_NewUnicodeLocalFile(dirNameString.get(), PR_TRUE, getter_AddRefs(tempLocal)); - } - else - { - rv = aRegistry->GetString(profKey, - kRegistryDirectoryString.get(), - getter_Copies(regData)); - if (NS_FAILED(rv)) return rv; - regLocationData = regData; + rv = aRegistry->GetString(profKey, + kRegistryDirectoryString.get(), + getter_Copies(regData)); + if (NS_FAILED(rv)) return rv; + regLocationData = regData; #ifdef XP_MAC - // For a brief time, this was a unicode path - PRInt32 firstColon = regLocationData.FindChar(PRUnichar(':')); - if (firstColon == -1) - { - rv = NS_NewLocalFile(nsnull, PR_TRUE, getter_AddRefs(tempLocal)); - if (NS_SUCCEEDED(rv)) - rv = tempLocal->SetPersistentDescriptor(NS_ConvertUCS2toUTF8(regLocationData).get()); - } - else -#endif - rv = NS_NewUnicodeLocalFile(regLocationData.get(), PR_TRUE, getter_AddRefs(tempLocal)); + // For a brief time, this was a unicode path + PRInt32 firstColon = regLocationData.FindChar(PRUnichar(':')); + if (firstColon == -1) + { + rv = NS_NewLocalFile(nsnull, PR_TRUE, getter_AddRefs(tempLocal)); + if (NS_SUCCEEDED(rv)) + rv = tempLocal->SetPersistentDescriptor(NS_ConvertUCS2toUTF8(regLocationData).get()); } + else +#endif + rv = NS_NewUnicodeLocalFile(regLocationData.get(), PR_TRUE, getter_AddRefs(tempLocal)); } if (NS_SUCCEEDED(rv) && tempLocal) @@ -1459,6 +1463,59 @@ nsresult ProfileStruct::ExternalizeLocation(nsIRegistry *aRegistry, nsRegistryKe return rv; } +nsresult ProfileStruct::InternalizeMigratedFromLocation(nsIRegistry *aRegistry, nsRegistryKey profKey) +{ + nsresult rv; + nsXPIDLCString regData; + nsCOMPtr tempLocal; + + rv = aRegistry->GetStringUTF8(profKey, + kRegistryMigratedFromString.get(), + getter_Copies(regData)); + if (NS_SUCCEEDED(rv)) + { +#ifdef XP_MAC + rv = NS_NewLocalFile(nsnull, PR_TRUE, getter_AddRefs(tempLocal)); + if (NS_SUCCEEDED(rv)) + { + // The persistent desc on Mac is base64 encoded so plain ASCII + rv = tempLocal->SetPersistentDescriptor(regData.get()); + if (NS_SUCCEEDED(rv)) + migratedFrom = tempLocal; + } +#else + rv = NS_NewUnicodeLocalFile(NS_ConvertUTF8toUCS2(regData).get(), PR_TRUE, getter_AddRefs(tempLocal)); + if (NS_SUCCEEDED(rv)) + migratedFrom = tempLocal; +#endif + } + return rv; +} + +nsresult ProfileStruct::ExternalizeMigratedFromLocation(nsIRegistry *aRegistry, nsRegistryKey profKey) +{ + nsresult rv = NS_OK; + nsXPIDLCString regData; + + if (migratedFrom) + { +#if XP_MAC + rv = migratedFrom->GetPersistentDescriptor(getter_Copies(regData)); +#else + nsXPIDLString ucPath; + rv = resolvedLocation->GetUnicodePath(getter_Copies(ucPath)); + if (NS_SUCCEEDED(rv)) + regData = NS_ConvertUCS2toUTF8(ucPath); +#endif + + if (NS_SUCCEEDED(rv)) + rv = aRegistry->SetStringUTF8(profKey, + kRegistryMigratedFromString.get(), + regData.get()); + } + return rv; +} + nsresult ProfileStruct::EnsureDirPathExists(nsILocalFile *aDir, PRBool *wasCreated) { NS_ENSURE_ARG(aDir); diff --git a/mozilla/profile/src/nsProfileAccess.h b/mozilla/profile/src/nsProfileAccess.h index 7cac649e96b..e7954345807 100644 --- a/mozilla/profile/src/nsProfileAccess.h +++ b/mozilla/profile/src/nsProfileAccess.h @@ -33,11 +33,13 @@ class ProfileStruct { public: - explicit ProfileStruct() { } + ProfileStruct(); ProfileStruct(const ProfileStruct& src); ~ProfileStruct() { } + ProfileStruct& operator=(const ProfileStruct& rhs); + /* * GetResolvedProfileDir returns the directory specified in the * registry. It will return NULL if the spec in the registry @@ -63,12 +65,19 @@ public: * Methods used by routines which internalize * and externalize profile info. */ - nsresult InternalizeLocation(nsIRegistry *aRegistry, nsRegistryKey profKey, PRBool is4x, PRBool isOld50); + nsresult InternalizeLocation(nsIRegistry *aRegistry, nsRegistryKey profKey, PRBool is4x); nsresult ExternalizeLocation(nsIRegistry *aRegistry, nsRegistryKey profKey); + nsresult InternalizeMigratedFromLocation(nsIRegistry *aRegistry, nsRegistryKey profKey); + nsresult ExternalizeMigratedFromLocation(nsIRegistry *aRegistry, nsRegistryKey profKey); public: nsString profileName; PRBool isMigrated; + + // The directory from which this profile was migrated from (if any) + // Added in mozilla1.0.1 and maintained in the registry + nsCOMPtr migratedFrom; + nsString NCProfileName; nsString NCDeniedService; nsString NCEmailAddress; @@ -76,6 +85,11 @@ public: PRBool updateProfileEntry; // this flag detemines if we added this profile to the list for the import module. PRBool isImportType; + // These fields were added in mozilla1.0.1 and maintained in the registry. + // Values are in milliseconds since midnight Jan 1, 1970 GMT (same as nsIFile) + // Their values will be LL_ZERO if undefined. + PRInt64 creationTime; + PRInt64 lastModTime; private: nsresult EnsureDirPathExists(nsILocalFile *aFile, PRBool *wasCreated); @@ -115,6 +129,8 @@ public: void GetFirstProfile(PRUnichar **firstProfile); nsresult GetProfileList(PRInt32 whichKind, PRUint32 *length, PRUnichar ***result); nsresult GetOriginalProfileDir(const PRUnichar *profileName, nsILocalFile **orginalDir); + nsresult SetMigratedFromDir(const PRUnichar *profileName, nsILocalFile *orginalDir); + nsresult SetProfileLastModTime(const PRUnichar *profileName, PRInt64 lastModTime); // if fromImport is true all the 4.x profiles will be added to mProfiles with the isImportType flag set. // pass fromImport as True only if you are calling from the Import Module. diff --git a/mozilla/xpfe/appshell/src/nsAppShellService.cpp b/mozilla/xpfe/appshell/src/nsAppShellService.cpp index 276f13543c9..87491cd264a 100644 --- a/mozilla/xpfe/appshell/src/nsAppShellService.cpp +++ b/mozilla/xpfe/appshell/src/nsAppShellService.cpp @@ -80,6 +80,12 @@ PRBool OnMacOSX(); #include "nsIScriptContext.h" #include "jsapi.h" +/* for the "remigration" stuff */ +#include "nsIPrefService.h" +#include "nsIPrefBranch.h" +#include "nsIPromptService.h" +#include "nsIStringBundle.h" + #include "nsAppShellService.h" #include "nsIProfileInternal.h" @@ -234,8 +240,7 @@ nsAppShellService::DoProfileStartup(nsICmdLineService *aCmdLineService, PRBool c nsresult rv; nsCOMPtr profileMgr(do_GetService(NS_PROFILE_CONTRACTID, &rv)); - NS_ASSERTION(NS_SUCCEEDED(rv), "failed to get profile manager"); - if (NS_FAILED(rv)) return rv; + NS_ENSURE_SUCCESS(rv,rv); PRBool saveQuitOnLastWindowClosing = mQuitOnLastWindowClosing; mQuitOnLastWindowClosing = PR_FALSE; @@ -247,11 +252,148 @@ nsAppShellService::DoProfileStartup(nsICmdLineService *aCmdLineService, PRBool c rv = NS_OK; } - mQuitOnLastWindowClosing = saveQuitOnLastWindowClosing; + if (NS_SUCCEEDED(rv)) { + rv = CheckAndRemigrateDefunctProfile(); + NS_ASSERTION(NS_SUCCEEDED(rv), "failed to check and remigrate profile"); + rv = NS_OK; + } + mQuitOnLastWindowClosing = saveQuitOnLastWindowClosing; return rv; } +nsresult +nsAppShellService::CheckAndRemigrateDefunctProfile() +{ + nsresult rv; + + nsCOMPtr prefBranch; + nsCOMPtr prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv,rv); + rv = prefs->GetBranch(nsnull, getter_AddRefs(prefBranch)); + NS_ENSURE_SUCCESS(rv,rv); + + PRInt32 secondsBeforeDefunct; + rv = prefBranch->GetIntPref("profile.seconds_until_defunct", &secondsBeforeDefunct); + NS_ENSURE_SUCCESS(rv,rv); + + // -1 is the value for "never go defunct" + // if the pref is set to -1, we'll never prompt the user to remigrate + // see all.js (and all-ns.js) + if (secondsBeforeDefunct == -1) + return NS_OK; + + // used for converting + // seconds -> millisecs + // and microsecs -> millisecs + PRInt64 oneThousand = LL_INIT(0, 1000); + + PRInt64 defunctInterval; + // Init as seconds + LL_I2L(defunctInterval, secondsBeforeDefunct); + // Convert secs to millisecs + LL_MUL(defunctInterval, defunctInterval, oneThousand); + + nsCOMPtr profileMgr(do_GetService(NS_PROFILE_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS(rv,rv); + + nsXPIDLString profileName; + PRInt64 lastModTime; + profileMgr->GetCurrentProfile(getter_Copies(profileName)); + rv = profileMgr->GetProfileLastModTime(profileName.get(), &lastModTime); + NS_ENSURE_SUCCESS(rv,rv); + + // convert "now" from microsecs to millisecs + PRInt64 nowInMilliSecs = PR_Now(); + LL_DIV(nowInMilliSecs, nowInMilliSecs, oneThousand); + + // determine (using the pref value) when the profile would be considered defunct + PRInt64 defunctIntervalAgo; + LL_SUB(defunctIntervalAgo, nowInMilliSecs, defunctInterval); + + // if we've used our current 6.x / mozilla profile more recently than + // when we'd consider it defunct, don't remigrate + if (LL_CMP(lastModTime, >, defunctIntervalAgo)) + return NS_OK; + + nsCOMPtr origProfileDir; + rv = profileMgr->GetOriginalProfileDir(profileName, getter_AddRefs(origProfileDir)); + // if this fails + // then the current profile is a new one (not from 4.x) + // so we are done. + if (NS_FAILED(rv)) + return NS_OK; + + // Now, we know that a matching 4.x profile exists + // See if it has any newer files in it than our defunct profile. + nsCOMPtr dirEnum; + rv = origProfileDir->GetDirectoryEntries(getter_AddRefs(dirEnum)); + NS_ENSURE_SUCCESS(rv,rv); + + PRBool promptForRemigration = PR_FALSE; + PRBool hasMore; + while (NS_SUCCEEDED(dirEnum->HasMoreElements(&hasMore)) && hasMore) { + nsCOMPtr currElem; + rv = dirEnum->GetNext(getter_AddRefs(currElem)); + NS_ENSURE_SUCCESS(rv,rv); + + PRInt64 currElemModTime; + rv = currElem->GetLastModifiedTime(&currElemModTime); + NS_ENSURE_SUCCESS(rv,rv); + // if this file in our 4.x profile is more recent than when we last used our mozilla / 6.x profile + // we should prompt for re-migration + if (LL_CMP(currElemModTime, >, lastModTime)) { + promptForRemigration = PR_TRUE; + break; + } + } + + // If nothing in the 4.x dir is newer than our defunct profile, return. + if (!promptForRemigration) + return NS_OK; + + nsCOMPtr stringBundleService(do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS(rv,rv); + + nsCOMPtr migrationBundle, brandBundle; + rv = stringBundleService->CreateBundle("chrome://communicator/locale/profile/migration.properties", getter_AddRefs(migrationBundle)); + NS_ENSURE_SUCCESS(rv,rv); + + rv = stringBundleService->CreateBundle("chrome://global/locale/brand.properties", getter_AddRefs(brandBundle)); + NS_ENSURE_SUCCESS(rv,rv); + + nsXPIDLString brandName; + rv = brandBundle->GetStringFromName(NS_LITERAL_STRING("brandShortName").get(), getter_Copies(brandName)); + NS_ENSURE_SUCCESS(rv,rv); + + nsXPIDLString dialogText; + rv = migrationBundle->GetStringFromName(NS_LITERAL_STRING("confirmRemigration").get(), getter_Copies(dialogText)); + NS_ENSURE_SUCCESS(rv,rv); + + nsCOMPtr promptService(do_GetService("@mozilla.org/embedcomp/prompt-service;1", &rv)); + NS_ENSURE_SUCCESS(rv,rv); + PRInt32 buttonPressed; + rv = promptService->ConfirmEx(nsnull, brandName.get(), + dialogText.get(), + (nsIPromptService::BUTTON_POS_0 * + nsIPromptService::BUTTON_TITLE_YES) + + (nsIPromptService::BUTTON_POS_1 * + nsIPromptService::BUTTON_TITLE_NO), + nsnull, nsnull, nsnull, nsnull, nsnull, &buttonPressed); + NS_ENSURE_SUCCESS(rv,rv); + + if (buttonPressed == 0) { + // Need to shut down the current profile before remigrating it + profileMgr->ShutDownCurrentProfile(nsIProfile::SHUTDOWN_PERSIST); + // If this fails, it will restore what was there. + rv = profileMgr->RemigrateProfile(profileName.get()); + NS_ASSERTION(NS_SUCCEEDED(rv), "Remigration of profile failed."); + // Whether or not we succeeded or failed, need to reset this. + profileMgr->SetCurrentProfile(profileName.get()); + } + return NS_OK; +} + NS_IMETHODIMP nsAppShellService::CreateHiddenWindow() { diff --git a/mozilla/xpfe/appshell/src/nsAppShellService.h b/mozilla/xpfe/appshell/src/nsAppShellService.h index 0dc696ab037..28d83fb3896 100644 --- a/mozilla/xpfe/appshell/src/nsAppShellService.h +++ b/mozilla/xpfe/appshell/src/nsAppShellService.h @@ -96,6 +96,9 @@ protected: static void* PR_CALLBACK HandleExitEvent(PLEvent* aEvent); static void PR_CALLBACK DestroyExitEvent(PLEvent* aEvent); + +private: + nsresult CheckAndRemigrateDefunctProfile(); }; #endif diff --git a/mozilla/xpfe/bootstrap/Makefile.in b/mozilla/xpfe/bootstrap/Makefile.in index dcb7d5e506f..b3389858664 100644 --- a/mozilla/xpfe/bootstrap/Makefile.in +++ b/mozilla/xpfe/bootstrap/Makefile.in @@ -52,6 +52,7 @@ REQUIRES = xpcom \ uconv \ locale \ xremoteservice \ + profile \ $(NULL) # for jprof REQUIRES += jprof diff --git a/mozilla/xpfe/bootstrap/nsAppRunner.cpp b/mozilla/xpfe/bootstrap/nsAppRunner.cpp index 51a1be0b0c2..ebbf2a6e646 100644 --- a/mozilla/xpfe/bootstrap/nsAppRunner.cpp +++ b/mozilla/xpfe/bootstrap/nsAppRunner.cpp @@ -96,6 +96,9 @@ #include "nsIXRemoteService.h" #endif +// see DoOnShutdown() +#include "nsIProfile.h" + #ifdef NS_TRACE_MALLOC #include "nsTraceMalloc.h" #endif @@ -106,7 +109,7 @@ #include "nsITimelineService.h" -#if defined(DEBUG_sspitzer) || defined(DEBUG_seth) || defined(DEBUG_pra) +#if defined(DEBUG_pra) #define DEBUG_CMD_LINE #endif @@ -773,6 +776,17 @@ static nsresult DoOnShutdown() { nsresult rv; + // call ShutDownCurrentProfile() so we update the last modified time of the profile + { + // scoping this in a block to force release + nsCOMPtr profileMgr(do_GetService(NS_PROFILE_CONTRACTID, &rv)); + NS_ASSERTION(NS_SUCCEEDED(rv), "failed to get profile manager, so unable to update last modified time"); + if (NS_SUCCEEDED(rv)) { + // 0 is undefined, we use this secret value so that we don't notify + profileMgr->ShutDownCurrentProfile(0); + } + } + // save the prefs, in case they weren't saved { // scoping this in a block to force release