--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -1579,16 +1579,17 @@ DumpHelp()
" --profile <path> Start with profile at <path>.\n"
" --migration Start with migration wizard.\n"
" --ProfileManager Start with ProfileManager.\n"
" --no-remote Do not accept or send remote commands; implies\n"
" --new-instance.\n"
" --new-instance Open new instance, not a new window in running instance.\n"
" --UILocale <locale> Start with <locale> resources as UI Locale.\n"
" --safe-mode Disables extensions and themes for this session.\n"
+ " --allow-downgrade Allows downgrading a profile.\n"
" -MOZ_LOG=<modules> Treated as MOZ_LOG=<modules> environment variable, overrides it.\n"
" -MOZ_LOG_FILE=<file> Treated as MOZ_LOG_FILE=<file> environment variable, overrides it.\n"
" If MOZ_LOG_FILE is not specified as an argument or as an environment variable,\n"
" logging will be written to stdout.\n"
, (const char*)gAppData->name);
#if defined(XP_WIN)
printf(" --console Start %s with a debugging console.\n", (const char*) gAppData->name);
@@ -1790,17 +1791,18 @@ RegisterApplicationRestartChanged(const
}
}
#endif // XP_WIN
// If aBlankCommandLine is true, then the application will be launched with a
// blank command line instead of being launched with the same command line that
// it was initially started with.
static nsresult LaunchChild(nsINativeAppSupport* aNative,
- bool aBlankCommandLine = false)
+ bool aBlankCommandLine = false,
+ nsIFile* binary = nullptr)
{
aNative->Quit(); // release DDE mutex, if we're holding it
// Restart this process by exec'ing it into the current process
// if supported by the platform. Otherwise, use NSPR.
#ifdef MOZ_JPROF
// make sure JPROF doesn't think we're E10s
@@ -1811,23 +1813,33 @@ static nsresult LaunchChild(nsINativeApp
gRestartArgc = 1;
gRestartArgv[gRestartArgc] = nullptr;
}
SaveToEnv("MOZ_LAUNCHED_CHILD=1");
#if !defined(MOZ_WIDGET_ANDROID) // Android has separate restart code.
#if defined(XP_MACOSX)
+ nsAutoCString binaryPath;
+ if (binary) {
+ binary->GetNativePath(binaryPath);
+ } else {
+ binaryPath = gRestartArgv[0];
+ }
CommandLineServiceMac::SetupMacCommandLine(gRestartArgc, gRestartArgv, true);
- LaunchChildMac(gRestartArgc, gRestartArgv);
+ LaunchChildMac(binaryPath.get(), gRestartArgc, gRestartArgv);
#else
- nsCOMPtr<nsIFile> lf;
- nsresult rv = XRE_GetBinaryPath(getter_AddRefs(lf));
- if (NS_FAILED(rv))
- return rv;
+ nsresult rv;
+
+ nsCOMPtr<nsIFile> lf(binary);
+ if (!lf) {
+ rv = XRE_GetBinaryPath(getter_AddRefs(lf));
+ if (NS_FAILED(rv))
+ return rv;
+ }
#if defined(XP_WIN)
nsAutoString exePath;
rv = lf->GetPath(exePath);
if (NS_FAILED(rv))
return rv;
HANDLE hProcess;
@@ -2065,108 +2077,118 @@ ProfileLockedDialog(nsIToolkitProfile* a
return ProfileLockedDialog(profileDir, profileLocalDir, aUnlocker, aNative,
aResult);
}
static const char kProfileManagerURL[] =
"chrome://mozapps/content/profile/profileSelection.xul";
static ReturnAbortOnError
-ShowProfileManager(nsIToolkitProfileService* aProfileSvc,
- nsINativeAppSupport* aNative)
+ShowProfileManagerInner(nsIToolkitProfileService* aProfileSvc,
+ nsINativeAppSupport* aNative)
{
nsresult rv;
nsCOMPtr<nsIFile> profD, profLD;
char16_t* profileNamePtr;
nsAutoCString profileName;
- {
- ScopedXPCOMStartup xpcom;
- rv = xpcom.Initialize();
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Initialize the graphics prefs, some of the paths need them before
- // any other graphics is initialized (e.g., showing the profile chooser.)
- gfxPrefs::GetSingleton();
-
- rv = xpcom.SetWindowCreator(aNative);
- NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+ // Initialize the graphics prefs, some of the paths need them before
+ // any other graphics is initialized (e.g., showing the profile chooser.)
+ gfxPrefs::GetSingleton();
#ifdef XP_MACOSX
- CommandLineServiceMac::SetupMacCommandLine(gRestartArgc, gRestartArgv, true);
+ CommandLineServiceMac::SetupMacCommandLine(gRestartArgc, gRestartArgv, true);
#endif
#ifdef XP_WIN
- // we don't have to wait here because profile manager window will pump
- // and DDE message will be handled
- ProcessDDE(aNative, false);
+ // we don't have to wait here because profile manager window will pump
+ // and DDE message will be handled
+ ProcessDDE(aNative, false);
#endif
- { //extra scoping is needed so we release these components before xpcom shutdown
- nsCOMPtr<nsIWindowWatcher> windowWatcher
- (do_GetService(NS_WINDOWWATCHER_CONTRACTID));
- nsCOMPtr<nsIDialogParamBlock> ioParamBlock
- (do_CreateInstance(NS_DIALOGPARAMBLOCK_CONTRACTID));
- nsCOMPtr<nsIMutableArray> dlgArray (do_CreateInstance(NS_ARRAY_CONTRACTID));
- NS_ENSURE_TRUE(windowWatcher && ioParamBlock && dlgArray, NS_ERROR_FAILURE);
-
- ioParamBlock->SetObjects(dlgArray);
-
- nsCOMPtr<nsIAppStartup> appStartup
- (do_GetService(NS_APPSTARTUP_CONTRACTID));
- NS_ENSURE_TRUE(appStartup, NS_ERROR_FAILURE);
-
- nsCOMPtr<mozIDOMWindowProxy> newWindow;
- rv = windowWatcher->OpenWindow(nullptr,
- kProfileManagerURL,
- "_blank",
- "centerscreen,chrome,modal,titlebar",
- ioParamBlock,
- getter_AddRefs(newWindow));
-
- NS_ENSURE_SUCCESS_LOG(rv, rv);
-
- aProfileSvc->Flush();
-
- int32_t dialogConfirmed;
- rv = ioParamBlock->GetInt(0, &dialogConfirmed);
- if (NS_FAILED(rv) || dialogConfirmed == 0) return NS_ERROR_ABORT;
-
- nsCOMPtr<nsIProfileLock> lock;
- rv = dlgArray->QueryElementAt(0, NS_GET_IID(nsIProfileLock),
- getter_AddRefs(lock));
- NS_ENSURE_SUCCESS_LOG(rv, rv);
-
- rv = lock->GetDirectory(getter_AddRefs(profD));
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = lock->GetLocalDirectory(getter_AddRefs(profLD));
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = ioParamBlock->GetString(0, &profileNamePtr);
- NS_ENSURE_SUCCESS(rv, rv);
-
- CopyUTF16toUTF8(profileNamePtr, profileName);
- free(profileNamePtr);
-
- lock->Unlock();
- }
+ { //extra scoping is needed so we release these components before xpcom shutdown
+ nsCOMPtr<nsIWindowWatcher> windowWatcher
+ (do_GetService(NS_WINDOWWATCHER_CONTRACTID));
+ nsCOMPtr<nsIDialogParamBlock> ioParamBlock
+ (do_CreateInstance(NS_DIALOGPARAMBLOCK_CONTRACTID));
+ nsCOMPtr<nsIMutableArray> dlgArray (do_CreateInstance(NS_ARRAY_CONTRACTID));
+ NS_ENSURE_TRUE(windowWatcher && ioParamBlock && dlgArray, NS_ERROR_FAILURE);
+
+ ioParamBlock->SetObjects(dlgArray);
+
+ nsCOMPtr<nsIAppStartup> appStartup
+ (do_GetService(NS_APPSTARTUP_CONTRACTID));
+ NS_ENSURE_TRUE(appStartup, NS_ERROR_FAILURE);
+
+ nsCOMPtr<mozIDOMWindowProxy> newWindow;
+ rv = windowWatcher->OpenWindow(nullptr,
+ kProfileManagerURL,
+ "_blank",
+ "centerscreen,chrome,modal,titlebar",
+ ioParamBlock,
+ getter_AddRefs(newWindow));
+
+ NS_ENSURE_SUCCESS_LOG(rv, rv);
+
+ aProfileSvc->Flush();
+
+ int32_t dialogConfirmed;
+ rv = ioParamBlock->GetInt(0, &dialogConfirmed);
+ if (NS_FAILED(rv) || dialogConfirmed == 0) return NS_ERROR_ABORT;
+
+ nsCOMPtr<nsIProfileLock> lock;
+ rv = dlgArray->QueryElementAt(0, NS_GET_IID(nsIProfileLock),
+ getter_AddRefs(lock));
+ NS_ENSURE_SUCCESS_LOG(rv, rv);
+
+ rv = lock->GetDirectory(getter_AddRefs(profD));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = lock->GetLocalDirectory(getter_AddRefs(profLD));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = ioParamBlock->GetString(0, &profileNamePtr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ CopyUTF16toUTF8(profileNamePtr, profileName);
+ free(profileNamePtr);
+
+ lock->Unlock();
}
SaveFileToEnv("XRE_PROFILE_PATH", profD);
SaveFileToEnv("XRE_PROFILE_LOCAL_PATH", profLD);
SaveWordToEnv("XRE_PROFILE_NAME", profileName);
bool offline = false;
aProfileSvc->GetStartOffline(&offline);
if (offline) {
SaveToEnv("XRE_START_OFFLINE=1");
}
+ return NS_OK;
+}
+
+static ReturnAbortOnError
+ShowProfileManager(nsIToolkitProfileService* aProfileSvc,
+ nsINativeAppSupport* aNative)
+{
+ {
+ ScopedXPCOMStartup xpcom;
+ nsresult rv = xpcom.Initialize();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = xpcom.SetWindowCreator(aNative);
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+
+ rv = ShowProfileManagerInner(aProfileSvc, aNative);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
return LaunchChild(aNative);
}
/**
* Get the currently running profile using its root directory.
*
* @param aProfileSvc The profile service
* @param aCurrentProfileRoot The root directory of the current profile.
@@ -2590,46 +2612,222 @@ SelectProfile(nsIProfileLock* *aResult,
return ProfileLockedDialog(profile, unlocker, aNative, aResult);
}
}
return ShowProfileManager(aProfileSvc, aNative);
}
+#ifdef MOZ_BUILD_APP_IS_BROWSER
+static const char kProfileDowngradeURL[] =
+ "chrome://mozapps/content/profile/profileDowngrade.xul";
+
+static ReturnAbortOnError
+CheckDowngrade(nsIFile* aProfileDir, nsIFile* aProfileLocalDir,
+ nsACString & aProfileName, nsIFile* aLastBinary,
+ nsINativeAppSupport* aNative, nsIToolkitProfileService* aProfileSvc)
+{
+ int32_t result = 0;
+
+ {
+ if (gfxPlatform::IsHeadless()) {
+ // TODO: make a way to turn off all dialogs when headless.
+ Output(true, "This profile was last used with a newer version of this application. Please create a new profile.\n");
+ return NS_ERROR_ABORT;
+ }
+
+ ScopedXPCOMStartup xpcom;
+ nsresult rv = xpcom.Initialize();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = xpcom.SetWindowCreator(aNative);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ { //extra scoping is needed so we release these components before xpcom shutdown
+ bool hasSync = false;
+ nsCOMPtr<nsIPrefService> prefSvc = do_GetService("@mozilla.org/preferences-service;1");
+ NS_ENSURE_TRUE(prefSvc, rv);
+
+ nsCOMPtr<nsIFile> prefsFile;
+ rv = aProfileDir->Clone(getter_AddRefs(prefsFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = prefsFile->Append(NS_LITERAL_STRING("prefs.js"));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = prefSvc->ReadUserPrefsFromFile(prefsFile);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(prefSvc);
+
+ rv = prefBranch->PrefHasUserValue("services.sync.username", &hasSync);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIWindowWatcher> windowWatcher = do_GetService(NS_WINDOWWATCHER_CONTRACTID);
+ NS_ENSURE_TRUE(windowWatcher, NS_ERROR_ABORT);
+
+ nsCOMPtr<nsIAppStartup> appStartup = do_GetService(NS_APPSTARTUP_CONTRACTID);
+ NS_ENSURE_TRUE(appStartup, NS_ERROR_ABORT);
+
+ nsCOMPtr<nsIDialogParamBlock> paramBlock = do_CreateInstance(NS_DIALOGPARAMBLOCK_CONTRACTID);
+ NS_ENSURE_TRUE(paramBlock, NS_ERROR_ABORT);
+
+ paramBlock->SetInt(0, hasSync ? 1 : 0);
+ paramBlock->SetInt(1, aLastBinary ? 1 : 0);
+
+ nsCOMPtr<mozIDOMWindowProxy> newWindow;
+ rv = windowWatcher->OpenWindow(nullptr, kProfileDowngradeURL, "_blank",
+ "centerscreen,chrome,modal,titlebar",
+ paramBlock, getter_AddRefs(newWindow));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ paramBlock->GetInt(2, &result);
+
+ if (result == 1) {
+ // Launch binary
+ if (!aLastBinary) {
+ return NS_ERROR_ABORT;
+ }
+
+ // We're re-launching something. Pass through the profile settings and
+ // unlock the profile.
+
+ gProfileLock->Unlock();
+
+#ifdef XP_MACOSX
+ CommandLineServiceMac::SetupMacCommandLine(gRestartArgc, gRestartArgv, true);
+#endif
+
+ SaveFileToEnv("XRE_PROFILE_PATH", aProfileDir);
+ SaveFileToEnv("XRE_PROFILE_LOCAL_PATH", aProfileLocalDir);
+ SaveWordToEnv("XRE_PROFILE_NAME", aProfileName);
+ } else if (result == 2) {
+ // Launch the profile manager
+ gProfileLock->Unlock();
+
+ rv = ShowProfileManagerInner(aProfileSvc, aNative);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (hasSync) {
+ SaveToEnv("XRE_SETUP_SYNC=1");
+ }
+ } else {
+ // Cancel
+ return NS_ERROR_ABORT;
+ }
+ }
+ }
+
+ // At this point we're either launching the last binary or launching whatever
+ // profile the manager selected.
+ if (result == 1) {
+ return LaunchChild(aNative, false, aLastBinary);
+ } else {
+ return LaunchChild(aNative);
+ }
+}
+#endif
+
/**
* Checks the compatibility.ini file to see if we have updated our application
* or otherwise invalidated our caches. If the application has been updated,
* we return false; otherwise, we return true. We also write the status
* of the caches (valid/invalid) into the return param aCachesOK. The aCachesOK
- * is always invalid if the application has been updated.
+ * is always invalid if the application has been updated. aIsDowngrade is set to
+ * true if the current application is older than that previously used by the
+ * profile. aLastBinary is the previous binary used with this profile if it
+ * still exists and is in a different location to the current binary.
*/
static bool
CheckCompatibility(nsIFile* aProfileDir, const nsCString& aVersion,
const nsCString& aOSABI, nsIFile* aXULRunnerDir,
nsIFile* aAppDir, nsIFile* aFlagFile,
- bool* aCachesOK)
+ bool* aCachesOK, bool* aIsDowngrade, nsIFile** aLastBinary)
{
*aCachesOK = false;
+ *aIsDowngrade = false;
+ *aLastBinary = nullptr;
+
nsCOMPtr<nsIFile> file;
aProfileDir->Clone(getter_AddRefs(file));
if (!file)
return false;
file->AppendNative(FILE_COMPATIBILITY_INFO);
nsINIParser parser;
nsresult rv = parser.Init(file);
if (NS_FAILED(rv))
return false;
nsAutoCString buf;
rv = parser.GetString("Compatibility", "LastVersion", buf);
- if (NS_FAILED(rv) || !aVersion.Equals(buf))
+ if (NS_FAILED(rv))
return false;
+ if (!aVersion.Equals(buf)) {
+ Version current(PromiseFlatCString(aVersion).get());
+ Version last(PromiseFlatCString(buf).get());
+ *aIsDowngrade = last > current;
+
+ if (*aIsDowngrade) {
+ nsCOMPtr<nsIFile> currentBinary;
+ rv = XRE_GetBinaryPath(getter_AddRefs(currentBinary));
+ if (NS_FAILED(rv))
+ return false;
+
+ // Get the relative path from the platform directory to the binary
+ nsCString relative;
+ rv = currentBinary->GetRelativeDescriptor(aXULRunnerDir, relative);
+ if (NS_FAILED(rv))
+ return false;
+
+ // Get the last binary
+ rv = parser.GetString("Compatibility", "LastPlatformDir", buf);
+ if (NS_FAILED(rv))
+ return false;
+
+ nsCOMPtr<nsIFile> lastPlatform;
+ rv = NS_NewNativeLocalFile(EmptyCString(), false,
+ getter_AddRefs(lastPlatform));
+ if (NS_FAILED(rv))
+ return false;
+
+ rv = lastPlatform->SetPersistentDescriptor(buf);
+ if (NS_FAILED(rv))
+ return false;
+
+ nsCOMPtr<nsIFile> lastBinary;
+ rv = NS_NewNativeLocalFile(EmptyCString(), false,
+ getter_AddRefs(lastBinary));
+ if (NS_FAILED(rv))
+ return false;
+
+ rv = lastBinary->SetRelativeDescriptor(lastPlatform, relative);
+ if (NS_FAILED(rv))
+ return false;
+
+ // If it is the current binary then ignore it
+ bool check;
+ rv = lastBinary->Equals(currentBinary, &check);
+ if (NS_FAILED(rv) || check)
+ return false;
+
+ // If it doesn't exist then ignore it
+ rv = lastBinary->Exists(&check);
+ if (NS_FAILED(rv) || !check)
+ return false;
+
+ // The last platform directory was different and still exists, pass it back
+ NS_ADDREF(*aLastBinary = lastBinary);
+ }
+
+ return false;
+ }
+
rv = parser.GetString("Compatibility", "LastOSABI", buf);
if (NS_FAILED(rv) || !aOSABI.Equals(buf))
return false;
rv = parser.GetString("Compatibility", "LastPlatformDir", buf);
if (NS_FAILED(rv))
return false;
@@ -4190,28 +4388,16 @@ XREMain::XRE_mainStartup(bool* aExitFlag
gProfileLock = mProfileLock;
rv = mProfileLock->GetDirectory(getter_AddRefs(mProfD));
NS_ENSURE_SUCCESS(rv, 1);
rv = mProfileLock->GetLocalDirectory(getter_AddRefs(mProfLD));
NS_ENSURE_SUCCESS(rv, 1);
- rv = mDirProvider.SetProfile(mProfD, mProfLD);
- NS_ENSURE_SUCCESS(rv, 1);
-
- //////////////////////// NOW WE HAVE A PROFILE ////////////////////////
-
- mozilla::Telemetry::SetProfileDir(mProfD);
-
- if (mAppData->flags & NS_XRE_ENABLE_CRASH_REPORTER)
- MakeOrSetMinidumpPath(mProfD);
-
- CrashReporter::SetProfileDirectory(mProfD);
-
nsAutoCString version;
BuildVersion(version);
#ifdef TARGET_OS_ABI
NS_NAMED_LITERAL_CSTRING(osABI, TARGET_OS_ABI);
#else
// No TARGET_XPCOM_ABI, but at least the OS is known
NS_NAMED_LITERAL_CSTRING(osABI, OS_TARGET "_UNKNOWN");
@@ -4230,20 +4416,47 @@ XREMain::XRE_mainStartup(bool* aExitFlag
if (mAppData->directory) {
rv = mAppData->directory->Clone(getter_AddRefs(flagFile));
}
if (flagFile) {
flagFile->AppendNative(FILE_INVALIDATE_CACHES);
}
bool cachesOK;
+ bool isDowngrade;
+ nsCOMPtr<nsIFile> lastBinary;
bool versionOK = CheckCompatibility(mProfD, version, osABI,
mDirProvider.GetGREDir(),
mAppData->directory, flagFile,
- &cachesOK);
+ &cachesOK, &isDowngrade,
+ getter_AddRefs(lastBinary));
+
+#ifdef MOZ_BUILD_APP_IS_BROWSER
+ if (isDowngrade && !CheckArg("allow-downgrade")) {
+ rv = CheckDowngrade(mProfD, mProfLD, mProfileName, lastBinary, mNativeApp,
+ mProfileSvc);
+ if (rv == NS_ERROR_LAUNCHED_CHILD_PROCESS ||
+ rv == NS_ERROR_ABORT) {
+ *aExitFlag = true;
+ return 0;
+ }
+ }
+#endif
+
+ rv = mDirProvider.SetProfile(mProfD, mProfLD);
+ NS_ENSURE_SUCCESS(rv, 1);
+
+ //////////////////////// NOW WE HAVE A PROFILE ////////////////////////
+
+ mozilla::Telemetry::SetProfileDir(mProfD);
+
+ if (mAppData->flags & NS_XRE_ENABLE_CRASH_REPORTER)
+ MakeOrSetMinidumpPath(mProfD);
+
+ CrashReporter::SetProfileDirectory(mProfD);
bool lastStartupWasCrash = CheckLastStartupWasCrash().unwrapOr(false);
if (CheckArg("purgecaches") || PR_GetEnv("MOZ_PURGE_CACHES") ||
lastStartupWasCrash) {
cachesOK = false;
}