commit bebc69b8e8e919abec272f9cff6ab13fb9ea51f7 Author: Kathy Brade brade@pearlcrescent.com Date: Tue Feb 24 13:50:23 2015 -0500
Bug 14631: Improve profile access error messages.
Instead of always reporting that the profile is locked, display specific messages for "access denied" and "read-only file system". --- .../mozapps/profile/profileSelection.properties | 5 + toolkit/xre/nsAppRunner.cpp | 112 +++++++++++++++++--- 2 files changed, 103 insertions(+), 14 deletions(-)
diff --git a/toolkit/locales/en-US/chrome/mozapps/profile/profileSelection.properties b/toolkit/locales/en-US/chrome/mozapps/profile/profileSelection.properties index adac95a..3cf48ff 100644 --- a/toolkit/locales/en-US/chrome/mozapps/profile/profileSelection.properties +++ b/toolkit/locales/en-US/chrome/mozapps/profile/profileSelection.properties @@ -12,6 +12,11 @@ restartMessageUnlocker=%S is already running, but is not responding. The old %S restartMessageNoUnlockerMac=A copy of %S is already open. Only one copy of %S can be open at a time. restartMessageUnlockerMac=A copy of %S is already open. The running copy of %S will quit in order to open this one.
+# LOCALIZATION NOTE (profileProblemTitle, profileReadOnly, profileReadOnlyMac, profileAccessDenied): Messages displayed when the browser profile cannot be accessed or written to. %S is the application name. +profileProblemTitle=%S Profile Problem +profileReadOnly=You cannot run %S from a read-only file system. Please copy %S to another location before trying to use it. +profileReadOnlyMac=You cannot run %S from a read-only file system. Please copy %S to your Desktop or Applications folder before trying to use it. +profileAccessDenied=%S does not have permission to access the profile. Please adjust your file system permissions and try again. # Profile manager # LOCALIZATION NOTE (profileTooltip): First %S is the profile name, second %S is the path to the profile folder. profileTooltip=Profile: '%S' - Path: '%S' diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index cadc46d..dbd1376 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -1682,12 +1682,20 @@ static nsresult LaunchChild(nsINativeAppSupport* aNative, return NS_ERROR_LAUNCHED_CHILD_PROCESS; }
+enum ProfileStatus { + PROFILE_STATUS_OK, + PROFILE_STATUS_ACCESS_DENIED, + PROFILE_STATUS_READ_ONLY, + PROFILE_STATUS_IS_LOCKED, + PROFILE_STATUS_OTHER_ERROR +}; + static const char kProfileProperties[] = "chrome://mozapps/locale/profile/profileSelection.properties";
static nsresult -ProfileLockedDialog(nsIFile* aProfileDir, nsIFile* aProfileLocalDir, - nsIProfileUnlocker* aUnlocker, +ProfileErrorDialog(nsIFile* aProfileDir, nsIFile* aProfileLocalDir, + ProfileStatus aStatus, nsIProfileUnlocker* aUnlocker, nsINativeAppSupport* aNative, nsIProfileLock* *aResult) { nsresult rv; @@ -1717,17 +1725,29 @@ ProfileLockedDialog(nsIFile* aProfileDir, nsIFile* aProfileLocalDir, #ifndef XP_MACOSX static const char16_t kRestartNoUnlocker[] = {'r','e','s','t','a','r','t','M','e','s','s','a','g','e','N','o','U','n','l','o','c','k','e','r','\0'}; // "restartMessageNoUnlocker" static const char16_t kRestartUnlocker[] = {'r','e','s','t','a','r','t','M','e','s','s','a','g','e','U','n','l','o','c','k','e','r','\0'}; // "restartMessageUnlocker" + static const char16_t kReadOnly[] = {'p','r','o','f','i','l','e','R','e','a','d','O','n','l','y','\0'}; // "profileReadOnly" #else static const char16_t kRestartNoUnlocker[] = {'r','e','s','t','a','r','t','M','e','s','s','a','g','e','N','o','U','n','l','o','c','k','e','r','M','a','c','\0'}; // "restartMessageNoUnlockerMac" static const char16_t kRestartUnlocker[] = {'r','e','s','t','a','r','t','M','e','s','s','a','g','e','U','n','l','o','c','k','e','r','M','a','c','\0'}; // "restartMessageUnlockerMac" + static const char16_t kReadOnly[] = {'p','r','o','f','i','l','e','R','e','a','d','O','n','l','y','M','a','c','\0'}; // "profileReadOnlyMac" #endif
- sb->FormatStringFromName(aUnlocker ? kRestartUnlocker : kRestartNoUnlocker, - params, 2, getter_Copies(killMessage)); + static const char16_t kAccessDenied[] = {'p','r','o','f','i','l','e','A','c','c','e','s','s','D','e','n','i','e','d','\0'}; // "profileAccessDenied"
+ const char16_t *errorKey = aUnlocker ? kRestartUnlocker + : kRestartNoUnlocker; + if (PROFILE_STATUS_READ_ONLY == aStatus) + errorKey = kReadOnly; + else if (PROFILE_STATUS_ACCESS_DENIED == aStatus) + errorKey = kAccessDenied; + sb->FormatStringFromName(errorKey, params, 2, getter_Copies(killMessage)); + + const char16_t *titleKey = ((PROFILE_STATUS_READ_ONLY == aStatus) || + (PROFILE_STATUS_ACCESS_DENIED == aStatus)) + ? MOZ_UTF16("profileProblemTitle") + : MOZ_UTF16("restartTitle"); nsXPIDLString killTitle; - sb->FormatStringFromName(MOZ_UTF16("restartTitle"), - params, 1, getter_Copies(killTitle)); + sb->FormatStringFromName(titleKey, params, 1, getter_Copies(killTitle));
if (!killMessage || !killTitle) return NS_ERROR_FAILURE; @@ -1828,8 +1848,9 @@ ProfileMissingDialog(nsINativeAppSupport* aNative) }
static nsresult -ProfileLockedDialog(nsIToolkitProfile* aProfile, nsIProfileUnlocker* aUnlocker, - nsINativeAppSupport* aNative, nsIProfileLock* *aResult) +ProfileErrorDialog(nsIToolkitProfile* aProfile, ProfileStatus aStatus, + nsIProfileUnlocker* aUnlocker, nsINativeAppSupport* aNative, + nsIProfileLock* *aResult) { nsCOMPtr<nsIFile> profileDir; nsresult rv = aProfile->GetRootDir(getter_AddRefs(profileDir)); @@ -1845,8 +1866,8 @@ ProfileLockedDialog(nsIToolkitProfile* aProfile, nsIProfileUnlocker* aUnlocker, rv = aProfile->GetLocalDir(getter_AddRefs(profileLocalDir)); if (NS_FAILED(rv)) return rv;
- return ProfileLockedDialog(profileDir, profileLocalDir, aUnlocker, aNative, - aResult); + return ProfileErrorDialog(profileDir, profileLocalDir, aStatus, aUnlocker, + aNative, aResult); }
static const char kProfileManagerURL[] = @@ -2007,6 +2028,53 @@ SetCurrentProfileAsDefault(nsIToolkitProfileService* aProfileSvc, return rv; }
+// Check for write permission to the profile directory by trying to create a +// new file (after ensuring that no file with the same name exists). +static ProfileStatus CheckProfileWriteAccess(nsIFile* aProfileDir) +{ +#if defined(XP_UNIX) + NS_NAMED_LITERAL_STRING(writeTestFileName, ".parentwritetest"); +#else + NS_NAMED_LITERAL_STRING(writeTestFileName, "parent.writetest"); +#endif + + nsCOMPtr<nsIFile> writeTestFile; + nsresult rv = aProfileDir->Clone(getter_AddRefs(writeTestFile)); + if (NS_SUCCEEDED(rv)) + rv = writeTestFile->Append(writeTestFileName); + + if (NS_SUCCEEDED(rv)) { + bool doesExist = false; + rv = writeTestFile->Exists(&doesExist); + if (NS_SUCCEEDED(rv) && doesExist) + rv = writeTestFile->Remove(true); + } + + if (NS_SUCCEEDED(rv)) { + rv = writeTestFile->Create(nsIFile::NORMAL_FILE_TYPE, 0666); + (void)writeTestFile->Remove(true); + } + + ProfileStatus status = NS_SUCCEEDED(rv) ? PROFILE_STATUS_OK + : PROFILE_STATUS_OTHER_ERROR; + if (NS_ERROR_FILE_ACCESS_DENIED == rv) + status = PROFILE_STATUS_ACCESS_DENIED; + else if (NS_ERROR_FILE_READ_ONLY == rv) + status = PROFILE_STATUS_READ_ONLY; + + return status; +} + +static ProfileStatus CheckProfileWriteAccess(nsIToolkitProfile* aProfile) +{ + nsCOMPtr<nsIFile> profileDir; + nsresult rv = aProfile->GetRootDir(getter_AddRefs(profileDir)); + if (NS_FAILED(rv)) + return PROFILE_STATUS_OTHER_ERROR; + + return CheckProfileWriteAccess(profileDir); +} + static bool gDoMigration = false; static bool gDoProfileReset = false;
@@ -2140,13 +2208,18 @@ SelectProfile(nsIProfileLock* *aResult, nsIToolkitProfileService* aProfileSvc, n NS_ENSURE_SUCCESS(rv, rv); }
+ ProfileStatus status = CheckProfileWriteAccess(lf); + if (PROFILE_STATUS_OK != status) + return ProfileErrorDialog(lf, lf, status, nullptr, aNative, aResult); + // If a profile path is specified directory on the command line, then // assume that the temp directory is the same as the given directory. rv = NS_LockProfilePath(lf, lf, getter_AddRefs(unlocker), aResult); if (NS_SUCCEEDED(rv)) return rv;
- return ProfileLockedDialog(lf, lf, unlocker, aNative, aResult); + return ProfileErrorDialog(lf, lf, PROFILE_STATUS_IS_LOCKED, unlocker, + aNative, aResult); }
ar = CheckArg("createprofile", true, &arg); @@ -2231,6 +2304,10 @@ SelectProfile(nsIProfileLock* *aResult, nsIToolkitProfileService* aProfileSvc, n gDoProfileReset = false; }
+ ProfileStatus status = CheckProfileWriteAccess(profile); + if (PROFILE_STATUS_OK != status) + return ProfileErrorDialog(profile, status, nullptr, aNative, aResult); + nsCOMPtr<nsIProfileUnlocker> unlocker; rv = profile->Lock(getter_AddRefs(unlocker), aResult); if (NS_SUCCEEDED(rv)) { @@ -2239,7 +2316,8 @@ SelectProfile(nsIProfileLock* *aResult, nsIToolkitProfileService* aProfileSvc, n return NS_OK; }
- return ProfileLockedDialog(profile, unlocker, aNative, aResult); + return ProfileErrorDialog(profile, PROFILE_STATUS_IS_LOCKED, unlocker, + aNative, aResult); }
if (CanShowProfileManager()) { @@ -2293,7 +2371,8 @@ SelectProfile(nsIProfileLock* *aResult, nsIToolkitProfileService* aProfileSvc, n nsCOMPtr<nsIProfileUnlocker> unlocker; rv = profile->Lock(getter_AddRefs(unlocker), &tempProfileLock); if (NS_FAILED(rv)) - return ProfileLockedDialog(profile, unlocker, aNative, &tempProfileLock); + return ProfileErrorDialog(profile, PROFILE_STATUS_IS_LOCKED, + unlocker, aNative, &tempProfileLock); }
nsCOMPtr<nsIToolkitProfile> newProfile; @@ -2304,6 +2383,10 @@ SelectProfile(nsIProfileLock* *aResult, nsIToolkitProfileService* aProfileSvc, n gDoProfileReset = false; }
+ ProfileStatus status = CheckProfileWriteAccess(profile); + if (PROFILE_STATUS_OK != status) + return ProfileErrorDialog(profile, status, nullptr, aNative, aResult); + // If you close Firefox and very quickly reopen it, the old Firefox may // still be closing down. Rather than immediately showing the // "Firefox is running but is not responding" message, we spend a few @@ -2329,7 +2412,8 @@ SelectProfile(nsIProfileLock* *aResult, nsIToolkitProfileService* aProfileSvc, n PR_Sleep(kLockRetrySleepMS); } while (TimeStamp::Now() - start < TimeDuration::FromSeconds(kLockRetrySeconds));
- return ProfileLockedDialog(profile, unlocker, aNative, aResult); + return ProfileErrorDialog(profile, PROFILE_STATUS_IS_LOCKED, unlocker, + aNative, aResult); } }