Bug 1440573 - Policy: Disable safe mode. r=jimm,felipe a=jcristau
authorYuki Hiroshi <yuki@clear-code.com>
Thu, 15 Mar 2018 23:06:22 -0300
changeset 462855 8bb565a35f45362c86e7a6844d87b8b260c994fd
parent 462854 75e3a82eac12a435c4f6316ca0ddc2d01d299778
child 462856 45249cb4b63e49e38d91e6424d509579620bb858
push id1683
push usersfraser@mozilla.com
push dateThu, 26 Apr 2018 16:43:40 +0000
treeherdermozilla-release@5af6cb21869d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm, felipe, jcristau
bugs1440573
milestone60.0
Bug 1440573 - Policy: Disable safe mode. r=jimm,felipe a=jcristau This policy disables the safe-mode UI entry points. In addition, only on Windows when using GPO, it also disables entering Safe Mode by holding down the Shift Key
browser/base/content/utilityOverlay.js
browser/components/enterprisepolicies/Policies.jsm
browser/components/enterprisepolicies/schemas/policies-schema.json
browser/components/enterprisepolicies/tests/browser/browser.ini
browser/components/enterprisepolicies/tests/browser/browser_policy_disable_safemode.js
toolkit/content/aboutProfiles.js
toolkit/content/aboutSupport.js
toolkit/xre/nsAppRunner.cpp
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -827,16 +827,19 @@ function openTourPage() {
   ChromeUtils.import("resource:///modules/UITour.jsm", scope);
   openUILinkIn(scope.UITour.url, "tab");
 }
 
 function buildHelpMenu() {
   document.getElementById("feedbackPage")
           .disabled = !Services.policies.isAllowed("feedbackCommands");
 
+  document.getElementById("helpSafeMode")
+          .disabled = !Services.policies.isAllowed("safeMode");
+
   // Enable/disable the "Report Web Forgery" menu item.
   if (typeof gSafeBrowsing != "undefined") {
     gSafeBrowsing.setReportPhishingMenu();
   }
 }
 
 function isElementVisible(aElement) {
   if (!aElement)
--- a/browser/components/enterprisepolicies/Policies.jsm
+++ b/browser/components/enterprisepolicies/Policies.jsm
@@ -211,16 +211,24 @@ var Policies = {
       if (param) {
         manager.disallowFeature("privatebrowsing");
         manager.disallowFeature("about:privatebrowsing", true);
         setAndLockPref("browser.privatebrowsing.autostart", false);
       }
     }
   },
 
+  "DisableSafeMode": {
+    onBeforeUIStartup(manager, param) {
+      if (param) {
+        manager.disallowFeature("safeMode");
+      }
+    }
+  },
+
   "DisableSysAddonUpdate": {
     onBeforeAddons(manager, param) {
       if (param) {
         manager.disallowFeature("SysAddonUpdate");
       }
     }
   },
 
--- a/browser/components/enterprisepolicies/schemas/policies-schema.json
+++ b/browser/components/enterprisepolicies/schemas/policies-schema.json
@@ -165,16 +165,23 @@
 
     "DisablePrivateBrowsing": {
       "description": "Disables private browsing.",
       "first_available": "60.0",
 
       "type": "boolean"
     },
 
+    "DisableSafeMode": {
+      "description": "Prevents ability to restart in safe mode.",
+      "first_available": "60.0",
+
+      "type": "boolean"
+    },
+
     "DisableSysAddonUpdate": {
       "description": "Prevent the browser from installing and updating system addons.",
       "first_available": "60.0",
       "enterprise_only": true,
 
       "type": "boolean"
     },
 
--- a/browser/components/enterprisepolicies/tests/browser/browser.ini
+++ b/browser/components/enterprisepolicies/tests/browser/browser.ini
@@ -27,15 +27,16 @@ support-files =
 [browser_policy_disable_feedback_commands.js]
 [browser_policy_disable_formhistory.js]
 [browser_policy_disable_fxaccounts.js]
 [browser_policy_disable_fxscreenshots.js]
 [browser_policy_disable_masterpassword.js]
 [browser_policy_disable_pdfjs.js]
 [browser_policy_disable_pocket.js]
 [browser_policy_disable_privatebrowsing.js]
+[browser_policy_disable_safemode.js]
 [browser_policy_disable_shield.js]
 [browser_policy_display_bookmarks.js]
 [browser_policy_display_menu.js]
 [browser_policy_enable_tracking_protection.js]
 [browser_policy_proxy.js]
 [browser_policy_remember_passwords.js]
 [browser_policy_set_homepage.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/enterprisepolicies/tests/browser/browser_policy_disable_safemode.js
@@ -0,0 +1,42 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+add_task(async function setup() {
+  await setupPolicyEngineWithJson({
+    "policies": {
+      "DisableSafeMode": true
+    }
+  });
+});
+
+add_task(async function test_help_menu() {
+  buildHelpMenu();
+  let safeModeMenu = document.getElementById("helpSafeMode");
+  is(safeModeMenu.getAttribute("disabled"), "true",
+     "The `Restart with Add-ons Disabled...` item should be disabled");
+});
+
+add_task(async function test_safemode_from_about_support() {
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:support");
+
+  await ContentTask.spawn(tab.linkedBrowser, null, async function() {
+    let button = content.document.getElementById("restart-in-safe-mode-button");
+    is(button.getAttribute("disabled"), "true",
+       "The `Restart with Add-ons Disabled...` button should be disabled");
+  });
+
+  await BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function test_safemode_from_about_profiles() {
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:profiles");
+
+  await ContentTask.spawn(tab.linkedBrowser, null, async function() {
+    let button = content.document.getElementById("restart-in-safe-mode-button");
+    is(button.getAttribute("disabled"), "true",
+       "The `Restart with Add-ons Disabled...` button should be disabled");
+  });
+
+  await BrowserTestUtils.removeTab(tab);
+});
--- a/toolkit/content/aboutProfiles.js
+++ b/toolkit/content/aboutProfiles.js
@@ -80,17 +80,21 @@ function refreshUI() {
       isInUse,
     });
   }
 
   let createButton = document.getElementById("create-button");
   createButton.onclick = createProfileWizard;
 
   let restartSafeModeButton = document.getElementById("restart-in-safe-mode-button");
-  restartSafeModeButton.onclick = function() { restart(true); };
+  if (!Services.policies || Services.policies.isAllowed("safeMode")) {
+    restartSafeModeButton.onclick = function() { restart(true); };
+  } else {
+    restartSafeModeButton.setAttribute("disabled", "true");
+  }
 
   let restartNormalModeButton = document.getElementById("restart-button");
   restartNormalModeButton.onclick = function() { restart(false); };
 }
 
 function openDirectory(dir) {
   let nsLocalFile = Components.Constructor("@mozilla.org/file/local;1",
                                            "nsIFile", "initWithPath");
--- a/toolkit/content/aboutSupport.js
+++ b/toolkit/content/aboutSupport.js
@@ -1201,16 +1201,20 @@ function openProfileDirectory() {
 function populateActionBox() {
   if (ResetProfile.resetSupported()) {
     $("reset-box").style.display = "block";
     $("action-box").style.display = "block";
   }
   if (!Services.appinfo.inSafeMode && AppConstants.platform !== "android") {
     $("safe-mode-box").style.display = "block";
     $("action-box").style.display = "block";
+
+    if (Services.policies && !Services.policies.isAllowed("safeMode")) {
+      $("restart-in-safe-mode-button").setAttribute("disabled", "true");
+    }
   }
 }
 
 // Prompt user to restart the browser in safe mode
 function safeModeRestart() {
   let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
                      .createInstance(Ci.nsISupportsPRBool);
   Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -3152,16 +3152,50 @@ public:
   bool mDisableRemote;
 #endif
 
 #if defined(MOZ_WIDGET_GTK)
   GdkDisplay* mGdkDisplay;
 #endif
 };
 
+#ifdef XP_WIN
+namespace {
+
+bool PolicyHasRegValue(HKEY aKey, LPCTSTR aName, DWORD* aValue)
+{
+  HKEY hkey = NULL;
+  LONG ret = RegOpenKeyExW(aKey,
+    L"SOFTWARE\\Policies\\Mozilla\\Firefox", 0, KEY_READ, &hkey);
+  if (ret != ERROR_SUCCESS) {
+     return false;
+  }
+  nsAutoRegKey key(hkey);
+  DWORD len = sizeof(aValue);
+  ret = RegQueryValueExW(hkey, aName, 0, NULL, (LPBYTE)aValue, &len);
+  RegCloseKey(key);
+  return ret == ERROR_SUCCESS;
+}
+
+bool SafeModeBlockedByPolicy()
+{
+  LPCTSTR policyName = L"DisableSafeMode";
+  DWORD value;
+  if (PolicyHasRegValue(HKEY_LOCAL_MACHINE, policyName, &value)) {
+    return value == 1;
+  }
+  if (PolicyHasRegValue(HKEY_CURRENT_USER, policyName, &value)) {
+    return value == 1;
+  }
+  return false;
+}
+
+} // anonymous namespace
+#endif // XP_WIN
+
 /*
  * XRE_mainInit - Initial setup and command line parameter processing.
  * Main() will exit early if either return value != 0 or if aExitFlag is
  * true.
  */
 int
 XREMain::XRE_mainInit(bool* aExitFlag)
 {
@@ -3488,22 +3522,16 @@ XREMain::XRE_mainInit(bool* aExitFlag)
   if (override) {
     gRestartArgv[gRestartArgc++] = const_cast<char*>("-override");
     gRestartArgv[gRestartArgc++] = const_cast<char*>(override);
   }
 
   gRestartArgv[gRestartArgc] = nullptr;
 
 
-  if (EnvHasValue("MOZ_SAFE_MODE_RESTART")) {
-    gSafeMode = true;
-    // unset the env variable
-    SaveToEnv("MOZ_SAFE_MODE_RESTART=");
-  }
-
   ar = CheckArg("safe-mode", true);
   if (ar == ARG_BAD) {
     PR_fprintf(PR_STDERR, "Error: argument --safe-mode is invalid when argument --osint is specified\n");
     return 1;
   }
   if (ar == ARG_FOUND) {
     gSafeMode = true;
   }
@@ -3524,16 +3552,30 @@ XREMain::XRE_mainInit(bool* aExitFlag)
 
 #ifdef XP_MACOSX
   if ((GetCurrentEventKeyModifiers() & optionKey) &&
       !EnvHasValue("MOZ_DISABLE_SAFE_MODE_KEY"))
     gSafeMode = true;
 #endif
 
 #ifdef XP_WIN
+if (gSafeMode && SafeModeBlockedByPolicy()) {
+    gSafeMode = false;
+  }
+#endif
+
+  // The Safe Mode Policy should not be enforced for the env var case
+  // (used by updater and crash-recovery).
+  if (EnvHasValue("MOZ_SAFE_MODE_RESTART")) {
+    gSafeMode = true;
+    // unset the env variable
+    SaveToEnv("MOZ_SAFE_MODE_RESTART=");
+  }
+
+#ifdef XP_WIN
   {
     // Add CPU microcode version to the crash report as "CPUMicrocodeVersion".
     // It feels like this code may belong in nsSystemInfo instead.
     int cpuUpdateRevision = -1;
     HKEY key;
     static const WCHAR keyName[] =
       L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0";