Bug 1429185 - Disable all devtools entry points if devtools.policy.disable=true draft
authorJulian Descottes <jdescottes@mozilla.com>
Thu, 22 Feb 2018 17:44:03 +0100
changeset 759049 1fc0d95f23eb86eb89111f6d864910b79c817a09
parent 758396 ea3da643422c58d65335f1778dd6c89c09911585
child 759050 16cea1ce96d85debafa330b93a5f39629c00b52d
push id100261
push userjdescottes@mozilla.com
push dateFri, 23 Feb 2018 16:52:47 +0000
bugs1429185
milestone60.0a1
Bug 1429185 - Disable all devtools entry points if devtools.policy.disable=true MozReview-Commit-ID: 9ObZc8my1mE
browser/base/content/nsContextMenu.js
devtools/shim/devtools-startup-prefs.js
devtools/shim/devtools-startup.js
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -430,17 +430,18 @@ nsContextMenu.prototype = {
                   this.onMathML && !this.isContentSelected);
 
     var shouldShow = !(this.isContentSelected ||
                        this.onImage || this.onCanvas ||
                        this.onVideo || this.onAudio ||
                        this.onLink || this.onTextInput);
 
     var showInspect = this.inTabBrowser &&
-                      Services.prefs.getBoolPref("devtools.inspector.enabled", true);
+                      Services.prefs.getBoolPref("devtools.inspector.enabled", true) &&
+                      !Services.prefs.getBoolPref("devtools.policy.disabled", false);
 
     this.showItem("context-viewsource", shouldShow);
     this.showItem("context-viewinfo", shouldShow);
     // The page info is broken for WebExtension popups, as the browser is
     // destroyed when the popup is closed.
     this.setItemAttr("context-viewinfo", "disabled", this.webExtBrowserType === "popup");
     this.showItem("inspect-separator", showInspect);
     this.showItem("context-inspect", showInspect);
--- a/devtools/shim/devtools-startup-prefs.js
+++ b/devtools/shim/devtools-startup-prefs.js
@@ -30,8 +30,14 @@ pref("devtools.onboarding.experiment", "
 
 // If devtools.onboarding.experiment is set to "on" or "force", we will flip the
 // devtools.enabled preference to false once. The flag is used to make sure it is only
 // flipped once.
 pref("devtools.onboarding.experiment.flipped", false);
 
 // Flag to check if we already logged the devtools onboarding related probe.
 pref("devtools.onboarding.telemetry.logged", false);
+
+// Completely disable DevTools entry points. This should be merged with devtools.enabled,
+// see Bug 1440675. Note: if this preference is true, a debugger server can still be
+// started when passing --start-debugger-server as a command line argument, to allow
+// remote debugging. This can be disabled by locking devtools.chrome.enabled to false.
+pref("devtools.policy.disabled", false);
\ No newline at end of file
--- a/devtools/shim/devtools-startup.js
+++ b/devtools/shim/devtools-startup.js
@@ -27,16 +27,18 @@ const kDebuggerPrefs = [
 ];
 
 // If devtools.toolbar.visible is set to true, the developer toolbar should appear on
 // startup.
 const TOOLBAR_VISIBLE_PREF = "devtools.toolbar.visible";
 
 const DEVTOOLS_ENABLED_PREF = "devtools.enabled";
 
+const DEVTOOLS_POLICY_DISABLED_PREF = "devtools.policy.disabled";
+
 const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", {});
 ChromeUtils.defineModuleGetter(this, "Services",
                                "resource://gre/modules/Services.jsm");
 ChromeUtils.defineModuleGetter(this, "AppConstants",
                                "resource://gre/modules/AppConstants.jsm");
 ChromeUtils.defineModuleGetter(this, "CustomizableUI",
                                "resource:///modules/CustomizableUI.jsm");
 ChromeUtils.defineModuleGetter(this, "CustomizableWidgets",
@@ -187,69 +189,94 @@ DevToolsStartup.prototype = {
    */
   recorded: false,
 
   /**
    * Flag that indicates if the developer toggle was already added to customizableUI.
    */
   developerToggleCreated: false,
 
+  isDisabledByPolicy: function () {
+    return Services.prefs.getBoolPref(DEVTOOLS_POLICY_DISABLED_PREF, false);
+  },
+
   handle: function (cmdLine) {
-    let consoleFlag = cmdLine.handleFlag("jsconsole", false);
-    let debuggerFlag = cmdLine.handleFlag("jsdebugger", false);
-    let devtoolsFlag = cmdLine.handleFlag("devtools", false);
+    let flags = this.readCommandLineFlags(cmdLine);
 
     // handle() can be called after browser startup (e.g. opening links from other apps).
     let isInitialLaunch = cmdLine.state == Ci.nsICommandLine.STATE_INITIAL_LAUNCH;
     if (isInitialLaunch) {
       // Execute only on first launch of this browser instance.
-      let hasDevToolsFlag = consoleFlag || devtoolsFlag || debuggerFlag;
+      let hasDevToolsFlag = flags.console || flags.devtools || flags.debugger;
       this.setupEnabledPref(hasDevToolsFlag);
 
       // Store devtoolsFlag to check it later in onWindowReady.
-      this.devtoolsFlag = devtoolsFlag;
+      this.devtoolsFlag = flags.devtools;
       // Only top level Firefox Windows fire a browser-delayed-startup-finished event
       Services.obs.addObserver(this.onWindowReady, "browser-delayed-startup-finished");
 
       if (AppConstants.MOZ_DEV_EDITION) {
         // On DevEdition, the developer toggle is displayed by default in the navbar area
         // and should be created before the first paint.
         this.hookDeveloperToggle();
       }
 
       // Update menu items when devtools.enabled changes.
       Services.prefs.addObserver(DEVTOOLS_ENABLED_PREF, this.onEnabledPrefChanged);
     }
 
-    if (consoleFlag) {
+    if (flags.console) {
       this.handleConsoleFlag(cmdLine);
     }
-    if (debuggerFlag) {
+    if (flags.debugger) {
       this.handleDebuggerFlag(cmdLine);
     }
 
+    if (flags.debuggerServer) {
+      this.handleDebuggerServerFlag(cmdLine, flags.debuggerServer);
+    }
+  },
+
+  readCommandLineFlags(cmdLine) {
+    let consoleFlag = cmdLine.handleFlag("jsconsole", false);
+    let debuggerFlag = cmdLine.handleFlag("jsdebugger", false);
+    let devtoolsFlag = cmdLine.handleFlag("devtools", false);
+
     let debuggerServerFlag;
     try {
       debuggerServerFlag =
         cmdLine.handleFlagWithParam("start-debugger-server", false);
     } catch (e) {
       // We get an error if the option is given but not followed by a value.
       // By catching and trying again, the value is effectively optional.
       debuggerServerFlag = cmdLine.handleFlag("start-debugger-server", false);
     }
-    if (debuggerServerFlag) {
-      this.handleDebuggerServerFlag(cmdLine, debuggerServerFlag);
-    }
+
+    let disabled = this.isDisabledByPolicy();
+    return {
+      // Flags that would start a DevTools UI are ignored if DevTools are disabled by
+      // policy.
+      console: consoleFlag && !disabled,
+      debugger: debuggerFlag && !disabled,
+      devtools: devtoolsFlag && !disabled,
+      // Remote debugging is allowed regardless of DevTools being disabled by policy.
+      debuggerServer: debuggerServerFlag,
+    };
   },
 
   /**
    * Called when receiving the "browser-delayed-startup-finished" event for a new
    * top-level window.
    */
   onWindowReady(window) {
+    if (this.isDisabledByPolicy()) {
+      this.removeDevToolsMenus(window);
+      return;
+    }
+
     this.hookWindow(window);
 
     if (Services.prefs.getBoolPref(TOOLBAR_VISIBLE_PREF, false)) {
       // Loading devtools-browser will open the developer toolbar by also checking this
       // pref.
       this.initDevTools();
     }
 
@@ -258,16 +285,24 @@ DevToolsStartup.prototype = {
     if (!this._firstWindowReadyReceived) {
       this.onFirstWindowReady(window);
       this._firstWindowReadyReceived = true;
     }
 
     JsonView.initialize();
   },
 
+  removeDevToolsMenus(window) {
+    // This will hide the "Tools > Web Developer" menu.
+    window.document.getElementById("webDeveloperMenu").setAttribute("hidden", "true");
+    // This will hide the "Web Developer" item in the hamburger menu.
+    window.document.getElementById("appMenu-developer-button").setAttribute("hidden",
+      "true");
+  },
+
   onFirstWindowReady(window) {
     if (this.devtoolsFlag) {
       this.handleDevToolsFlag(window);
     }
 
     // Wait until we get a window before sending a ping to telemetry to avoid slowing down
     // the startup phase.
     this.pingOnboardingTelemetry();
@@ -611,16 +646,22 @@ DevToolsStartup.prototype = {
    * @param {String} reason
    *        One of "KeyShortcut", "SystemMenu", "HamburgerMenu", "ContextMenu",
    *        "CommandLine".
    * @param {String} keyId
    *        Optional. If the onboarding flow was triggered by a keyboard shortcut, pass
    *        the shortcut key id (or toolId) to about:devtools.
    */
   openInstallPage: function (reason, keyId) {
+    // If DevTools are completely disabled, bail out here as this might be called directly
+    // from other files.
+    if (this.isDisabledByPolicy()) {
+      return;
+    }
+
     let { gBrowser } = Services.wm.getMostRecentWindow("navigator:browser");
 
     // Focus about:devtools tab if there is already one opened in the current window.
     for (let tab of gBrowser.tabs) {
       let browser = tab.linkedBrowser;
       // browser.documentURI might be undefined if the browser tab is still loading.
       let location = browser.documentURI ? browser.documentURI.spec : "";
       if (location.startsWith("about:devtools") &&