Bug 1264469 - Would be nice to be able to open 2 firefox sessions with a different profiles from about:profiles, r=smaug
authorAndrea Marchesini <amarchesini@mozilla.com>
Tue, 19 Apr 2016 12:00:59 +0200
changeset 293778 0eb46e22a78285e7eba9abbfd76bc98148812ba5
parent 293777 f58b6c6a39f92e4b6d415a4d84ed5ceac0407b73
child 293779 764146352ed7021c3fbd0776b1471f0a2dc0f9a4
push id75326
push useramarchesini@mozilla.com
push dateTue, 19 Apr 2016 10:01:12 +0000
treeherdermozilla-inbound@0eb46e22a782 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1264469
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1264469 - Would be nice to be able to open 2 firefox sessions with a different profiles from about:profiles, r=smaug
toolkit/components/startup/nsAppStartup.cpp
toolkit/components/startup/public/nsIAppStartup.idl
toolkit/content/aboutProfiles.js
toolkit/locales/en-US/chrome/global/aboutProfiles.properties
toolkit/xre/nsAppRunner.cpp
toolkit/xre/nsAppRunner.h
--- a/toolkit/components/startup/nsAppStartup.cpp
+++ b/toolkit/components/startup/nsAppStartup.cpp
@@ -7,34 +7,37 @@
 
 #include "nsIAppShellService.h"
 #include "nsPIDOMWindow.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIFile.h"
 #include "nsIObserverService.h"
 #include "nsIPrefBranch.h"
 #include "nsIPrefService.h"
+#include "nsIProcess.h"
 #include "nsIPromptService.h"
 #include "nsIStringBundle.h"
 #include "nsISupportsPrimitives.h"
+#include "nsIToolkitProfile.h"
 #include "nsIWebBrowserChrome.h"
 #include "nsIWindowMediator.h"
 #include "nsIWindowWatcher.h"
 #include "nsIXULRuntime.h"
 #include "nsIXULWindow.h"
 #include "nsNativeCharsetUtils.h"
 #include "nsThreadUtils.h"
 #include "nsAutoPtr.h"
 #include "nsString.h"
 #include "mozilla/Preferences.h"
 #include "GeckoProfiler.h"
 
 #include "prprf.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsWidgetsCID.h"
+#include "nsAppRunner.h"
 #include "nsAppShellCID.h"
 #include "nsXPCOMCIDInternal.h"
 #include "mozilla/Services.h"
 #include "nsIXPConnect.h"
 #include "jsapi.h"
 #include "js/Date.h"
 #include "prenv.h"
 #include "nsAppDirectoryServiceDefs.h"
@@ -974,8 +977,51 @@ nsAppStartup::TrackStartupCrashEnd()
 NS_IMETHODIMP
 nsAppStartup::RestartInSafeMode(uint32_t aQuitMode)
 {
   PR_SetEnv("MOZ_SAFE_MODE_RESTART=1");
   this->Quit(aQuitMode | nsIAppStartup::eRestart);
 
   return NS_OK;
 }
+
+NS_IMETHODIMP
+nsAppStartup::CreateInstanceWithProfile(nsIToolkitProfile* aProfile)
+{
+  if (NS_WARN_IF(!aProfile)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (NS_WARN_IF(gAbsoluteArgv0Path.IsEmpty())) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIFile> execPath;
+  nsresult rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(gAbsoluteArgv0Path),
+                                      true, getter_AddRefs(execPath));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<nsIProcess> process = do_CreateInstance(NS_PROCESS_CONTRACTID, &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = process->Init(execPath);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsAutoCString profileName;
+  rv = aProfile->GetName(profileName);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  const char *args[] = { "-P", profileName.get() };
+  rv = process->Run(false, args, 2);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
--- a/toolkit/components/startup/public/nsIAppStartup.idl
+++ b/toolkit/components/startup/public/nsIAppStartup.idl
@@ -1,16 +1,17 @@
 /* -*- Mode: IDL; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 interface nsICmdLineService;
+interface nsIToolkitProfile;
 
 [scriptable, uuid(6621f6d5-6c04-4a0e-9e74-447db221484e)]
 
 interface nsIAppStartup : nsISupports
 {
     /**
      * Create the hidden window.
      */
@@ -69,16 +70,24 @@ interface nsIAppStartup : nsISupports
      * Restart the application in safe mode
      * @param aQuitMode
      *        This parameter modifies how the app is shutdown.
      * @see nsIAppStartup::quit
      */
     void restartInSafeMode(in uint32_t aQuitMode);
 
     /**
+     * Run a new instance of this app with a specified profile
+     * @param aProfile
+     *        The profile we want to use.
+     * @see nsIAppStartup::quit
+     */
+    void createInstanceWithProfile(in nsIToolkitProfile aProfile);
+
+    /**
      * If the last startup crashed then increment a counter.
      * Set a flag so on next startup we can detect whether TrackStartupCrashEnd
      * was called (and therefore the application crashed).
      * @return whether safe mode is necessary
      */
     bool trackStartupCrashBegin();
 
     /**
--- a/toolkit/content/aboutProfiles.js
+++ b/toolkit/content/aboutProfiles.js
@@ -176,16 +176,25 @@ function display(profileData) {
     let defaultButton = document.createElement('button');
     defaultButton.appendChild(document.createTextNode(bundle.GetStringFromName('setAsDefault')));
     defaultButton.onclick = function() {
       defaultProfile(profileData.profile);
     };
     div.appendChild(defaultButton);
   }
 
+  if (!profileData.isCurrentProfile) {
+    let runButton = document.createElement('button');
+    runButton.appendChild(document.createTextNode(bundle.GetStringFromName('start')));
+    runButton.onclick = function() {
+      openProfile(profileData.profile);
+    };
+    div.appendChild(runButton);
+  }
+
   let sep = document.createElement('hr');
   div.appendChild(sep);
 }
 
 function CreateProfile(profile) {
   ProfileService.selectedProfile = profile;
   ProfileService.flush();
   refreshUI();
@@ -289,16 +298,28 @@ function removeProfile(profile) {
 
 function defaultProfile(profile) {
   ProfileService.defaultProfile = profile;
   ProfileService.selectedProfile = profile;
   ProfileService.flush();
   refreshUI();
 }
 
+function openProfile(profile) {
+  let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
+                     .createInstance(Ci.nsISupportsPRBool);
+  Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
+
+  if (cancelQuit.data) {
+    return;
+  }
+
+  Services.startup.createInstanceWithProfile(profile);
+}
+
 function restart(safeMode) {
   let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
                      .createInstance(Ci.nsISupportsPRBool);
   Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
 
   if (cancelQuit.data) {
     return;
   }
--- a/toolkit/locales/en-US/chrome/global/aboutProfiles.properties
+++ b/toolkit/locales/en-US/chrome/global/aboutProfiles.properties
@@ -12,16 +12,17 @@ rootDir = Root Directory
 # part of a backup scheme.)
 # In case localDIr and rootDir are equal, localDir is not shown.
 localDir = Local Directory
 currentProfile = This is the profile in use and it cannot be deleted.
 
 rename = Rename
 remove = Remove
 setAsDefault = Set as default profile
+start = Exec with this profile
 
 yes = yes
 no = no
 
 renameProfileTitle = Rename Profile
 renameProfile = Rename profile %S
 
 invalidProfileNameTitle = Invalid profile name
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -232,16 +232,18 @@ static const char gToolkitBuildID[] = NS
 
 static nsIProfileLock* gProfileLock;
 
 int    gRestartArgc;
 char **gRestartArgv;
 
 bool gIsGtest = false;
 
+nsString gAbsoluteArgv0Path;
+
 #ifdef MOZ_WIDGET_QT
 static int    gQtOnlyArgc;
 static char **gQtOnlyArgv;
 #endif
 
 #if defined(MOZ_WIDGET_GTK)
 #include <glib.h>
 #if defined(DEBUG) || defined(NS_BUILD_REFCNT_LOGGING)
@@ -4405,16 +4407,23 @@ XREMain::XRE_main(int argc, char* argv[]
   if (!mAppData)
     return 1;
   if (!mAppData->remotingName) {
     SetAllocatedString(mAppData->remotingName, mAppData->name);
   }
   // used throughout this file
   gAppData = mAppData;
 
+  nsCOMPtr<nsIFile> binFile;
+  rv = XRE_GetBinaryPath(argv[0], getter_AddRefs(binFile));
+  NS_ENSURE_SUCCESS(rv, 1);
+
+  rv = binFile->GetPath(gAbsoluteArgv0Path);
+  NS_ENSURE_SUCCESS(rv, 1);
+
   mozilla::IOInterposerInit ioInterposerGuard;
 
 #if MOZ_WIDGET_GTK == 2
   XRE_GlibInit();
 #endif
 
   // init
   bool exit = false;
--- a/toolkit/xre/nsAppRunner.h
+++ b/toolkit/xre/nsAppRunner.h
@@ -34,30 +34,32 @@
 
 class nsINativeAppSupport;
 class nsXREDirProvider;
 class nsIToolkitProfileService;
 class nsIFile;
 class nsIProfileLock;
 class nsIProfileUnlocker;
 class nsIFactory;
+class nsString;
 
 extern nsXREDirProvider* gDirServiceProvider;
 
 // NOTE: gAppData will be null in embedded contexts. The "size" parameter
 // will be the size of the original structure passed to XRE_main, but the
 // structure will have all of the members available.
 extern const nsXREAppData* gAppData;
 extern bool gSafeMode;
 
 extern int    gArgc;
 extern char **gArgv;
 extern int    gRestartArgc;
 extern char **gRestartArgv;
 extern bool gLogConsoleErrors;
+extern nsString gAbsoluteArgv0Path;
 
 extern bool gIsGtest;
 
 /**
  * Create the nativeappsupport implementation.
  *
  * @note XPCOMInit has not happened yet.
  */