Bug 1455707: Detect when running an older version than previously ran with the selected profile. r=froydnj draft
authorDave Townsend <dtownsend@oxymoronical.com>
Tue, 22 May 2018 09:06:50 -0700
changeset 802437 95ccfef1c70d169dd19035ef25255dcb5553e985
parent 799556 4777e0648f1767d6b3c89a7982bad9c531d7d176
child 802438 74f50130abddfee6f78193a2fbde5d7a120568a3
push id111878
push userdtownsend@mozilla.com
push dateThu, 31 May 2018 19:46:10 +0000
reviewersfroydnj
bugs1455707
milestone62.0a1
Bug 1455707: Detect when running an older version than previously ran with the selected profile. r=froydnj Use the information in compatibility.ini to detect that the current running application is an older version than previously ran with the profile and in that case open a UI allowing the user to launch the profile manager, launch the previous instance of the application or quit. MozReview-Commit-ID: F7rwlvXiOfN
toolkit/xre/MacLaunchHelper.h
toolkit/xre/MacLaunchHelper.mm
toolkit/xre/nsAppRunner.cpp
toolkit/xre/nsUpdateDriver.cpp
--- a/toolkit/xre/MacLaunchHelper.h
+++ b/toolkit/xre/MacLaunchHelper.h
@@ -11,13 +11,13 @@
 #include <unistd.h>
 
 extern "C" {
   /**
    * Passing an aPid parameter to LaunchChildMac will wait for the launched
    * process to terminate. When the process terminates, aPid will be set to the
    * pid of the terminated process to confirm that it executed successfully.
    */
-  void LaunchChildMac(int aArgc, char** aArgv, pid_t* aPid = 0);
+  void LaunchChildMac(const char* binary, int aArgc, char** aArgv, pid_t* aPid = 0);
   bool LaunchElevatedUpdate(int aArgc, char** aArgv, pid_t* aPid = 0);
 }
 
 #endif
--- a/toolkit/xre/MacLaunchHelper.mm
+++ b/toolkit/xre/MacLaunchHelper.mm
@@ -14,22 +14,22 @@
 #include <crt_externs.h>
 #include <ServiceManagement/ServiceManagement.h>
 #include <Security/Authorization.h>
 #include <spawn.h>
 #include <stdio.h>
 
 using namespace mozilla;
 
-void LaunchChildMac(int aArgc, char** aArgv, pid_t* aPid)
+void LaunchChildMac(const char* binary, int aArgc, char** aArgv, pid_t* aPid)
 {
   MacAutoreleasePool pool;
 
   @try {
-    NSString* launchPath = [NSString stringWithUTF8String:aArgv[0]];
+    NSString* launchPath = [NSString stringWithUTF8String:binary];
     NSMutableArray* arguments = [NSMutableArray arrayWithCapacity:aArgc - 1];
     for (int i = 1; i < aArgc; i++) {
       [arguments addObject:[NSString stringWithUTF8String:aArgv[i]]];
     }
     NSTask* child = [NSTask launchedTaskWithLaunchPath:launchPath
                                              arguments:arguments];
     if (aPid) {
       *aPid = [child processIdentifier];
@@ -123,15 +123,15 @@ void AbortElevatedUpdate()
       currTry++;
     }
   }
   NSLog(@"Unable to clean up updater.");
 }
 
 bool LaunchElevatedUpdate(int aArgc, char** aArgv, pid_t* aPid)
 {
-  LaunchChildMac(aArgc, aArgv, aPid);
+  LaunchChildMac(aArgv[0], aArgc, aArgv, aPid);
   bool didSucceed = InstallPrivilegedHelper();
   if (!didSucceed) {
     AbortElevatedUpdate();
   }
   return didSucceed;
 }
--- 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;
   }
 
--- a/toolkit/xre/nsUpdateDriver.cpp
+++ b/toolkit/xre/nsUpdateDriver.cpp
@@ -674,20 +674,20 @@ ApplyUpdate(nsIFile *greDir, nsIFile *up
       LOG(("Failed to launch elevated update!"));
       exit(1);
     }
     exit(0);
   }
 
   if (isStaged) {
     // Launch the updater to replace the installation with the staged updated.
-    LaunchChildMac(argc, argv);
+    LaunchChildMac(updaterPath.get(), argc, argv);
   } else {
     // Launch the updater to either stage or apply an update.
-    LaunchChildMac(argc, argv, outpid);
+    LaunchChildMac(updaterPath.get(), argc, argv, outpid);
   }
   if (restart) {
     exit(0);
   }
 #else
   if (isStaged) {
     // Launch the updater to replace the installation with the staged updated.
     PR_CreateProcessDetached(updaterPath.get(), argv, nullptr, nullptr);