Bug 1086936 part 1 - Add a UI switch to disable custom profile selection process in Aurora. r=bsmedberg,jaws f=matej,shorlander
authorPanos Astithas <past@mozilla.com>
Mon, 27 Oct 2014 20:52:20 +0200
changeset 235031 b8162870a2086db971aee986dbe8d04cdb6cdb95
parent 235030 2f7dd54a6f494ca0bc417867b5e9cd472be2e59d
child 235032 f0abb6e26c0902efc46823166ea9489ee737412c
push id611
push userraliiev@mozilla.com
push dateMon, 05 Jan 2015 23:23:16 +0000
treeherdermozilla-release@345cd3b9c445 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbsmedberg, jaws
bugs1086936
milestone35.0a2
Bug 1086936 part 1 - Add a UI switch to disable custom profile selection process in Aurora. r=bsmedberg,jaws f=matej,shorlander
browser/components/preferences/in-content/main.js
browser/components/preferences/in-content/main.xul
browser/components/preferences/main.js
browser/components/preferences/main.xul
browser/locales/en-US/chrome/browser/preferences/main.dtd
browser/themes/linux/preferences/preferences.css
browser/themes/osx/preferences/preferences.css
browser/themes/shared/incontentprefs/preferences.css
browser/themes/windows/preferences/preferences.css
toolkit/profile/nsIToolkitProfileService.idl
toolkit/profile/nsToolkitProfileService.cpp
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -92,16 +92,29 @@ var gMainPane = {
     setEventListener("e10sAutoStart", "command",
                      gMainPane.enableE10SChange);
     let e10sCheckbox = document.getElementById("e10sAutoStart");
     let e10sPref = document.getElementById("browser.tabs.remote.autostart");
     let e10sTempPref = document.getElementById("e10sTempPref");
     e10sCheckbox.checked = e10sPref.value || e10sTempPref.value;
 #endif
 
+#ifdef MOZ_DEV_EDITION
+    Cu.import("resource://gre/modules/osfile.jsm");
+    let uAppData = OS.Constants.Path.userApplicationDataDir;
+    let ignoreSeparateProfile = OS.Path.join(uAppData, "ignore-dev-edition-profile");
+
+    setEventListener("separateProfileMode", "command", gMainPane.separateProfileModeChange);
+    let separateProfileModeCheckbox = document.getElementById("separateProfileMode");
+    setEventListener("getStarted", "click", gMainPane.onGetStarted);
+
+    OS.File.stat(ignoreSeparateProfile).then(() => separateProfileModeCheckbox.checked = false,
+                                             () => separateProfileModeCheckbox.checked = true);
+#endif
+
     // Notify observers that the UI is now ready
     Components.classes["@mozilla.org/observer-service;1"]
               .getService(Components.interfaces.nsIObserverService)
               .notifyObservers(window, "main-pane-loaded", null);
   },
 
 #ifdef E10S_TESTING_ONLY
   enableE10SChange: function ()
@@ -149,16 +162,76 @@ var gMainPane = {
       }
     }
 
     // Revert the checkbox in case we didn't quit
     e10sCheckbox.checked = e10sPref.value || e10sTempPref.value;
   },
 #endif
 
+#ifdef MOZ_DEV_EDITION
+  separateProfileModeChange: function ()
+  {
+    function quitApp() {
+      Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit |  Ci.nsIAppStartup.eRestart);
+    }
+    function revertCheckbox(error) {
+      separateProfileModeCheckbox.checked = !separateProfileModeCheckbox.checked;
+      if (error) {
+        Cu.reportError("Failed to toggle separate profile mode: " + error);
+      }
+    }
+
+    const Cc = Components.classes, Ci = Components.interfaces;
+    let separateProfileModeCheckbox = document.getElementById("separateProfileMode");
+    let brandName = document.getElementById("bundleBrand").getString("brandShortName");
+    let bundle = document.getElementById("bundlePreferences");
+    let msg = bundle.getFormattedString(separateProfileModeCheckbox.checked ?
+                                        "featureEnableRequiresRestart" : "featureDisableRequiresRestart",
+                                        [brandName]);
+    let title = bundle.getFormattedString("shouldRestartTitle", [brandName]);
+    let shouldProceed = Services.prompt.confirm(window, title, msg)
+    if (shouldProceed) {
+      let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
+                         .createInstance(Ci.nsISupportsPRBool);
+      Services.obs.notifyObservers(cancelQuit, "quit-application-requested",
+                                   "restart");
+      shouldProceed = !cancelQuit.data;
+
+      if (shouldProceed) {
+        Cu.import("resource://gre/modules/osfile.jsm");
+        let uAppData = OS.Constants.Path.userApplicationDataDir;
+        let ignoreSeparateProfile = OS.Path.join(uAppData, "ignore-dev-edition-profile");
+
+        if (separateProfileModeCheckbox.checked) {
+          OS.File.remove(ignoreSeparateProfile).then(quitApp, revertCheckbox);
+        } else {
+          OS.File.writeAtomic(ignoreSeparateProfile, new Uint8Array()).then(quitApp, revertCheckbox);
+        }
+        return;
+      }
+    }
+
+    // Revert the checkbox in case we didn't quit
+    revertCheckbox();
+  },
+
+  onGetStarted: function (aEvent) {
+    const Cc = Components.classes, Ci = Components.interfaces;
+    let wm = Cc["@mozilla.org/appshell/window-mediator;1"]
+                .getService(Ci.nsIWindowMediator);
+    let win = wm.getMostRecentWindow("navigator:browser");
+
+    if (win) {
+      let accountsTab = win.gBrowser.addTab("about:accounts?action=migrateToDevEdition");
+      win.gBrowser.selectedTab = accountsTab;
+    }
+  },
+#endif
+
   // HOME PAGE
 
   /*
    * Preferences:
    *
    * browser.startup.homepage
    * - the user's home page, as a string; if the home page is a set of tabs,
    *   this will be those URLs separated by the pipe character "|"
--- a/browser/components/preferences/in-content/main.xul
+++ b/browser/components/preferences/in-content/main.xul
@@ -112,32 +112,43 @@
 </hbox>
 
 <!-- Startup -->
 <groupbox id="startupGroup"
           data-category="paneGeneral"
           hidden="true">
   <caption><label>&startup.label;</label></caption>
 
+#ifdef MOZ_DEV_EDITION
+  <vbox id="separateProfileBox">
+    <checkbox id="separateProfileMode"
+              label="&separateProfileMode.label;"/>
+    <hbox align="center" class="indent">
+      <label id="useFirefoxSync">&useFirefoxSync.label;</label>
+      <label id="getStarted" class="text-link">&getStarted.label;</label>
+    </hbox>
+  </vbox>
+#endif
+
 #ifdef E10S_TESTING_ONLY
   <checkbox id="e10sAutoStart"
             label="Enable E10S (multi-process)"/>
 #endif
 
 #ifdef HAVE_SHELL_SERVICE
   <vbox id="defaultBrowserBox">
     <hbox align="center">
       <checkbox id="alwaysCheckDefault" preference="browser.shell.checkDefaultBrowser"
-                label="&alwaysCheckDefault.label;" accesskey="&alwaysCheckDefault2.accesskey;"/>
+                label="&alwaysCheckDefault2.label;" accesskey="&alwaysCheckDefault2.accesskey;"/>
     </hbox>
     <deck id="setDefaultPane">
       <hbox align="center" class="indent">
         <label id="isNotDefaultLabel" flex="1">&isNotDefault.label;</label>
         <button id="setDefaultButton"
-                label="&setAsMyDefaultBrowser.label;" accesskey="&setAsMyDefaultBrowser.accesskey;"
+                label="&setAsMyDefaultBrowser2.label;" accesskey="&setAsMyDefaultBrowser2.accesskey;"
                 preference="pref.general.disable_button.default_browser"/>
       </hbox>
       <hbox align="center" class="indent">
         <label id="isDefaultLabel" flex="1">&isDefault.label;</label>
       </hbox>
     </deck>
     <separator class="thin"/>
   </vbox>
--- a/browser/components/preferences/main.js
+++ b/browser/components/preferences/main.js
@@ -55,22 +55,97 @@ var gMainPane = {
 #endif
 
     // set up the "use current page" label-changing listener
     this._updateUseCurrentButton();
     window.addEventListener("focus", this._updateUseCurrentButton.bind(this), false);
 
     this.updateBrowserStartupLastSession();
 
+#ifdef MOZ_DEV_EDITION
+    let separateProfileModeCheckbox = document.getElementById("separateProfileMode");
+    let listener = gMainPane.separateProfileModeChange.bind(gMainPane);
+    separateProfileModeCheckbox.addEventListener("command", listener);
+
+    let getStartedLink = document.getElementById("getStarted");
+    let syncListener = gMainPane.onGetStarted.bind(gMainPane);
+    getStartedLink.addEventListener("click", syncListener);
+
+    Cu.import("resource://gre/modules/osfile.jsm");
+    let uAppData = OS.Constants.Path.userApplicationDataDir;
+    let ignoreSeparateProfile = OS.Path.join(uAppData, "ignore-dev-edition-profile");
+
+    OS.File.stat(ignoreSeparateProfile).then(() => separateProfileModeCheckbox.checked = false,
+                                             () => separateProfileModeCheckbox.checked = true);
+#endif
+
     // Notify observers that the UI is now ready
     Components.classes["@mozilla.org/observer-service;1"]
               .getService(Components.interfaces.nsIObserverService)
               .notifyObservers(window, "main-pane-loaded", null);
   },
 
+#ifdef MOZ_DEV_EDITION
+  separateProfileModeChange: function ()
+  {
+    function quitApp() {
+      Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit |  Ci.nsIAppStartup.eRestart);
+    }
+    function revertCheckbox(error) {
+      separateProfileModeCheckbox.checked = !separateProfileModeCheckbox.checked;
+      if (error) {
+        Cu.reportError("Failed to toggle separate profile mode: " + error);
+      }
+    }
+
+    let separateProfileModeCheckbox = document.getElementById("separateProfileMode");
+    let brandName = document.getElementById("bundleBrand").getString("brandShortName");
+    let bundle = document.getElementById("bundlePreferences");
+    let msg = bundle.getFormattedString(separateProfileModeCheckbox.checked ?
+                                        "featureEnableRequiresRestart" : "featureDisableRequiresRestart",
+                                        [brandName]);
+    let title = bundle.getFormattedString("shouldRestartTitle", [brandName]);
+    let shouldProceed = Services.prompt.confirm(window, title, msg)
+    if (shouldProceed) {
+      let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
+                         .createInstance(Ci.nsISupportsPRBool);
+      Services.obs.notifyObservers(cancelQuit, "quit-application-requested",
+                                   "restart");
+      shouldProceed = !cancelQuit.data;
+
+      if (shouldProceed) {
+        Cu.import("resource://gre/modules/osfile.jsm");
+        let uAppData = OS.Constants.Path.userApplicationDataDir;
+        let ignoreSeparateProfile = OS.Path.join(uAppData, "ignore-dev-edition-profile");
+
+        if (separateProfileModeCheckbox.checked) {
+          OS.File.remove(ignoreSeparateProfile).then(quitApp, revertCheckbox);
+        } else {
+          OS.File.writeAtomic(ignoreSeparateProfile, new Uint8Array()).then(quitApp, revertCheckbox);
+        }
+      }
+    }
+
+    // Revert the checkbox in case we didn't quit
+    revertCheckbox();
+  },
+
+  onGetStarted: function (aEvent) {
+    const Cc = Components.classes, Ci = Components.interfaces;
+    let wm = Cc["@mozilla.org/appshell/window-mediator;1"]
+               .getService(Ci.nsIWindowMediator);
+    let win = wm.getMostRecentWindow("navigator:browser");
+
+    if (win) {
+      let accountsTab = win.gBrowser.addTab("about:accounts?action=migrateToDevEdition");
+      win.gBrowser.selectedTab = accountsTab;
+    }
+  },
+#endif
+
   // HOME PAGE
 
   /*
    * Preferences:
    *
    * browser.startup.homepage
    * - the user's home page, as a string; if the home page is a set of tabs,
    *   this will be those URLs separated by the pipe character "|"
--- a/browser/components/preferences/main.xul
+++ b/browser/components/preferences/main.xul
@@ -72,27 +72,38 @@
     </preferences>
     
     <stringbundle id="bundlePreferences" src="chrome://browser/locale/preferences/preferences.properties"/>
 
     <!-- Startup -->
     <groupbox id="startupGroup">
       <caption label="&startup.label;"/>
 
+#ifdef MOZ_DEV_EDITION
+      <vbox id="separateProfileBox">
+        <checkbox id="separateProfileMode"
+                  label="&separateProfileMode.label;"/>
+        <hbox align="center" class="indent">
+          <label id="useFirefoxSync">&useFirefoxSync.label;</label>
+          <label id="getStarted" class="text-link">&getStarted.label;</label>
+        </hbox>
+      </vbox>
+#endif
+
 #ifdef HAVE_SHELL_SERVICE
       <vbox id="defaultBrowserBox">
         <hbox align="center">
           <checkbox id="alwaysCheckDefault" preference="browser.shell.checkDefaultBrowser"
-                    label="&alwaysCheckDefault.label;" accesskey="&alwaysCheckDefault2.accesskey;"/>
+                    label="&alwaysCheckDefault2.label;" accesskey="&alwaysCheckDefault2.accesskey;"/>
         </hbox>
         <deck id="setDefaultPane">
           <hbox align="center" class="indent">
             <label id="isNotDefaultLabel" flex="1">&isNotDefault.label;</label>
             <button id="setDefaultButton"
-                    label="&setAsMyDefaultBrowser.label;" accesskey="&setAsMyDefaultBrowser.accesskey;"
+                    label="&setAsMyDefaultBrowser2.label;" accesskey="&setAsMyDefaultBrowser2.accesskey;"
                     oncommand="gMainPane.setDefaultBrowser();"
                     preference="pref.general.disable_button.default_browser"/>
           </hbox>
           <hbox align="center" class="indent">
             <label id="isDefaultLabel" flex="1">&isDefault.label;</label>
           </hbox>
         </deck>
         <separator class="thin"/>
--- a/browser/locales/en-US/chrome/browser/preferences/main.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/main.dtd
@@ -26,15 +26,18 @@
 <!ENTITY saveTo.accesskey "v">
 <!ENTITY chooseFolderWin.label        "Browse…">
 <!ENTITY chooseFolderWin.accesskey    "o">
 <!ENTITY chooseFolderMac.label        "Choose…">
 <!ENTITY chooseFolderMac.accesskey    "e">
 <!ENTITY alwaysAsk.label "Always ask me where to save files">
 <!ENTITY alwaysAsk.accesskey "A">
 
-<!ENTITY alwaysCheckDefault.label         "Always check to see if &brandShortName; is the default browser on startup">
+<!ENTITY alwaysCheckDefault2.label        "Always check if &brandShortName; is your default browser">
 <!ENTITY alwaysCheckDefault2.accesskey    "w">
-<!ENTITY setAsMyDefaultBrowser.label      "Make &brandShortName; My Default Browser">
-<!ENTITY setAsMyDefaultBrowser.accesskey  "D">
+<!ENTITY setAsMyDefaultBrowser2.label     "Make Default">
+<!ENTITY setAsMyDefaultBrowser2.accesskey "D">
 <!ENTITY isDefault.label                  "&brandShortName; is currently your default browser">
 <!ENTITY isNotDefault.label               "&brandShortName; is not your default browser">
 
+<!ENTITY separateProfileMode.label        "Allow &brandShortName; and Firefox to run at the same time">
+<!ENTITY useFirefoxSync.label             "Tip: This uses separate profiles. Use Sync to share data between them.">
+<!ENTITY getStarted.label                 "Start using Sync…">
--- a/browser/themes/linux/preferences/preferences.css
+++ b/browser/themes/linux/preferences/preferences.css
@@ -50,16 +50,21 @@ label.small {
   font-size: smaller;
 }
 
 #tabPrefsBox {
   margin: 5px;
 }
 
 /* General Pane */
+#useFirefoxSync,
+#getStarted {
+  font-size: 90%;
+}
+
 #isNotDefaultLabel {
   font-weight: bold;
 }
 
 /* Content Pane */
 #translationAttributionImage {
   width: 70px;
   cursor: pointer;
--- a/browser/themes/osx/preferences/preferences.css
+++ b/browser/themes/osx/preferences/preferences.css
@@ -164,16 +164,21 @@ caption {
 }
 
 #OCSPDialogPane {
   font: message-box !important;
 }
 
 /* General Pane */
 
+#useFirefoxSync,
+#getStarted {
+  font-size: 90%;
+}
+
 #isNotDefaultLabel {
   font-weight: bold;
 }
 
 /**
  * Update Preferences
  */
 #autoInstallOptions {
--- a/browser/themes/shared/incontentprefs/preferences.css
+++ b/browser/themes/shared/incontentprefs/preferences.css
@@ -105,16 +105,25 @@ treecol {
 
 #header-advanced {
   border-bottom: none;
   padding-bottom: 0;
 }
 
 /* General Pane */
 
+#useFirefoxSync  {
+  font-size: 90%;
+  -moz-margin-end: 8px !important;
+}
+
+#getStarted {
+  font-size: 90%;
+}
+
 #isNotDefaultLabel {
   font-weight: bold;
 }
 
 #downloadFolder {
   -moz-margin-start: 0;
 }
 
--- a/browser/themes/windows/preferences/preferences.css
+++ b/browser/themes/windows/preferences/preferences.css
@@ -50,16 +50,21 @@ label.small {
 }
 
 #tabPrefsBox {
   margin: 6px;
 }
 
 /* General Pane */
 
+#useFirefoxSync,
+#getStarted {
+  font-size: 90%;
+}
+
 #isNotDefaultLabel {
   font-weight: bold;
 }
 
 /* Content Pane */
 #translationAttributionImage {
   width: 70px;
   cursor: pointer;
--- a/toolkit/profile/nsIToolkitProfileService.idl
+++ b/toolkit/profile/nsIToolkitProfileService.idl
@@ -24,16 +24,22 @@ interface nsIToolkitProfileService : nsI
      */
     attribute nsIToolkitProfile selectedProfile;
 
     /**
      * The default profile (the one used or about to be used by the
      * browser if no other profile is specified at runtime). This is the profile
      * marked with Default=1 in profiles.ini and is usually the same as
      * selectedProfile, except on Developer Edition.
+     *
+     * Developer Edition uses a profile named "dev-edition-default" as the
+     * default profile (which it creates if it doesn't exist), unless a special
+     * empty file named "ignore-dev-edition-profile" is present next to
+     * profiles.ini. In that case Developer Edition behaves the same as any
+     * other build of Firefox.
      */
     attribute nsIToolkitProfile defaultProfile;
 
     /**
      * Get a profile by name. This is mainly for use by the -P
      * commandline flag.
      *
      * @param aName The profile name to find.
--- a/toolkit/profile/nsToolkitProfileService.cpp
+++ b/toolkit/profile/nsToolkitProfileService.cpp
@@ -419,16 +419,32 @@ nsToolkitProfileService::Init()
 
     nsAutoCString buffer;
     rv = parser.GetString("General", "StartWithLastProfile", buffer);
     if (NS_SUCCEEDED(rv) && buffer.EqualsLiteral("0"))
         mStartWithLast = false;
 
     nsToolkitProfile* currentProfile = nullptr;
 
+#ifdef MOZ_DEV_EDITION
+    nsCOMPtr<nsIFile> ignoreSeparateProfile;
+    rv = mAppData->Clone(getter_AddRefs(ignoreSeparateProfile));
+    if (NS_FAILED(rv))
+        return rv;
+
+    rv = ignoreSeparateProfile->AppendNative(NS_LITERAL_CSTRING("ignore-dev-edition-profile"));
+    if (NS_FAILED(rv))
+        return rv;
+
+    bool shouldIgnoreSeparateProfile;
+    rv = ignoreSeparateProfile->Exists(&shouldIgnoreSeparateProfile);
+    if (NS_FAILED(rv))
+        return rv;
+#endif
+
     unsigned int c = 0;
     bool foundAuroraDefault = false;
     for (c = 0; true; ++c) {
         nsAutoCString profileID("Profile");
         profileID.AppendInt(c);
 
         rv = parser.GetString(profileID.get(), "IsRelative", buffer);
         if (NS_FAILED(rv)) break;
@@ -480,30 +496,31 @@ nsToolkitProfileService::Init()
         NS_ENSURE_TRUE(currentProfile, NS_ERROR_OUT_OF_MEMORY);
 
         rv = parser.GetString(profileID.get(), "Default", buffer);
         if (NS_SUCCEEDED(rv) && buffer.EqualsLiteral("1") && !foundAuroraDefault) {
             mChosen = currentProfile;
             this->SetDefaultProfile(currentProfile);
         }
 #ifdef MOZ_DEV_EDITION
-        // Use the dev-edition-default profile if this is an Aurora build.
-        if (name.EqualsLiteral("dev-edition-default")) {
+        // Use the dev-edition-default profile if this is an Aurora build and
+        // ignore-dev-edition-profile is not present.
+        if (name.EqualsLiteral("dev-edition-default") && !shouldIgnoreSeparateProfile) {
             mChosen = currentProfile;
             foundAuroraDefault = true;
         }
 #endif
     }
 
 #ifdef MOZ_DEV_EDITION
     // Check if we are running Firefox, as we don't want to create a profile
     // on webapprt.
     bool isFirefox = strcmp(gAppData->ID,
                             "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}") == 0;
-    if (!foundAuroraDefault && isFirefox) {
+    if (!foundAuroraDefault && isFirefox && !shouldIgnoreSeparateProfile) {
         // If a single profile exists, it may not be already marked as default.
         // Do it now to avoid problems when we create the dev-edition-default profile.
         if (!mChosen && mFirst && !mFirst->mNext)
             this->SetDefaultProfile(mFirst);
 
         // Create a default profile for aurora, if none was found.
         nsCOMPtr<nsIToolkitProfile> profile;
         rv = CreateProfile(nullptr,