Commit f24043ce authored by Kathleen Brade's avatar Kathleen Brade Committed by Georg Koppen
Browse files

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".
parent c0d73900
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -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’
+111 −21
Original line number Diff line number Diff line
@@ -1953,6 +1953,14 @@ 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";

@@ -1991,8 +1999,8 @@ private:
} // namespace

static ReturnAbortOnError
ProfileLockedDialog(nsIFile* aProfileDir, nsIFile* aProfileLocalDir,
                    nsIProfileUnlocker* aUnlocker,
ProfileErrorDialog(nsIFile* aProfileDir, nsIFile* aProfileLocalDir,
                   ProfileStatus aStatus, nsIProfileUnlocker* aUnlocker,
                   nsINativeAppSupport* aNative, nsIProfileLock* *aResult)
{
  nsresult rv;
@@ -2020,19 +2028,31 @@ ProfileLockedDialog(nsIFile* aProfileDir, nsIFile* aProfileLocalDir,

    nsAutoString killMessage;
#ifndef XP_MACOSX
    rv = sb->FormatStringFromName(aUnlocker ? "restartMessageUnlocker"
                                            : "restartMessageNoUnlocker",
                                  params, 2, killMessage);
    static const char16_t kRestartUnlocker[] = MOZ_UTF16("restartMessageUnlocker");
    static const char16_t kRestartNoUnlocker[] = MOZ_UTF16("restartMessageNoUnlocker");
    static const char16_t kReadOnly[] = MOZ_UTF16("profileReadOnly");
#else
    rv = sb->FormatStringFromName(aUnlocker ? "restartMessageUnlockerMac"
                                            : "restartMessageNoUnlockerMac",
                                  params, 2, killMessage);
    static const char16_t kRestartUnlocker[] = MOZ_UTF16("restartMessageUnlockerMac");
    static const char16_t kRestartNoUnlocker[] = MOZ_UTF16("restartMessageNoUnlockerMac");
    static const char16_t kReadOnly[] = MOZ_UTF16("profileReadOnlyMac");
#endif
    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
    static const char16_t kAccessDenied[] = MOZ_UTF16("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, killMessage);

    const char16_t *titleKey = ((PROFILE_STATUS_READ_ONLY == aStatus) ||
                                (PROFILE_STATUS_ACCESS_DENIED == aStatus))
                                   ? MOZ_UTF16("profileProblemTitle")
                                   : MOZ_UTF16("restartTitle");

    nsAutoString killTitle;
    rv = sb->FormatStringFromName("restartTitle", params, 1, killTitle);
    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
    sb->FormatStringFromName(titleKey, params, 1, killTitle);

    if (gfxPlatform::IsHeadless()) {
      // TODO: make a way to turn off all dialogs when headless.
@@ -2133,8 +2153,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));
@@ -2150,8 +2171,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[] =
@@ -2295,6 +2316,53 @@ GetCurrentProfile(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;
static nsAutoCString gResetOldProfileName;
@@ -2418,6 +2486,15 @@ SelectProfile(nsIProfileLock* *aResult, nsIToolkitProfileService* aProfileSvc, n
      gDoProfileReset = false;
    }

    nsCOMPtr<nsIToolkitProfile> profile;
    rv = aProfileSvc->GetProfileByName(nsDependentCString(arg),
                                       getter_AddRefs(profile));
    if (NS_SUCCEEDED(rv)) {
      ProfileStatus status = CheckProfileWriteAccess(profile);
      if (PROFILE_STATUS_OK != status)
        return ProfileErrorDialog(profile, status, nullptr, aNative, aResult);
    }

    nsCOMPtr<nsIFile> lf;
    rv = XRE_GetFileFromPath(arg, getter_AddRefs(lf));
    NS_ENSURE_SUCCESS(rv, rv);
@@ -2432,13 +2509,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);
@@ -2526,7 +2608,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);
        }

        nsresult gotName = profile->GetName(gResetOldProfileName);
@@ -2554,7 +2637,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()) {
@@ -2637,7 +2721,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);
        }

        nsresult gotName = profile->GetName(gResetOldProfileName);
@@ -2659,6 +2744,10 @@ SelectProfile(nsIProfileLock* *aResult, nsIToolkitProfileService* aProfileSvc, n
        }
      }

      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
@@ -2684,7 +2773,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);
    }
  }