Backed out 7 changesets (bug 942756) for Gi bustage
authorWes Kocher <wkocher@mozilla.com>
Thu, 24 Apr 2014 14:30:44 -0700
changeset 198620 bd55f7f8b48cfa2bc5b9590ca647f371aead6b5c
parent 198619 e063eb25827ac05c044d2c718b6c951b5a444dac
child 198621 36b040d46b79c229ee7c486185f9f0d8ef0aa269
push id3624
push userasasaki@mozilla.com
push dateMon, 09 Jun 2014 21:49:01 +0000
treeherdermozilla-beta@b1a5da15899a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs942756
milestone31.0a1
backs out87b1be303630eebd4bf9c9c5d4c009573bf872e1
eb385c85d88b160fcc7cbb9e0b1ef9c5d931ce31
d234447173a8b4fbfc94f2215c1c73613b0ed636
f7b85c252914c7b0855fff96262b48965f943f52
4e540736b330280c0d4420e74eee60ec1e3d43c9
824aec2863f55c98fd9d78954f541418737f7f07
43feed75916e326c5d74d1bf40f9d8112dee8c60
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
Backed out 7 changesets (bug 942756) for Gi bustage Backed out changeset 87b1be303630 (bug 942756) Backed out changeset eb385c85d88b (bug 942756) Backed out changeset d234447173a8 (bug 942756) Backed out changeset f7b85c252914 (bug 942756) Backed out changeset 4e540736b330 (bug 942756) Backed out changeset 824aec2863f5 (bug 942756) Backed out changeset 43feed75916e (bug 942756)
b2g/app/b2g.js
b2g/chrome/content/adbController.js
b2g/chrome/content/desktop.js
b2g/chrome/content/devtools.js
b2g/chrome/content/settings.js
b2g/chrome/content/shell.html
b2g/chrome/content/shell.js
b2g/chrome/jar.mn
b2g/components/B2GComponents.manifest
b2g/components/DebuggerServerController.js
b2g/components/moz.build
b2g/installer/package-manifest.in
browser/app/profile/firefox.js
browser/base/content/browser-sets.inc
browser/devtools/components/CommandLineHandler.js
browser/devtools/components/DebuggerServerController.js
browser/devtools/components/DevToolsComponents.manifest
browser/devtools/components/moz.build
browser/devtools/debugger/test/head.js
browser/devtools/devtools-clhandler.js
browser/devtools/devtools-clhandler.manifest
browser/devtools/framework/ToolboxProcess.jsm
browser/devtools/framework/gDevTools.jsm
browser/devtools/framework/test/browser_dynamic_tool_enabling.js
browser/devtools/framework/test/head.js
browser/devtools/framework/toolbox-options.xul
browser/devtools/moz.build
browser/devtools/profiler/test/browser_profiler_remote.js
browser/devtools/profiler/test/head.js
browser/installer/package-manifest.in
browser/locales/en-US/chrome/browser/devtools/gclicommands.properties
browser/locales/en-US/chrome/browser/devtools/toolbox.dtd
browser/metro/base/content/browser-ui.js
browser/metro/components/DebuggerServerController.js
browser/metro/components/components.manifest
browser/metro/components/moz.build
mobile/android/chrome/content/browser.js
mobile/android/components/DebuggerServerController.js
mobile/android/components/MobileComponents.manifest
mobile/android/installer/package-manifest.in
modules/libpref/src/init/all.js
testing/mochitest/mochitest_options.py
toolkit/devtools/DevToolsAppStartup.js
toolkit/devtools/DevToolsComponents.manifest
toolkit/devtools/apps/tests/debugger-protocol-helper.js
toolkit/devtools/gcli/commands/listen.js
toolkit/devtools/moz.build
toolkit/devtools/server/main.js
toolkit/devtools/server/moz.build
toolkit/devtools/server/nsIDebuggerServerController.idl
toolkit/devtools/server/tests/unit/head_dbg.js
toolkit/locales/en-US/chrome/global/devtools/debugger.properties
toolkit/mozapps/extensions/content/extensions.js
toolkit/mozapps/extensions/content/extensions.xml
toolkit/mozapps/extensions/test/browser/browser_debug_button.js
webapprt/prefs.js
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -842,23 +842,19 @@ pref("dom.inter-app-communication-api.en
 // Allow ADB to run for this many hours before disabling
 // (only applies when marionette is disabled)
 // 0 disables the timer.
 pref("b2g.adb.timeout-hours", 12);
 
 // InputMethod so we can do soft keyboards
 pref("dom.mozInputMethod.enabled", true);
 
-#ifdef MOZ_WIDGET_GONK
 // Absolute path to the devtool unix domain socket file used
 // to communicate with a usb cable via adb forward
 pref("devtools.debugger.unix-domain-socket", "/data/local/debugger-socket");
-#else
-pref("devtools.debugger.remote-port", 6000);
-#endif
 
 // enable Skia/GL (OpenGL-accelerated 2D drawing) for large enough 2d canvases,
 // falling back to Skia/software for smaller canvases
 #ifdef MOZ_WIDGET_GONK
 pref("gfx.canvas.azure.backends", "skia");
 pref("gfx.canvas.azure.accelerated", true);
 #endif
 
deleted file mode 100644
--- a/b2g/chrome/content/adbController.js
+++ /dev/null
@@ -1,267 +0,0 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
-/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
-/* 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/. */
-
-"use strict;"
-
-Components.utils.import("resource://gre/modules/Services.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this,
-    "DebuggerServer",
-    "resource://gre/modules/devtools/dbg-server.jsm");
-
-let adbController = {
-  DEBUG: false,
-  locked: undefined,
-  remoteDebuggerEnabled: undefined,
-  lockEnabled: undefined,
-  disableAdbTimer: null,
-  disableAdbTimeoutHours: 12,
-  umsActive: false,
-
-  init: function() {
-    this.updateState = this.updateState.bind(this);
-    Services.obs.addObserver(this.updateState,
-                             "debugger-connection-changed",
-                             false);
-    SettingsListener.observe('lockscreen.locked', undefined,
-                             v => this.setLockscreenState(v));
-    SettingsListener.observe('lockscreen.enabled', undefined,
-                             v => this.setLockscreenEnabled(v));
-    SettingsListener.observe('debugger.remote-mode', 'disabled',
-                             v => {
-                               this.setRemoteDebuggerState(v != 'disabled');
-                             });
-  },
-
-  debug: function(str) {
-    dump("AdbController: " + str + "\n");
-  },
-
-  setLockscreenEnabled: function(value) {
-    this.lockEnabled = value;
-    if (this.DEBUG) {
-      this.debug("setLockscreenEnabled = " + this.lockEnabled);
-    }
-    this.updateState();
-  },
-
-  setLockscreenState: function(value) {
-    this.locked = value;
-    if (this.DEBUG) {
-      this.debug("setLockscreenState = " + this.locked);
-    }
-    this.updateState();
-  },
-
-  setRemoteDebuggerState: function(value) {
-    this.remoteDebuggerEnabled = value;
-    if (this.DEBUG) {
-      this.debug("setRemoteDebuggerState = " + this.remoteDebuggerEnabled);
-    }
-    this.updateState();
-  },
-
-  startDisableAdbTimer: function() {
-    if (this.disableAdbTimer) {
-      this.disableAdbTimer.cancel();
-    } else {
-      this.disableAdbTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-      try {
-        this.disableAdbTimeoutHours =
-          Services.prefs.getIntPref("b2g.adb.timeout-hours");
-      } catch (e) {
-        // This happens if the pref doesn't exist, in which case
-        // disableAdbTimeoutHours will still be set to the default.
-      }
-    }
-    if (this.disableAdbTimeoutHours <= 0) {
-      if (this.DEBUG) {
-        this.debug("Timer to disable ADB not started due to zero timeout");
-      }
-      return;
-    }
-
-    if (this.DEBUG) {
-      this.debug("Starting timer to disable ADB in " +
-                 this.disableAdbTimeoutHours + " hours");
-    }
-    let timeoutMilliseconds = this.disableAdbTimeoutHours * 60 * 60 * 1000;
-    this.disableAdbTimer.initWithCallback(this, timeoutMilliseconds,
-                                          Ci.nsITimer.TYPE_ONE_SHOT);
-  },
-
-  stopDisableAdbTimer: function() {
-    if (this.DEBUG) {
-      this.debug("Stopping timer to disable ADB");
-    }
-    if (this.disableAdbTimer) {
-      this.disableAdbTimer.cancel();
-      this.disableAdbTimer = null;
-    }
-  },
-
-  notify: function(aTimer) {
-    if (aTimer == this.disableAdbTimer) {
-      this.disableAdbTimer = null;
-      // The following dump will be the last thing that shows up in logcat,
-      // and will at least give the user a clue about why logcat was
-      // disconnected, if the user happens to be using logcat.
-      dump("AdbController: ADB timer expired - disabling ADB\n");
-      navigator.mozSettings.createLock().set(
-        {'devtools.debugger.remote-enabled': false});
-    }
-  },
-
-  updateState: function() {
-    this.umsActive = false;
-    this.storages = navigator.getDeviceStorages('sdcard');
-    this.updateStorageState(0);
-  },
-
-  updateStorageState: function(storageIndex) {
-    if (storageIndex >= this.storages.length) {
-      // We've iterated through all of the storage objects, now we can
-      // really do updateStateInternal.
-      this.updateStateInternal();
-      return;
-    }
-    let storage = this.storages[storageIndex];
-    if (this.DEBUG) {
-      this.debug("Checking availability of storage: '" +
-                 storage.storageName);
-    }
-
-    let req = storage.available();
-    req.onsuccess = function(e) {
-      if (this.DEBUG) {
-        this.debug("Storage: '" + storage.storageName + "' is '" +
-                   e.target.result);
-      }
-      if (e.target.result == 'shared') {
-        // We've found a storage area that's being shared with the PC.
-        // We can stop looking now.
-        this.umsActive = true;
-        this.updateStateInternal();
-        return;
-      }
-      this.updateStorageState(storageIndex + 1);
-    }.bind(this);
-    req.onerror = function(e) {
-      dump("AdbController: error querying storage availability for '" +
-           this.storages[storageIndex].storageName + "' (ignoring)\n");
-      this.updateStorageState(storageIndex + 1);
-    }.bind(this);
-  },
-
-  updateStateInternal: function() {
-    if (this.DEBUG) {
-      this.debug("updateStateInternal: called");
-    }
-
-    if (this.remoteDebuggerEnabled === undefined ||
-        this.lockEnabled === undefined ||
-        this.locked === undefined) {
-      // Part of initializing the settings database will cause the observers
-      // to trigger. We want to wait until both have been initialized before
-      // we start changing ther adb state. Without this then we can wind up
-      // toggling adb off and back on again (or on and back off again).
-      //
-      // For completeness, one scenario which toggles adb is using the unagi.
-      // The unagi has adb enabled by default (prior to b2g starting). If you
-      // have the phone lock disabled and remote debugging enabled, then we'll
-      // receive an unlock event and an rde event. However at the time we
-      // receive the unlock event we haven't yet received the rde event, so
-      // we turn adb off momentarily, which disconnects a logcat that might
-      // be running. Changing the defaults (in AdbController) just moves the
-      // problem to a different phone, which has adb disabled by default and
-      // we wind up turning on adb for a short period when we shouldn't.
-      //
-      // By waiting until both values are properly initialized, we avoid
-      // turning adb on or off accidentally.
-      if (this.DEBUG) {
-        this.debug("updateState: Waiting for all vars to be initialized");
-      }
-      return;
-    }
-
-    // Check if we have a remote debugging session going on. If so, we won't
-    // disable adb even if the screen is locked.
-    let isDebugging = DebuggerServer.isSocketConnected();
-    if (this.DEBUG) {
-      this.debug("isDebugging=" + isDebugging);
-    }
-
-    // If USB Mass Storage, USB tethering, or a debug session is active,
-    // then we don't want to disable adb in an automatic fashion (i.e.
-    // when the screen locks or due to timeout).
-    let sysUsbConfig = libcutils.property_get("sys.usb.config");
-    let rndisActive = (sysUsbConfig.split(",").indexOf("rndis") >= 0);
-    let usbFuncActive = rndisActive || this.umsActive || isDebugging;
-
-    let enableAdb = this.remoteDebuggerEnabled &&
-      (!(this.lockEnabled && this.locked) || usbFuncActive);
-
-    let useDisableAdbTimer = true;
-    try {
-      if (Services.prefs.getBoolPref("marionette.defaultPrefs.enabled")) {
-        // Marionette is enabled. Marionette requires that adb be on (and also
-        // requires that remote debugging be off). The fact that marionette
-        // is enabled also implies that we're doing a non-production build, so
-        // we want adb enabled all of the time.
-        enableAdb = true;
-        useDisableAdbTimer = false;
-      }
-    } catch (e) {
-      // This means that the pref doesn't exist. Which is fine. We just leave
-      // enableAdb alone.
-    }
-    if (this.DEBUG) {
-      this.debug("updateState: enableAdb = " + enableAdb +
-                 " remoteDebuggerEnabled = " + this.remoteDebuggerEnabled +
-                 " lockEnabled = " + this.lockEnabled +
-                 " locked = " + this.locked +
-                 " usbFuncActive = " + usbFuncActive);
-    }
-
-    // Configure adb.
-    let currentConfig = libcutils.property_get("persist.sys.usb.config");
-    let configFuncs = currentConfig.split(",");
-    let adbIndex = configFuncs.indexOf("adb");
-
-    if (enableAdb) {
-      // Add adb to the list of functions, if not already present
-      if (adbIndex < 0) {
-        configFuncs.push("adb");
-      }
-    } else {
-      // Remove adb from the list of functions, if present
-      if (adbIndex >= 0) {
-        configFuncs.splice(adbIndex, 1);
-      }
-    }
-    let newConfig = configFuncs.join(",");
-    if (newConfig != currentConfig) {
-      if (this.DEBUG) {
-        this.debug("updateState: currentConfig = " + currentConfig);
-        this.debug("updateState:     newConfig = " + newConfig);
-      }
-      try {
-        libcutils.property_set("persist.sys.usb.config", newConfig);
-      } catch(e) {
-        dump("Error configuring adb: " + e);
-      }
-    }
-    if (useDisableAdbTimer) {
-      if (enableAdb && !usbFuncActive) {
-        this.startDisableAdbTimer();
-      } else {
-        this.stopDisableAdbTimer();
-      }
-    }
-  }
-};
-
-adbController.init();
--- a/b2g/chrome/content/desktop.js
+++ b/b2g/chrome/content/desktop.js
@@ -41,12 +41,39 @@ function setupButtons() {
     rotateButton.classList.add('active');
   });
   rotateButton.addEventListener('touchend', function() {
     GlobalSimulatorScreen.flipScreen();
     rotateButton.classList.remove('active');
   });
 }
 
+function checkDebuggerPort() {
+  // XXX: To be removed once bug 942756 lands.
+  // We are hacking 'unix-domain-socket' pref by setting a tcp port (number).
+  // DebuggerServer.openListener detects that it isn't a file path (string),
+  // and starts listening on the tcp port given here as command line argument.
+
+  if (!window.arguments) {
+    return;
+  }
+
+  // Get the command line arguments that were passed to the b2g client
+  let args = window.arguments[0].QueryInterface(Ci.nsICommandLine);
+
+  let dbgport;
+  try {
+    dbgport = args.handleFlagWithParam('start-debugger-server', false);
+  } catch(e) {}
+
+  if (dbgport) {
+    dump('Opening debugger server on ' + dbgport + '\n');
+    Services.prefs.setCharPref('devtools.debugger.unix-domain-socket', dbgport);
+    navigator.mozSettings.createLock().set(
+      {'debugger.remote-mode': 'adb-devtools'});
+  }
+}
+
 window.addEventListener('ContentStart', function() {
   enableTouch();
   setupButtons();
+  checkDebuggerPort();
 });
--- a/b2g/chrome/content/devtools.js
+++ b/b2g/chrome/content/devtools.js
@@ -10,20 +10,16 @@ XPCOMUtils.defineLazyGetter(this, 'devto
   const {devtools} = Cu.import('resource://gre/modules/devtools/Loader.jsm', {});
   return devtools;
 });
 
 XPCOMUtils.defineLazyGetter(this, 'DebuggerClient', function() {
   return Cu.import('resource://gre/modules/devtools/dbg-client.jsm', {}).DebuggerClient;
 });
 
-XPCOMUtils.defineLazyGetter(this, 'DebuggerServer', function() {
-  return Cu.import('resource://gre/modules/devtools/dbg-server.jsm', {}).DebuggerServer;
-});
-
 XPCOMUtils.defineLazyGetter(this, 'WebConsoleUtils', function() {
   return devtools.require('devtools/toolkit/webconsole/utils').Utils;
 });
 
 XPCOMUtils.defineLazyGetter(this, 'EventLoopLagFront', function() {
   return devtools.require('devtools/server/actors/eventlooplag').EventLoopLagFront;
 });
 
@@ -57,17 +53,17 @@ let developerHUD = {
     this._watchers.unshift(watcher);
   },
 
   init: function dwp_init() {
     if (this._client)
       return;
 
     if (!DebuggerServer.initialized) {
-      DebuggerServer.controller.start(null);
+      RemoteDebugger.start();
     }
 
     // We instantiate a local debugger connection so that watchers can use our
     // DebuggerClient to send requests to tab actors (e.g. the consoleActor).
     // Note the special usage of the private _serverConnection, which we need
     // to call connectToChild and set up child process actors on a frame we
     // intend to track. These actors will use the connection to communicate with
     // our DebuggerServer in the parent process.
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -183,16 +183,302 @@ SettingsListener.observe('devtools.overl
     developerHUD.init();
   } else {
     if (developerHUD) {
       developerHUD.uninit();
     }
   }
 });
 
+// =================== Debugger / ADB ====================
+
+#ifdef MOZ_WIDGET_GONK
+let AdbController = {
+  DEBUG: false,
+  locked: undefined,
+  remoteDebuggerEnabled: undefined,
+  lockEnabled: undefined,
+  disableAdbTimer: null,
+  disableAdbTimeoutHours: 12,
+  umsActive: false,
+
+  debug: function(str) {
+    dump("AdbController: " + str + "\n");
+  },
+
+  setLockscreenEnabled: function(value) {
+    this.lockEnabled = value;
+    if (this.DEBUG) {
+      this.debug("setLockscreenEnabled = " + this.lockEnabled);
+    }
+    this.updateState();
+  },
+
+  setLockscreenState: function(value) {
+    this.locked = value;
+    if (this.DEBUG) {
+      this.debug("setLockscreenState = " + this.locked);
+    }
+    this.updateState();
+  },
+
+  setRemoteDebuggerState: function(value) {
+    this.remoteDebuggerEnabled = value;
+    if (this.DEBUG) {
+      this.debug("setRemoteDebuggerState = " + this.remoteDebuggerEnabled);
+    }
+    this.updateState();
+  },
+
+  startDisableAdbTimer: function() {
+    if (this.disableAdbTimer) {
+      this.disableAdbTimer.cancel();
+    } else {
+      this.disableAdbTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+      try {
+        this.disableAdbTimeoutHours =
+          Services.prefs.getIntPref("b2g.adb.timeout-hours");
+      } catch (e) {
+        // This happens if the pref doesn't exist, in which case
+        // disableAdbTimeoutHours will still be set to the default.
+      }
+    }
+    if (this.disableAdbTimeoutHours <= 0) {
+      if (this.DEBUG) {
+        this.debug("Timer to disable ADB not started due to zero timeout");
+      }
+      return;
+    }
+
+    if (this.DEBUG) {
+      this.debug("Starting timer to disable ADB in " +
+                 this.disableAdbTimeoutHours + " hours");
+    }
+    let timeoutMilliseconds = this.disableAdbTimeoutHours * 60 * 60 * 1000;
+    this.disableAdbTimer.initWithCallback(this, timeoutMilliseconds,
+                                          Ci.nsITimer.TYPE_ONE_SHOT);
+  },
+
+  stopDisableAdbTimer: function() {
+    if (this.DEBUG) {
+      this.debug("Stopping timer to disable ADB");
+    }
+    if (this.disableAdbTimer) {
+      this.disableAdbTimer.cancel();
+      this.disableAdbTimer = null;
+    }
+  },
+
+  notify: function(aTimer) {
+    if (aTimer == this.disableAdbTimer) {
+      this.disableAdbTimer = null;
+      // The following dump will be the last thing that shows up in logcat,
+      // and will at least give the user a clue about why logcat was
+      // disconnected, if the user happens to be using logcat.
+      dump("AdbController: ADB timer expired - disabling ADB\n");
+      navigator.mozSettings.createLock().set(
+        {'devtools.debugger.remote-enabled': false});
+    }
+  },
+
+  updateState: function() {
+    this.umsActive = false;
+    this.storages = navigator.getDeviceStorages('sdcard');
+    this.updateStorageState(0);
+  },
+
+  updateStorageState: function(storageIndex) {
+    if (storageIndex >= this.storages.length) {
+      // We've iterated through all of the storage objects, now we can
+      // really do updateStateInternal.
+      this.updateStateInternal();
+      return;
+    }
+    let storage = this.storages[storageIndex];
+    if (this.DEBUG) {
+      this.debug("Checking availability of storage: '" +
+                 storage.storageName);
+    }
+
+    let req = storage.available();
+    req.onsuccess = function(e) {
+      if (this.DEBUG) {
+        this.debug("Storage: '" + storage.storageName + "' is '" +
+                   e.target.result);
+      }
+      if (e.target.result == 'shared') {
+        // We've found a storage area that's being shared with the PC.
+        // We can stop looking now.
+        this.umsActive = true;
+        this.updateStateInternal();
+        return;
+      }
+      this.updateStorageState(storageIndex + 1);
+    }.bind(this);
+    req.onerror = function(e) {
+      dump("AdbController: error querying storage availability for '" +
+           this.storages[storageIndex].storageName + "' (ignoring)\n");
+      this.updateStorageState(storageIndex + 1);
+    }.bind(this);
+  },
+
+  updateStateInternal: function() {
+    if (this.DEBUG) {
+      this.debug("updateStateInternal: called");
+    }
+
+    if (this.remoteDebuggerEnabled === undefined ||
+        this.lockEnabled === undefined ||
+        this.locked === undefined) {
+      // Part of initializing the settings database will cause the observers
+      // to trigger. We want to wait until both have been initialized before
+      // we start changing ther adb state. Without this then we can wind up
+      // toggling adb off and back on again (or on and back off again).
+      //
+      // For completeness, one scenario which toggles adb is using the unagi.
+      // The unagi has adb enabled by default (prior to b2g starting). If you
+      // have the phone lock disabled and remote debugging enabled, then we'll
+      // receive an unlock event and an rde event. However at the time we
+      // receive the unlock event we haven't yet received the rde event, so
+      // we turn adb off momentarily, which disconnects a logcat that might
+      // be running. Changing the defaults (in AdbController) just moves the
+      // problem to a different phone, which has adb disabled by default and
+      // we wind up turning on adb for a short period when we shouldn't.
+      //
+      // By waiting until both values are properly initialized, we avoid
+      // turning adb on or off accidentally.
+      if (this.DEBUG) {
+        this.debug("updateState: Waiting for all vars to be initialized");
+      }
+      return;
+    }
+
+    // Check if we have a remote debugging session going on. If so, we won't
+    // disable adb even if the screen is locked.
+    let isDebugging = RemoteDebugger.isDebugging;
+    if (this.DEBUG) {
+      this.debug("isDebugging=" + isDebugging);
+    }
+
+    // If USB Mass Storage, USB tethering, or a debug session is active,
+    // then we don't want to disable adb in an automatic fashion (i.e.
+    // when the screen locks or due to timeout).
+    let sysUsbConfig = libcutils.property_get("sys.usb.config");
+    let rndisActive = (sysUsbConfig.split(",").indexOf("rndis") >= 0);
+    let usbFuncActive = rndisActive || this.umsActive || isDebugging;
+
+    let enableAdb = this.remoteDebuggerEnabled &&
+      (!(this.lockEnabled && this.locked) || usbFuncActive);
+
+    let useDisableAdbTimer = true;
+    try {
+      if (Services.prefs.getBoolPref("marionette.defaultPrefs.enabled")) {
+        // Marionette is enabled. Marionette requires that adb be on (and also
+        // requires that remote debugging be off). The fact that marionette
+        // is enabled also implies that we're doing a non-production build, so
+        // we want adb enabled all of the time.
+        enableAdb = true;
+        useDisableAdbTimer = false;
+      }
+    } catch (e) {
+      // This means that the pref doesn't exist. Which is fine. We just leave
+      // enableAdb alone.
+    }
+    if (this.DEBUG) {
+      this.debug("updateState: enableAdb = " + enableAdb +
+                 " remoteDebuggerEnabled = " + this.remoteDebuggerEnabled +
+                 " lockEnabled = " + this.lockEnabled +
+                 " locked = " + this.locked +
+                 " usbFuncActive = " + usbFuncActive);
+    }
+
+    // Configure adb.
+    let currentConfig = libcutils.property_get("persist.sys.usb.config");
+    let configFuncs = currentConfig.split(",");
+    let adbIndex = configFuncs.indexOf("adb");
+
+    if (enableAdb) {
+      // Add adb to the list of functions, if not already present
+      if (adbIndex < 0) {
+        configFuncs.push("adb");
+      }
+    } else {
+      // Remove adb from the list of functions, if present
+      if (adbIndex >= 0) {
+        configFuncs.splice(adbIndex, 1);
+      }
+    }
+    let newConfig = configFuncs.join(",");
+    if (newConfig != currentConfig) {
+      if (this.DEBUG) {
+        this.debug("updateState: currentConfig = " + currentConfig);
+        this.debug("updateState:     newConfig = " + newConfig);
+      }
+      try {
+        libcutils.property_set("persist.sys.usb.config", newConfig);
+      } catch(e) {
+        dump("Error configuring adb: " + e);
+      }
+    }
+    if (useDisableAdbTimer) {
+      if (enableAdb && !usbFuncActive) {
+        this.startDisableAdbTimer();
+      } else {
+        this.stopDisableAdbTimer();
+      }
+    }
+  }
+};
+
+SettingsListener.observe("lockscreen.locked", false,
+                         AdbController.setLockscreenState.bind(AdbController));
+SettingsListener.observe("lockscreen.enabled", false,
+                         AdbController.setLockscreenEnabled.bind(AdbController));
+#endif
+
+// Keep the old setting to not break people that won't have updated
+// gaia and gecko.
+SettingsListener.observe('devtools.debugger.remote-enabled', false, function(value) {
+  Services.prefs.setBoolPref('devtools.debugger.remote-enabled', value);
+  // This preference is consulted during startup
+  Services.prefs.savePrefFile(null);
+  try {
+    value ? RemoteDebugger.start() : RemoteDebugger.stop();
+  } catch(e) {
+    dump("Error while initializing devtools: " + e + "\n" + e.stack + "\n");
+  }
+
+#ifdef MOZ_WIDGET_GONK
+  AdbController.setRemoteDebuggerState(value);
+#endif
+});
+
+SettingsListener.observe('debugger.remote-mode', false, function(value) {
+  if (['disabled', 'adb-only', 'adb-devtools'].indexOf(value) == -1) {
+    dump('Illegal value for debugger.remote-mode: ' + value + '\n');
+    return;
+  }
+
+  Services.prefs.setBoolPref('devtools.debugger.remote-enabled',
+                             value == 'adb-devtools');
+  // This preference is consulted during startup
+  Services.prefs.savePrefFile(null);
+
+  try {
+    (value == 'adb-devtools') ? RemoteDebugger.start()
+                              : RemoteDebugger.stop();
+  } catch(e) {
+    dump("Error while initializing devtools: " + e + "\n" + e.stack + "\n");
+  }
+
+#ifdef MOZ_WIDGET_GONK
+  AdbController.setRemoteDebuggerState(value != 'disabled');
+#endif
+});
+
 // =================== Device Storage ====================
 SettingsListener.observe('device.storage.writable.name', 'sdcard', function(value) {
   if (Services.prefs.getPrefType('device.storage.writable.name') != Ci.nsIPrefBranch.PREF_STRING) {
     // We clear the pref because it used to be erroneously written as a bool
     // and we need to clear it before we can change it to have the correct type.
     Services.prefs.clearUserPref('device.storage.writable.name');
   }
   Services.prefs.setCharPref('device.storage.writable.name', value);
--- a/b2g/chrome/content/shell.html
+++ b/b2g/chrome/content/shell.html
@@ -27,19 +27,16 @@
   <script type="application/javascript;version=1.8"
           src="chrome://b2g/content/desktop.js"> </script>
   <!-- this script handles the screen argument for desktop builds -->
   <script type="application/javascript;version=1.8"
           src="chrome://b2g/content/screen.js"> </script>
   <!-- this script handles the "runapp" argument for desktop builds -->
   <script type="application/javascript;version=1.8"
           src="chrome://b2g/content/runapp.js"> </script>
-#else
-  <script type="application/javascript;version=1.8"
-          src="chrome://b2g/content/adbController.js"> </script>
 #endif
 </head>
   <body id="container">
 #ifdef FXOS_SIMULATOR
     <!--
      Some additional buttons are displayed on desktop to fake hardware buttons.
     -->
     <footer id="controls">
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -47,16 +47,21 @@ XPCOMUtils.defineLazyServiceGetter(Servi
 XPCOMUtils.defineLazyServiceGetter(this, 'gSystemMessenger',
                                    '@mozilla.org/system-message-internal;1',
                                    'nsISystemMessagesInternal');
 
 XPCOMUtils.defineLazyServiceGetter(Services, 'fm',
                                    '@mozilla.org/focus-manager;1',
                                    'nsIFocusManager');
 
+XPCOMUtils.defineLazyGetter(this, 'DebuggerServer', function() {
+  Cu.import('resource://gre/modules/devtools/dbg-server.jsm');
+  return DebuggerServer;
+});
+
 XPCOMUtils.defineLazyGetter(this, "ppmm", function() {
   return Cc["@mozilla.org/parentprocessmessagemanager;1"]
          .getService(Ci.nsIMessageListenerManager);
 });
 
 #ifdef MOZ_WIDGET_GONK
 XPCOMUtils.defineLazyGetter(this, "libcutils", function () {
   Cu.import("resource://gre/modules/systemlibs.js");
@@ -757,16 +762,19 @@ var CustomEventManager = {
         WebappsHelper.handleEvent(detail);
         break;
       case 'select-choicechange':
         FormsHelper.handleEvent(detail);
         break;
       case 'system-message-listener-ready':
         Services.obs.notifyObservers(null, 'system-message-listener-ready', null);
         break;
+      case 'remote-debugger-prompt':
+        RemoteDebugger.handleEvent(detail);
+        break;
       case 'captive-portal-login-cancel':
         CaptivePortalLoginHelper.handleEvent(detail);
         break;
       case 'inter-app-comm-permission':
         Services.obs.notifyObservers(null, 'inter-app-comm-select-app-result',
           JSON.stringify({ callerID: detail.chromeEventID,
                            keyword: detail.keyword,
                            manifestURL: detail.manifestURL,
@@ -1064,16 +1072,141 @@ let IndexedDBPromptHelper = {
 
     setTimeout(function() {
       observer.observe(null, responseTopic,
                        Ci.nsIPermissionManager.DENY_ACTION);
     }, 0);
   }
 }
 
+let RemoteDebugger = {
+  _promptDone: false,
+  _promptAnswer: false,
+  _running: false,
+
+  prompt: function debugger_prompt() {
+    this._promptDone = false;
+
+    shell.sendChromeEvent({
+      "type": "remote-debugger-prompt"
+    });
+
+    while(!this._promptDone) {
+      Services.tm.currentThread.processNextEvent(true);
+    }
+
+    return this._promptAnswer;
+  },
+
+  handleEvent: function debugger_handleEvent(detail) {
+    this._promptAnswer = detail.value;
+    this._promptDone = true;
+  },
+
+  get isDebugging() {
+    if (!this._running) {
+      return false;
+    }
+
+    return DebuggerServer._connections &&
+           Object.keys(DebuggerServer._connections).length > 0;
+  },
+
+  // Start the debugger server.
+  start: function debugger_start() {
+    if (this._running) {
+      return;
+    }
+
+    if (!DebuggerServer.initialized) {
+      // Ask for remote connections.
+      DebuggerServer.init(this.prompt.bind(this));
+
+      // /!\ Be careful when adding a new actor, especially global actors.
+      // Any new global actor will be exposed and returned by the root actor.
+
+      // Add Firefox-specific actors, but prevent tab actors to be loaded in
+      // the parent process, unless we enable certified apps debugging.
+      let restrictPrivileges = Services.prefs.getBoolPref("devtools.debugger.forbid-certified-apps");
+      DebuggerServer.addBrowserActors("navigator:browser", restrictPrivileges);
+
+      /**
+       * Construct a root actor appropriate for use in a server running in B2G.
+       * The returned root actor respects the factories registered with
+       * DebuggerServer.addGlobalActor only if certified apps debugging is on,
+       * otherwise we used an explicit limited list of global actors
+       *
+       * * @param connection DebuggerServerConnection
+       *        The conection to the client.
+       */
+      DebuggerServer.createRootActor = function createRootActor(connection)
+      {
+        let { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
+        let parameters = {
+          // We do not expose browser tab actors yet,
+          // but we still have to define tabList.getList(),
+          // otherwise, client won't be able to fetch global actors
+          // from listTabs request!
+          tabList: {
+            getList: function() {
+              return promise.resolve([]);
+            }
+          },
+          // Use an explicit global actor list to prevent exposing
+          // unexpected actors
+          globalActorFactories: restrictPrivileges ? {
+            webappsActor: DebuggerServer.globalActorFactories.webappsActor,
+            deviceActor: DebuggerServer.globalActorFactories.deviceActor,
+          } : DebuggerServer.globalActorFactories
+        };
+        let root = new DebuggerServer.RootActor(connection, parameters);
+        root.applicationType = "operating-system";
+        return root;
+      };
+
+#ifdef MOZ_WIDGET_GONK
+      DebuggerServer.on("connectionchange", function() {
+        AdbController.updateState();
+      });
+#endif
+    }
+
+    let path = Services.prefs.getCharPref("devtools.debugger.unix-domain-socket") ||
+               "/data/local/debugger-socket";
+    try {
+      DebuggerServer.openListener(path);
+      // Temporary event, until bug 942756 lands and offers a way to know
+      // when the server is up and running.
+      Services.obs.notifyObservers(null, 'debugger-server-started', null);
+      this._running = true;
+    } catch (e) {
+      dump('Unable to start debugger server: ' + e + '\n');
+    }
+  },
+
+  stop: function debugger_stop() {
+    if (!this._running) {
+      return;
+    }
+
+    if (!DebuggerServer.initialized) {
+      // Can this really happen if we are running?
+      this._running = false;
+      return;
+    }
+
+    try {
+      DebuggerServer.closeListener();
+    } catch (e) {
+      dump('Unable to stop debugger server: ' + e + '\n');
+    }
+    this._running = false;
+  }
+}
+
 let KeyboardHelper = {
   handleEvent: function keyboard_handleEvent(detail) {
     Keyboard.setLayouts(detail.layouts);
   }
 };
 
 // This is the backend for Gaia's screenshot feature.  Gaia requests a
 // screenshot by sending a mozContentEvent with detail.type set to
--- a/b2g/chrome/jar.mn
+++ b/b2g/chrome/jar.mn
@@ -20,19 +20,16 @@ chrome.jar:
   content/images/desktop/home-white.png (content/images/desktop/home-white.png)
   content/images/desktop/rotate.png     (content/images/desktop/rotate.png)
 #endif
 #ifndef MOZ_WIDGET_GONK
   content/desktop.js                    (content/desktop.js)
   content/screen.js                     (content/screen.js)
   content/runapp.js                     (content/runapp.js)
 #endif
-#ifdef MOZ_WIDGET_GONK
-  content/adbController.js              (content/adbController.js)
-#endif
 * content/content.css                   (content/content.css)
   content/touchcontrols.css             (content/touchcontrols.css)
 
 * content/payment.js                    (content/payment.js)
   content/identity.js                   (content/identity.js)
 
 % override chrome://global/skin/media/videocontrols.css chrome://b2g/content/touchcontrols.css
 % override chrome://global/content/aboutCertError.xhtml chrome://b2g/content/aboutCertError.xhtml
--- a/b2g/components/B2GComponents.manifest
+++ b/b2g/components/B2GComponents.manifest
@@ -82,12 +82,8 @@ contract @mozilla.org/fxaccounts/fxaccou
 component {710322af-e6ae-4b0c-b2c9-1474a87b077e} HelperAppDialog.js
 contract @mozilla.org/helperapplauncherdialog;1 {710322af-e6ae-4b0c-b2c9-1474a87b077e}
 
 #ifndef MOZ_WIDGET_GONK
 component {c83c02c0-5d43-4e3e-987f-9173b313e880} SimulatorScreen.js
 contract @mozilla.org/simulator-screen;1 {c83c02c0-5d43-4e3e-987f-9173b313e880}
 category profile-after-change SimulatorScreen @mozilla.org/simulator-screen;1
 #endif
-
-# DebuggerServerController.js
-component {9390f6ac-7914-46c6-b9d0-ccc7db550d8c} DebuggerServerController.js
-contract @mozilla.org/devtools/DebuggerServerController;1 {9390f6ac-7914-46c6-b9d0-ccc7db550d8c}
deleted file mode 100644
--- a/b2g/components/DebuggerServerController.js
+++ /dev/null
@@ -1,195 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-
-XPCOMUtils.defineLazyServiceGetter(this,
-    "Settings",
-    "@mozilla.org/settingsService;1", "nsISettingsService");
-
-XPCOMUtils.defineLazyModuleGetter(this,
-     "SystemAppProxy",
-     "resource://gre/modules/SystemAppProxy.jsm");
-
-function DebuggerServerController() {
-}
-
-DebuggerServerController.prototype = {
-  classID: Components.ID("{9390f6ac-7914-46c6-b9d0-ccc7db550d8c}"),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDebuggerServerController, Ci.nsIObserver]),
-
-  init: function init(debuggerServer) {
-    this.debugger = debuggerServer;
-    Services.obs.addObserver(this, "mozsettings-changed", false);
-    Services.obs.addObserver(this, "debugger-server-started", false);
-    Services.obs.addObserver(this, "debugger-server-stopped", false);
-    Services.obs.addObserver(this, "xpcom-shutdown", false);
-  },
-
-  uninit: function uninit() {
-    this.debugger = null;
-    Services.obs.removeObserver(this, "mozsettings-changed");
-    Services.obs.removeObserver(this, "debugger-server-started");
-    Services.obs.removeObserver(this, "debugger-server-stopped");
-    Services.obs.removeObserver(this, "xpcom-shutdown");
-  },
-
-  // nsIObserver
-
-  observe: function observe(subject, topic, data) {
-    switch (topic) {
-      case "xpcom-shutdown":
-        this.uninit();
-        break;
-      case "debugger-server-started":
-        this._onDebuggerStarted(data);
-        break;
-      case "debugger-server-stopped":
-        this._onDebuggerStopped();
-        break;
-      case "mozsettings-changed":
-        let {key, value} = JSON.parse(data);
-        switch(key) {
-          case "debugger.remote-mode":
-            if (["disabled", "adb-only", "adb-devtools"].indexOf(value) == -1) {
-              dump("Illegal value for debugger.remote-mode: " + value + "\n");
-              return;
-            }
-
-            Services.prefs.setBoolPref("devtools.debugger.remote-enabled", value == "adb-devtools");
-            Services.prefs.savePrefFile(null);
-
-            if (value != "adb-devtools") {
-              // The *pref* "devtools.debugger.remote-enabled" has been set to false (setBoolPref)
-              // above. In theory, it's supposed to automatically stop the debugger, but maybe the
-              // debugger has been started from the command line, so the value was already false,
-              // so the observer is not called because the value didn't change. We need to stop
-              // the debugger manually:
-              this.stop();
-            }
-        }
-    }
-
-  },
-
-  // nsIDebuggerController
-
-  start: function(portOrPath) {
-
-    if (!this.debugger.initialized) {
-      // Ask for remote connections.
-      this.debugger.init(Prompt.prompt.bind(Prompt));
-
-      // /!\ Be careful when adding a new actor, especially global actors.
-      // Any new global actor will be exposed and returned by the root actor.
-
-      // Add Firefox-specific actors, but prevent tab actors to be loaded in
-      // the parent process, unless we enable certified apps debugging.
-      let restrictPrivileges = Services.prefs.getBoolPref("devtools.debugger.forbid-certified-apps");
-      this.debugger.addBrowserActors("navigator:browser", restrictPrivileges);
-
-      /**
-       * Construct a root actor appropriate for use in a server running in B2G.
-       * The returned root actor respects the factories registered with
-       * DebuggerServer.addGlobalActor only if certified apps debugging is on,
-       * otherwise we used an explicit limited list of global actors
-       *
-       * * @param connection DebuggerServerConnection
-       *        The conection to the client.
-       */
-      let debuggerServer = this.debugger;
-      debuggerServer.createRootActor = function createRootActor(connection)
-      {
-        let { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
-        let parameters = {
-          // We do not expose browser tab actors yet,
-          // but we still have to define tabList.getList(),
-          // otherwise, client won't be able to fetch global actors
-          // from listTabs request!
-          tabList: {
-            getList: function() {
-              return promise.resolve([]);
-            }
-          },
-          // Use an explicit global actor list to prevent exposing
-          // unexpected actors
-          globalActorFactories: restrictPrivileges ? {
-            webappsActor: debuggerServer.globalActorFactories.webappsActor,
-            deviceActor: debuggerServer.globalActorFactories.deviceActor,
-          } : debuggerServer.globalActorFactories
-        };
-        let root = new debuggerServer.RootActor(connection, parameters);
-        root.applicationType = "operating-system";
-        return root;
-      };
-
-    }
-
-    if (portOrPath) {
-      try {
-        this.debugger.openListener(portOrPath);
-      } catch (e) {
-        dump("Unable to start debugger server (" + portOrPath + "): " + e + "\n");
-      }
-    }
-
-  },
-
-  stop: function() {
-    this.debugger.destroy();
-  },
-
-  _onDebuggerStarted: function(portOrPath) {
-    dump("Devtools debugger server started: " + portOrPath + "\n");
-    Settings.createLock().set("debugger.remote-mode", "adb-devtools", null);
-  },
-
-
-  _onDebuggerStopped: function() {
-    dump("Devtools debugger server stopped\n");
-  },
-};
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DebuggerServerController]);
-
-// =================== Prompt ====================
-
-let Prompt = {
-  _promptDone: false,
-  _promptAnswer: false,
-  _listenerAttached: false,
-
-  prompt: function () {
-    if (!this._listenerAttached) {
-      SystemAppProxy.addEventListener("mozContentEvent", this, false, true);
-      this._listenerAttached = true;
-    }
-
-    this._promptDone = false;
-
-    SystemAppProxy._sendCustomEvent("mozChromeEvent", {
-      "type": "remote-debugger-prompt"
-    });
-
-
-    while(!this._promptDone) {
-      Services.tm.currentThread.processNextEvent(true);
-    }
-
-    return this._promptAnswer;
-  },
-
-  // Content events listener
-
-  handleEvent: function (event) {
-    if (event.detail.type == "remote-debugger-prompt") {
-      this._promptAnswer = event.detail.value;
-      this._promptDone = true;
-    }
-  }
-}
--- a/b2g/components/moz.build
+++ b/b2g/components/moz.build
@@ -7,17 +7,16 @@
 DIRS += ['test']
 
 EXTRA_COMPONENTS += [
     'ActivitiesGlue.js',
     'AlertsService.js',
     'B2GAboutRedirector.js',
     'ContentHandler.js',
     'ContentPermissionPrompt.js',
-    'DebuggerServerController.js',
     'FilePicker.js',
     'HelperAppDialog.js',
     'MailtoProtocolHandler.js',
     'PaymentGlue.js',
     'ProcessGlobal.js',
     'SmsProtocolHandler.js',
     'TelProtocolHandler.js',
     'WebappsUpdateTimer.js',
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -429,21 +429,16 @@
 #ifndef MOZ_WIDGET_GONK
 @BINPATH@/components/extensions.manifest
 @BINPATH@/components/addonManager.js
 @BINPATH@/components/amContentHandler.js
 @BINPATH@/components/amWebInstallListener.js
 @BINPATH@/components/nsBlocklistService.js
 #endif
 
-; DevTools
-@BINPATH@/components/DevToolsComponents.manifest
-@BINPATH@/components/DevToolsAppStartup.js
-@BINPATH@/components/DebuggerServerController.js
-
 #ifdef MOZ_UPDATER
 @BINPATH@/components/nsUpdateService.manifest
 @BINPATH@/components/nsUpdateService.js
 @BINPATH@/components/nsUpdateServiceStub.js
 #endif
 @BINPATH@/components/nsUpdateTimerManager.manifest
 @BINPATH@/components/nsUpdateTimerManager.js
 @BINPATH@/components/pluginGlue.manifest
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1261,17 +1261,16 @@ pref("devtools.responsiveUI.no-reload-no
 
 // Enable the Debugger
 pref("devtools.debugger.enabled", true);
 pref("devtools.debugger.chrome-enabled", true);
 pref("devtools.debugger.chrome-debugging-host", "localhost");
 pref("devtools.debugger.chrome-debugging-port", 6080);
 pref("devtools.debugger.remote-host", "localhost");
 pref("devtools.debugger.remote-timeout", 20000);
-pref("devtools.debugger.remote-enabled-pref-migrated", false);
 pref("devtools.debugger.pause-on-exceptions", false);
 pref("devtools.debugger.ignore-caught-exceptions", true);
 pref("devtools.debugger.source-maps-enabled", true);
 pref("devtools.debugger.pretty-print-enabled", true);
 pref("devtools.debugger.auto-pretty-print", false);
 pref("devtools.debugger.tracer", false);
 
 // The default Debugger UI settings
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -100,17 +100,17 @@
     <command id="Tools:DevAppMgr" oncommand="gDevToolsBrowser.openAppManager(gBrowser);" disabled="true" hidden="true"/>
     <command id="Tools:BrowserToolbox" oncommand="BrowserToolboxProcess.init();" disabled="true" hidden="true"/>
     <command id="Tools:BrowserConsole" oncommand="HUDService.toggleBrowserConsole();"/>
     <command id="Tools:Scratchpad" oncommand="Scratchpad.openScratchpad();"/>
     <command id="Tools:ResponsiveUI" oncommand="ResponsiveUI.toggle();"/>
     <command id="Tools:Eyedropper" oncommand="openEyedropper();"/>
     <command id="Tools:Addons" oncommand="BrowserOpenAddonsMgr();"/>
     <command id="Tools:ErrorConsole" oncommand="toJavaScriptConsole()" disabled="true" hidden="true"/>
-    <command id="Tools:DevToolsConnect" oncommand="gDevToolsBrowser.openConnectScreen(gBrowser)"/>
+    <command id="Tools:DevToolsConnect" oncommand="gDevToolsBrowser.openConnectScreen(gBrowser)" disabled="true" hidden="true"/>
     <command id="Tools:Sanitize"
      oncommand="Cc['@mozilla.org/browser/browserglue;1'].getService(Ci.nsIBrowserGlue).sanitize(window);"/>
     <command id="Tools:PrivateBrowsing"
       oncommand="OpenBrowserWindow({private: true});"/>
     <command id="Tools:RemoteWindow"
       oncommand="OpenBrowserWindow({remote: true});"/>
     <command id="Tools:NonRemoteWindow"
       oncommand="OpenBrowserWindow({remote: false});"/>
deleted file mode 100644
--- a/browser/devtools/components/CommandLineHandler.js
+++ /dev/null
@@ -1,76 +0,0 @@
-/* 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/. */
-
-const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
-const kDebuggerPrefs = [
-  "devtools.debugger.chrome-enabled",
-  "devtools.chrome.enabled"
-];
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
-
-function devtoolsCommandlineHandler() {
-}
-devtoolsCommandlineHandler.prototype = {
-  handle: function(cmdLine) {
-    let consoleFlag = cmdLine.handleFlag("jsconsole", false);
-    let debuggerFlag = cmdLine.handleFlag("jsdebugger", false);
-    if (consoleFlag) {
-      this.handleConsoleFlag(cmdLine);
-    }
-    if (debuggerFlag) {
-      this.handleDebuggerFlag(cmdLine);
-    }
-  },
-
-  handleConsoleFlag: function(cmdLine) {
-    let window = Services.wm.getMostRecentWindow("devtools:webconsole");
-    if (!window) {
-      let devtools = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
-      // Load the browser devtools main module as the loader's main module.
-      Cu.import("resource:///modules/devtools/gDevTools.jsm");
-      let hudservice = devtools.require("devtools/webconsole/hudservice");
-      let console = Cu.import("resource://gre/modules/devtools/Console.jsm", {}).console;
-      hudservice.toggleBrowserConsole().then(null, console.error);
-    } else {
-      window.focus(); // the Browser Console was already open
-    }
-
-    if (cmdLine.state == Ci.nsICommandLine.STATE_REMOTE_AUTO) {
-      cmdLine.preventDefault = true;
-    }
-  },
-
-  handleDebuggerFlag: function(cmdLine) {
-    let remoteDebuggingEnabled = false;
-    try {
-      remoteDebuggingEnabled = kDebuggerPrefs.every((pref) => Services.prefs.getBoolPref(pref));
-    } catch (ex) {
-      Cu.reportError(ex);
-      return;
-    }
-    if (remoteDebuggingEnabled) {
-      Cu.import("resource:///modules/devtools/ToolboxProcess.jsm");
-      BrowserToolboxProcess.init();
-    } else {
-      let errorMsg = "Could not run chrome debugger! You need the following prefs " +
-                     "to be set to true: " + kDebuggerPrefs.join(", ");
-      Cu.reportError(errorMsg);
-      // Dump as well, as we're doing this from a commandline, make sure people don't miss it:
-      dump(errorMsg + "\n");
-    }
-
-    if (cmdLine.state == Ci.nsICommandLine.STATE_REMOTE_AUTO) {
-      cmdLine.preventDefault = true;
-    }
-  },
-
-  helpInfo : "  -jsconsole         Open the Browser Console.\n" +
-             "  -jsdebugger        Open the Browser Toolbox.\n",
-
-  classID: Components.ID("{9e9a9283-0ce9-4e4a-8f1c-ba129a032c32}"),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler]),
-};
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([devtoolsCommandlineHandler]);
deleted file mode 100644
--- a/browser/devtools/components/DebuggerServerController.js
+++ /dev/null
@@ -1,123 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-const { interfaces: Ci, utils: Cu, results: Cr } = Components;
-
-const gPrefRemoteEnabled = "devtools.debugger.remote-enabled";
-const gPrefMigrated = "devtools.debugger.remote-enabled-pref-migrated";
-const gPrefShowNotifications = "devtools.debugger.show-server-notifications";
-
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-
-
-XPCOMUtils.defineLazyServiceGetter(this,
-    "Alerts",
-    "@mozilla.org/alerts-service;1", "nsIAlertsService");
-
-XPCOMUtils.defineLazyGetter(this,
-    "l10n",
-    () => Services.strings.createBundle("chrome://global/locale/devtools/debugger.properties"));
-
-function DebuggerServerController() {
-}
-
-DebuggerServerController.prototype = {
-  classID: Components.ID("{f6e8e269-ae4a-4c4a-bf80-fb4164fb072d}"),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDebuggerServerController, Ci.nsIObserver]),
-
-  init: function(debuggerServer) {
-
-    this.debugger = debuggerServer;
-
-    // The remote-enabled pref used to mean that Firefox was allowed
-    // to connect to a debugger server. In other products (b2g, thunderbird,
-    // fennec, metro and webrt) this pref had a different meaning: it runs a
-    // debugger server. We want Firefox Desktop to follow the same rule.
-    //
-    // We don't want to surprise users with this new behavior. So we reset
-    // the remote-enabled pref once.
-
-    if (!Services.prefs.getBoolPref(gPrefMigrated)) {
-      Services.prefs.clearUserPref(gPrefRemoteEnabled);
-      Services.prefs.setBoolPref(gPrefMigrated, true);
-    }
-
-    Services.obs.addObserver(this, "debugger-server-started", false);
-    Services.obs.addObserver(this, "debugger-server-stopped", false);
-    Services.obs.addObserver(this, "xpcom-shutdown", false);
-  },
-
-  uninit: function() {
-    this.debugger = null;
-    Services.obs.removeObserver(this, "debugger-server-started");
-    Services.obs.removeObserver(this, "debugger-server-stopped");
-    Services.obs.removeObserver(this, "xpcom-shutdown");
-  },
-
-  start: function(pathOrPort) {
-    if (!this.debugger.initialized) {
-      this.debugger.init();
-      this.debugger.addBrowserActors();
-    }
-
-    if (!pathOrPort) {
-      // If the "devtools.debugger.unix-domain-socket" pref is set, we use a unix socket.
-      // If not, we use a regular TCP socket.
-      try {
-        pathOrPort = Services.prefs.getCharPref("devtools.debugger.unix-domain-socket");
-      } catch (e) {
-        pathOrPort = Services.prefs.getIntPref("devtools.debugger.remote-port");
-      }
-    }
-
-    this._showNotifications = Services.prefs.getBoolPref(gPrefShowNotifications);
-
-    try {
-      this.debugger.openListener(pathOrPort);
-    } catch (e if e.result != Cr.NS_ERROR_NOT_AVAILABLE) {
-      dump("Unable to start debugger server (" + pathOrPort + "): " + e + "\n");
-    }
-  },
-
-  stop: function() {
-    this.debugger.closeListener(true);
-  },
-
-  // nsIObserver
-
-  observe: function (subject, topic, data) {
-    if (topic == "xpcom-shutdown")
-      this.uninit();
-    if (topic == "debugger-server-started")
-      this._onDebuggerStarted(data);
-    if (topic == "debugger-server-stopped")
-      this._onDebuggerStopped();
-  },
-
-  _onDebuggerStarted: function(portOrPath) {
-    if (!this._showNotifications)
-      return;
-    let title = l10n.GetStringFromName("debuggerStartedAlert.title");
-    let port = Number(portOrPath);
-    let detail;
-    if (port) {
-      detail = l10n.formatStringFromName("debuggerStartedAlert.detailPort", [portOrPath], 1);
-    } else {
-      detail = l10n.formatStringFromName("debuggerStartedAlert.detailPath", [portOrPath], 1);
-    }
-    Alerts.showAlertNotification(null, title, detail, false, "", function(){});
-  },
-
-  _onDebuggerStopped: function() {
-    if (!this._showNotifications)
-      return;
-    let title = l10n.GetStringFromName("debuggerStopped.title");
-    Alerts.showAlertNotification(null, title, null, false, "", function(){});
-  },
-};
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DebuggerServerController]);
deleted file mode 100644
--- a/browser/devtools/components/DevToolsComponents.manifest
+++ /dev/null
@@ -1,7 +0,0 @@
-# CommandLineHandler.js
-component {9e9a9283-0ce9-4e4a-8f1c-ba129a032c32} CommandLineHandler.js
-contract @mozilla.org/toolkit/console-clh;1 {9e9a9283-0ce9-4e4a-8f1c-ba129a032c32}
-
-# DebuggerServerController.js
-component {f6e8e269-ae4a-4c4a-bf80-fb4164fb072d} DebuggerServerController.js
-contract @mozilla.org/devtools/DebuggerServerController;1 {f6e8e269-ae4a-4c4a-bf80-fb4164fb072d}
deleted file mode 100644
--- a/browser/devtools/components/moz.build
+++ /dev/null
@@ -1,11 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-EXTRA_COMPONENTS += [
-    'CommandLineHandler.js',
-    'DebuggerServerController.js',
-    'DevToolsComponents.manifest',
-]
--- a/browser/devtools/debugger/test/head.js
+++ b/browser/devtools/debugger/test/head.js
@@ -7,19 +7,16 @@ const { classes: Cc, interfaces: Ci, uti
 let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
 
 // Disable logging for faster test runs. Set this pref to true if you want to
 // debug a test in your try runs. Both the debugger server and frontend will
 // be affected by this pref.
 let gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log");
 Services.prefs.setBoolPref("devtools.debugger.log", false);
 
-// Disable notifications (to avoid "unknown window" errors)
-Services.prefs.setBoolPref("devtools.debugger.show-server-notifications", false);
-
 let { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
 let { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
 let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
 let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 let { require } = devtools;
 let { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
 let { BrowserToolboxProcess } = Cu.import("resource:///modules/devtools/ToolboxProcess.jsm", {});
 let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
@@ -37,17 +34,16 @@ SimpleTest.registerCleanupFunction(() =>
 });
 
 // All tests are asynchronous.
 waitForExplicitFinish();
 
 registerCleanupFunction(function() {
   info("finish() was called, cleaning up...");
   Services.prefs.setBoolPref("devtools.debugger.log", gEnableLogging);
-  Services.prefs.clearUserPref("devtools.debugger.show-server-notifications");
 
   // Properly shut down the server to avoid memory leaks.
   DebuggerServer.destroy();
 
   // Debugger tests use a lot of memory, so force a GC to help fragmentation.
   info("Forcing GC after debugger test.");
   Cu.forceGC();
 });
new file mode 100644
--- /dev/null
+++ b/browser/devtools/devtools-clhandler.js
@@ -0,0 +1,77 @@
+/* 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/. */
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+const kDebuggerPrefs = [
+  "devtools.debugger.remote-enabled",
+  "devtools.debugger.chrome-enabled",
+  "devtools.chrome.enabled"
+];
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
+
+function devtoolsCommandlineHandler() {
+}
+devtoolsCommandlineHandler.prototype = {
+  handle: function(cmdLine) {
+    let consoleFlag = cmdLine.handleFlag("jsconsole", false);
+    let debuggerFlag = cmdLine.handleFlag("jsdebugger", false);
+    if (consoleFlag) {
+      this.handleConsoleFlag(cmdLine);
+    }
+    if (debuggerFlag) {
+      this.handleDebuggerFlag(cmdLine);
+    }
+  },
+
+  handleConsoleFlag: function(cmdLine) {
+    let window = Services.wm.getMostRecentWindow("devtools:webconsole");
+    if (!window) {
+      let devtools = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
+      // Load the browser devtools main module as the loader's main module.
+      Cu.import("resource:///modules/devtools/gDevTools.jsm");
+      let hudservice = devtools.require("devtools/webconsole/hudservice");
+      let console = Cu.import("resource://gre/modules/devtools/Console.jsm", {}).console;
+      hudservice.toggleBrowserConsole().then(null, console.error);
+    } else {
+      window.focus(); // the Browser Console was already open
+    }
+
+    if (cmdLine.state == Ci.nsICommandLine.STATE_REMOTE_AUTO) {
+      cmdLine.preventDefault = true;
+    }
+  },
+
+  handleDebuggerFlag: function(cmdLine) {
+    let remoteDebuggingEnabled = false;
+    try {
+      remoteDebuggingEnabled = kDebuggerPrefs.every((pref) => Services.prefs.getBoolPref(pref));
+    } catch (ex) {
+      Cu.reportError(ex);
+      return;
+    }
+    if (remoteDebuggingEnabled) {
+      Cu.import("resource:///modules/devtools/ToolboxProcess.jsm");
+      BrowserToolboxProcess.init();
+    } else {
+      let errorMsg = "Could not run chrome debugger! You need the following prefs " +
+                     "to be set to true: " + kDebuggerPrefs.join(", ");
+      Cu.reportError(errorMsg);
+      // Dump as well, as we're doing this from a commandline, make sure people don't miss it:
+      dump(errorMsg + "\n");
+    }
+
+    if (cmdLine.state == Ci.nsICommandLine.STATE_REMOTE_AUTO) {
+      cmdLine.preventDefault = true;
+    }
+  },
+
+  helpInfo : "  -jsconsole         Open the Browser Console.\n" +
+             "  -jsdebugger        Open the Browser Toolbox.\n",
+
+  classID: Components.ID("{9e9a9283-0ce9-4e4a-8f1c-ba129a032c32}"),
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler]),
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([devtoolsCommandlineHandler]);
new file mode 100644
--- /dev/null
+++ b/browser/devtools/devtools-clhandler.manifest
@@ -0,0 +1,2 @@
+component {9e9a9283-0ce9-4e4a-8f1c-ba129a032c32} devtools-clhandler.js
+contract @mozilla.org/toolkit/console-clh;1 {9e9a9283-0ce9-4e4a-8f1c-ba129a032c32}
--- a/browser/devtools/framework/ToolboxProcess.jsm
+++ b/browser/devtools/framework/ToolboxProcess.jsm
@@ -122,17 +122,23 @@ BrowserToolboxProcess.prototype = {
       this.loader.main("devtools/server/main");
       this.debuggerServer = this.loader.DebuggerServer;
       dumpn("Created a separate loader instance for the DebuggerServer.");
 
       // Forward interesting events.
       this.debuggerServer.on("connectionchange", this.emit.bind(this));
     }
 
-    this.debuggerServer.controller.start(Prefs.chromeDebuggingPort);
+    if (!this.debuggerServer.initialized) {
+      this.debuggerServer.init();
+      this.debuggerServer.addBrowserActors();
+      dumpn("initialized and added the browser actors for the DebuggerServer.");
+    }
+
+    this.debuggerServer.openListener(Prefs.chromeDebuggingPort);
 
     dumpn("Finished initializing the chrome toolbox server.");
     dumpn("Started listening on port: " + Prefs.chromeDebuggingPort);
   },
 
   /**
    * Initializes a profile for the remote debugger process.
    */
--- a/browser/devtools/framework/gDevTools.jsm
+++ b/browser/devtools/framework/gDevTools.jsm
@@ -434,23 +434,28 @@ let gDevToolsBrowser = {
       win.DeveloperToolbar.show(false);
     }
 
     // Enable App Manager?
     let appMgrEnabled = Services.prefs.getBoolPref("devtools.appmanager.enabled");
     toggleCmd("Tools:DevAppMgr", appMgrEnabled);
 
     // Enable Browser Toolbox?
-    let chromeEnabled = Services.prefs.getBoolPref("devtools.chrome.enabled") &&
+    let chromeEnabled = Services.prefs.getBoolPref("devtools.chrome.enabled");
+    let devtoolsRemoteEnabled = Services.prefs.getBoolPref("devtools.debugger.remote-enabled");
+    let remoteEnabled = chromeEnabled && devtoolsRemoteEnabled &&
                         Services.prefs.getBoolPref("devtools.debugger.chrome-enabled");
-    toggleCmd("Tools:BrowserToolbox", chromeEnabled);
+    toggleCmd("Tools:BrowserToolbox", remoteEnabled);
 
     // Enable Error Console?
     let consoleEnabled = Services.prefs.getBoolPref("devtools.errorconsole.enabled");
     toggleCmd("Tools:ErrorConsole", consoleEnabled);
+
+    // Enable DevTools connection screen, if the preference allows this.
+    toggleCmd("Tools:DevToolsConnect", devtoolsRemoteEnabled);
   },
 
   observe: function(subject, topic, prefName) {
     if (prefName.endsWith("enabled")) {
       for (let win of this._trackedBrowserWindows) {
         this.updateCommandAvailability(win);
       }
     }
--- a/browser/devtools/framework/test/browser_dynamic_tool_enabling.js
+++ b/browser/devtools/framework/test/browser_dynamic_tool_enabling.js
@@ -1,19 +1,19 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that toggling prefs immediately (de)activates the relevant menuitem
 
 let gItemsToTest = {
   "menu_devToolbar": "devtools.toolbar.enabled",
   "menu_devAppMgr": "devtools.appmanager.enabled",
-  "menu_browserToolbox": ["devtools.chrome.enabled", "devtools.debugger.chrome-enabled"],
+  "menu_browserToolbox": ["devtools.chrome.enabled", "devtools.debugger.remote-enabled", "devtools.debugger.chrome-enabled"],
   "javascriptConsole": "devtools.errorconsole.enabled",
-  "menu_devtools_connect": [],
+  "menu_devtools_connect": "devtools.debugger.remote-enabled",
 };
 
 function expectedAttributeValueFromPrefs(prefs) {
   return prefs.every((pref) => Services.prefs.getBoolPref(pref)) ?
          "" : "true";
 }
 
 function checkItem(el, prefs) {
--- a/browser/devtools/framework/test/head.js
+++ b/browser/devtools/framework/test/head.js
@@ -8,20 +8,16 @@ let tempScope = {};
 Components.utils.import("resource://gre/modules/devtools/Console.jsm", tempScope);
 let console = tempScope.console;
 Components.utils.import("resource://gre/modules/Promise.jsm", tempScope);
 let promise = tempScope.Promise;
 
 let {devtools} = Components.utils.import("resource://gre/modules/devtools/Loader.jsm", {});
 let TargetFactory = devtools.TargetFactory;
 
-let {Services} = Components.utils.import("resource://gre/modules/Services.jsm", {});
-// Disable notifications (to avoid "unknown window" errors)
-Services.prefs.setBoolPref("devtools.debugger.show-server-notifications", false);
-
 gDevTools.testing = true;
 SimpleTest.registerCleanupFunction(() => {
   gDevTools.testing = false;
 });
 
 /**
  * Open a new tab at a URL and call a callback on load
  */
@@ -53,17 +49,16 @@ function addTab(aURL, aCallback)
   browser.addEventListener("load", onTabLoad, true);
   return deferred.promise;
 }
 
 registerCleanupFunction(function tearDown() {
   while (gBrowser.tabs.length > 1) {
     gBrowser.removeCurrentTab();
   }
-  Services.prefs.clearUserPref("devtools.debugger.show-server-notifications");
 });
 
 function synthesizeKeyFromKeyTag(aKeyId, document) {
   let key = document.getElementById(aKeyId);
   isnot(key, null, "Successfully retrieved the <key> node");
 
   let modifiersAttr = key.getAttribute("modifiers");
 
--- a/browser/devtools/framework/toolbox-options.xul
+++ b/browser/devtools/framework/toolbox-options.xul
@@ -92,17 +92,17 @@
                     tooltiptext="&options.disableJavaScript.tooltip;"/>
           <hbox class="hidden-labels-box">
             <checkbox label="&options.enableChrome.label4;"
                       tooltiptext="&options.enableChrome.tooltip2;"
                       data-pref="devtools.chrome.enabled"/>
           </hbox>
           <hbox class="hidden-labels-box">
             <checkbox label="&options.enableRemote.label3;"
-                      tooltiptext="&options.enableRemote.tooltip2;"
+                      tooltiptext="&options.enableRemote.tooltip;"
                       data-pref="devtools.debugger.remote-enabled"/>
           </hbox>
           <label class="options-citation-label"
                  value="&options.context.triggersPageRefresh;"/>
         </vbox>
       </vbox>
     </hbox>
   </hbox>
--- a/browser/devtools/moz.build
+++ b/browser/devtools/moz.build
@@ -3,17 +3,16 @@
 # 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/.
 
 DIRS += [
     'app-manager',
     'canvasdebugger',
     'commandline',
-    'components',
     'debugger',
     'eyedropper',
     'fontinspector',
     'framework',
     'inspector',
     'layoutview',
     'markupview',
     'netmonitor',
@@ -25,9 +24,14 @@ DIRS += [
     'sourceeditor',
     'styleeditor',
     'styleinspector',
     'tilt',
     'webaudioeditor',
     'webconsole',
 ]
 
+EXTRA_COMPONENTS += [
+    'devtools-clhandler.js',
+    'devtools-clhandler.manifest',
+]
+
 JAR_MANIFESTS += ['jar.mn']
--- a/browser/devtools/profiler/test/browser_profiler_remote.js
+++ b/browser/devtools/profiler/test/browser_profiler_remote.js
@@ -12,16 +12,17 @@ Cu.import("resource://gre/modules/devtoo
 let DebuggerClient = temp.DebuggerClient;
 let debuggerSocketConnect = temp.debuggerSocketConnect;
 
 Cu.import("resource:///modules/devtools/profiler/controller.js", temp);
 let ProfilerController = temp.ProfilerController;
 
 function test() {
   waitForExplicitFinish();
+  Services.prefs.setBoolPref(REMOTE_ENABLED, true);
 
   loadTab(URL, function onTabLoad(tab, browser) {
     DebuggerServer.init(function () true);
     DebuggerServer.addBrowserActors();
     is(DebuggerServer._socketConnections, 0);
 
     DebuggerServer.openListener(2929);
     is(DebuggerServer._socketConnections, 1);
--- a/browser/devtools/profiler/test/head.js
+++ b/browser/devtools/profiler/test/head.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 let temp = {};
 
 const PROFILER_ENABLED = "devtools.profiler.enabled";
+const REMOTE_ENABLED = "devtools.debugger.remote-enabled";
 const SHOW_PLATFORM_DATA = "devtools.profiler.ui.show-platform-data";
-const SHOW_NOTIFICATIONS = "devtools.debugger.show-server-notifications";
 const PROFILE_IDLE = 0;
 const PROFILE_RUNNING = 1;
 const PROFILE_COMPLETED = 2;
 
 Cu.import("resource:///modules/devtools/gDevTools.jsm", temp);
 let gDevTools = temp.gDevTools;
 
 Cu.import("resource://gre/modules/devtools/Loader.jsm", temp);
@@ -26,18 +26,18 @@ Services.scriptloader.loadSubScript(test
 gDevTools.testing = true;
 SimpleTest.registerCleanupFunction(() => {
   gDevTools.testing = false;
 });
 
 registerCleanupFunction(function () {
   helpers = null;
   Services.prefs.clearUserPref(PROFILER_ENABLED);
+  Services.prefs.clearUserPref(REMOTE_ENABLED);
   Services.prefs.clearUserPref(SHOW_PLATFORM_DATA);
-  Services.prefs.clearUserPref(SHOW_NOTIFICATIONS);
   DebuggerServer.destroy();
 
   // These tests use a lot of memory due to GL contexts, so force a GC to help
   // fragmentation.
   info("Forcing GC after profiler test.");
   Cu.forceGC();
 });
 
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -361,16 +361,18 @@
 @BINPATH@/browser/components/nsBrowserContentHandler.js
 @BINPATH@/browser/components/nsBrowserGlue.js
 @BINPATH@/browser/components/nsSetDefaultBrowser.manifest
 @BINPATH@/browser/components/nsSetDefaultBrowser.js
 @BINPATH@/browser/components/BrowserDownloads.manifest
 @BINPATH@/browser/components/DownloadsStartup.js
 @BINPATH@/browser/components/DownloadsUI.js
 @BINPATH@/browser/components/BrowserPlaces.manifest
+@BINPATH@/browser/components/devtools-clhandler.manifest
+@BINPATH@/browser/components/devtools-clhandler.js
 @BINPATH@/browser/components/Experiments.manifest
 @BINPATH@/browser/components/ExperimentsService.js
 @BINPATH@/components/Downloads.manifest
 @BINPATH@/components/DownloadLegacy.js
 @BINPATH@/components/BrowserPageThumbs.manifest
 @BINPATH@/components/crashmonitor.manifest
 @BINPATH@/components/nsCrashMonitor.js
 @BINPATH@/components/SiteSpecificUserAgent.js
@@ -568,30 +570,21 @@
 #ifdef MOZ_WEBSPEECH
 @BINPATH@/components/dom_webspeechsynth.xpt
 #endif
 
 ; InputMethod API
 @BINPATH@/components/MozKeyboard.js
 @BINPATH@/components/InputMethod.manifest
 
-
 #ifdef MOZ_DEBUG
 @BINPATH@/components/TestInterfaceJS.js
 @BINPATH@/components/TestInterfaceJS.manifest
 #endif
 
-; DevTools browser
-@BINPATH@/browser/components/DevToolsComponents.manifest
-@BINPATH@/browser/components/DebuggerServerController.js
-@BINPATH@/browser/components/CommandLineHandler.js
-; DevTools toolkit
-@BINPATH@/components/DevToolsComponents.manifest
-@BINPATH@/components/DevToolsAppStartup.js
-
 ; Modules
 @BINPATH@/browser/modules/*
 @BINPATH@/modules/*
 
 ; Safe Browsing
 #ifdef MOZ_URL_CLASSIFIER
 @BINPATH@/components/nsURLClassifier.manifest
 @BINPATH@/components/nsUrlClassifierHashCompleter.js
--- a/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties
@@ -1346,24 +1346,28 @@ listenDesc=Open a remote debug port
 # LOCALIZATION NOTE (listenManual2) A longer description of the 'listen'
 # command.
 listenManual2=%1$S can allow remote debugging over a TCP/IP connection. For security reasons this is turned off by default, but can be enabled using this command.
 
 # LOCALIZATION NOTE (listenPortDesc) A very short string used to describe the
 # function of 'port' parameter to the 'listen' command.
 listenPortDesc=The TCP port to listen on
 
-# LOCALIZATION NOTE (listenFailed) Text of a failure message during the
+# LOCALIZATION NOTE (listenDisabledOutput) Text of a message output during the
 # execution of the 'listen' command.
-listenFailed=Listen failed.
+listenDisabledOutput=Listen is disabled by the devtools.debugger.remote-enabled preference
 
 # LOCALIZATION NOTE (listenInitOutput) Text of a message output during the
 # execution of the 'listen' command. %1$S is a port number
 listenInitOutput=Listening on port %1$S
 
+# LOCALIZATION NOTE (listenNoInitOutput) Text of a message output during the
+# execution of the 'listen' command.
+listenNoInitOutput=DebuggerServer not initialized
+
 # LOCALIZATION NOTE (mediaDesc, mediaEmulateDesc, mediaEmulateManual,
 # mediaEmulateType, mediaResetDesc, mediaResetManual) These strings describe
 # the 'media' commands and all available parameters.
 mediaDesc=CSS media type emulation
 mediaEmulateDesc=Emulate a specified CSS media type
 mediaEmulateManual=View the document as if rendered on a device supporting the given media type, with the relevant CSS rules applied.
 mediaEmulateType=The media type to emulate
 mediaResetDesc=Stop emulating a CSS media type
--- a/browser/locales/en-US/chrome/browser/devtools/toolbox.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/toolbox.dtd
@@ -62,17 +62,17 @@
   -  boolean preference in about:config, in the options panel. -->
 <!ENTITY options.enableChrome.label4    "Enable chrome and addon debugging">
 <!ENTITY options.enableChrome.tooltip2  "Turning this option on will allow you to use various developer tools in browser context and debug addons from the Add-On Manager">
 
 <!-- LOCALIZATION NOTE (options.enableRemote.label3): This is the label for the
   -  checkbox that toggles remote debugging, i.e. devtools.debugger.remote-enabled
   -  boolean preference in about:config, in the options panel. -->
 <!ENTITY options.enableRemote.label3    "Enable remote debugging">
-<!ENTITY options.enableRemote.tooltip2  "Turning this option on will start a debugger server and allow other instances of Firefox to debug this instance">
+<!ENTITY options.enableRemote.tooltip   "Turning this option on will allow the developer tools to debug remote Firefox instance like Firefox OS">
 
 <!-- LOCALIZATION NOTE (options.disableJavaScript.label,
   -  options.disableJavaScript.tooltip): This is the options panel label and
   -  tooltip for the checkbox that toggles JavaScript on or off. -->
 <!ENTITY options.disableJavaScript.label     "Disable JavaScript *">
 <!ENTITY options.disableJavaScript.tooltip   "Turning this option on will disable JavaScript for the current tab. If the tab or the toolbox is closed then this setting will be forgotten.">
 
 <!-- LOCALIZATION NOTE (options.disableCache.label,
--- a/browser/metro/base/content/browser-ui.js
+++ b/browser/metro/base/content/browser-ui.js
@@ -6,16 +6,20 @@
 
 Cu.import("resource://gre/modules/devtools/dbg-server.jsm")
 Cu.import("resource://gre/modules/WindowsPrefSync.jsm");
 
 /**
  * Constants
  */
 
+// Devtools Messages
+const debugServerStateChanged = "devtools.debugger.remote-enabled";
+const debugServerPortChanged = "devtools.debugger.remote-port";
+
 // delay when showing the tab bar briefly after a new foreground tab opens
 const kForegroundTabAnimationDelay = 1000;
 // delay when showing the tab bar after opening a new background tab opens
 const kBackgroundTabAnimationDelay = 3000;
 // delay before closing tab bar after closing or selecting a tab
 const kChangeTabAnimationDelay = 500;
 
 /**
@@ -72,16 +76,22 @@ var BrowserUI = {
   get _edit() { return document.getElementById("urlbar-edit"); },
   get _back() { return document.getElementById("cmd_back"); },
   get _forward() { return document.getElementById("cmd_forward"); },
 
   lastKnownGoodURL: "", // used when the user wants to escape unfinished url entry
   ready: false, // used for tests to determine when delayed initialization is done
 
   init: function() {
+    // start the debugger now so we can use it on the startup code as well
+    if (Services.prefs.getBoolPref(debugServerStateChanged)) {
+      this.runDebugServer();
+    }
+    Services.prefs.addObserver(debugServerStateChanged, this, false);
+    Services.prefs.addObserver(debugServerPortChanged, this, false);
     Services.prefs.addObserver("app.crashreporter.autosubmit", this, false);
     Services.prefs.addObserver("metro.private_browsing.enabled", this, false);
     this.updatePrivateBrowsingUI();
 
     Services.obs.addObserver(this, "handle-xul-text-link", false);
 
     // listen content messages
     messageManager.addMessageListener("DOMTitleChanged", this);
@@ -191,29 +201,62 @@ var BrowserUI = {
     messageManager.removeMessageListener("DOMWindowClose", this);
 
     messageManager.removeMessageListener("Browser:OpenURI", this);
     messageManager.removeMessageListener("Browser:SaveAs:Return", this);
     messageManager.removeMessageListener("Content:StateChange", this);
 
     messageManager.removeMessageListener("Browser:MozApplicationManifest", OfflineApps);
 
+    Services.prefs.removeObserver(debugServerStateChanged, this);
+    Services.prefs.removeObserver(debugServerPortChanged, this);
     Services.prefs.removeObserver("app.crashreporter.autosubmit", this);
     Services.prefs.removeObserver("metro.private_browsing.enabled", this);
 
     Services.obs.removeObserver(this, "handle-xul-text-link");
 
     PanelUI.uninit();
     FlyoutPanelsUI.uninit();
     MetroDownloadsView.uninit();
     SettingsCharm.uninit();
     PageThumbs.uninit();
     if (WindowsPrefSync) {
       WindowsPrefSync.uninit();
     }
+    this.stopDebugServer();
+  },
+
+  /************************************
+   * Devtools Debugger
+   */
+  runDebugServer: function runDebugServer(aPort) {
+    let port = aPort || Services.prefs.getIntPref(debugServerPortChanged);
+    if (!DebuggerServer.initialized) {
+      DebuggerServer.init();
+      DebuggerServer.addBrowserActors();
+      DebuggerServer.addActors('chrome://browser/content/dbg-metro-actors.js');
+    }
+    DebuggerServer.openListener(port);
+  },
+
+  stopDebugServer: function stopDebugServer() {
+    if (DebuggerServer.initialized) {
+      DebuggerServer.destroy();
+    }
+  },
+
+  // If the server is not on, port changes have nothing to effect. The new value
+  //    will be picked up if the server is started.
+  // To be consistent with desktop fx, if the port is changed while the server
+  //    is running, restart server.
+  changeDebugPort:function changeDebugPort(aPort) {
+    if (DebuggerServer.initialized) {
+      this.stopDebugServer();
+      this.runDebugServer(aPort);
+    }
   },
 
   /*********************************
    * Content visibility
    */
 
   get isContentShowing() {
     return Elements.contentShowing.getAttribute("disabled") != true;
@@ -579,16 +622,26 @@ var BrowserUI = {
           handled.data = true;
         }
         break;
       case "nsPref:changed":
         switch (aData) {
           case "browser.cache.disk_cache_ssl":
             this._sslDiskCacheEnabled = Services.prefs.getBoolPref(aData);
             break;
+          case debugServerStateChanged:
+            if (Services.prefs.getBoolPref(aData)) {
+              this.runDebugServer();
+            } else {
+              this.stopDebugServer();
+            }
+            break;
+          case debugServerPortChanged:
+            this.changeDebugPort(Services.prefs.getIntPref(aData));
+            break;
           case "app.crashreporter.autosubmit":
 #ifdef MOZ_CRASHREPORTER
             CrashReporter.submitReports = Services.prefs.getBoolPref(aData);
 
             // The user explicitly set the autosubmit option, so there is no
             // need to prompt them about crash reporting in the future
             Services.prefs.setBoolPref("app.crashreporter.prompted", true);
 
deleted file mode 100644
--- a/browser/metro/components/DebuggerServerController.js
+++ /dev/null
@@ -1,99 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-"use strict";
-
-const { interfaces: Ci, utils: Cu, results: Cr } = Components;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-
-XPCOMUtils.defineLazyServiceGetter(this,
-    "Alerts",
-    "@mozilla.org/alerts-service;1", "nsIAlertsService");
-
-XPCOMUtils.defineLazyGetter(this,
-    "l10n",
-    () => Services.strings.createBundle("chrome://global/locale/devtools/debugger.properties"));
-
-function DebuggerServerController() {
-}
-
-DebuggerServerController.prototype = {
-  classID: Components.ID("{0849238d-6fb7-4bc4-87b7-4019bb53e01b}"),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDebuggerServerController, Ci.nsIObserver]),
-
-  init: function(debuggerServer) {
-    this.debugger = debuggerServer;
-    Services.obs.addObserver(this, "debugger-server-started", false);
-    Services.obs.addObserver(this, "debugger-server-stopped", false);
-    Services.obs.addObserver(this, "xpcom-shutdown", false);
-  },
-
-  uninit: function() {
-    this.debugger = null;
-    Services.obs.removeObserver(this, "debugger-server-started");
-    Services.obs.removeObserver(this, "debugger-server-stopped");
-    Services.obs.removeObserver(this, "xpcom-shutdown");
-  },
-
-  start: function(pathOrPort) {
-    if (!this.debugger.initialized) {
-      this.debugger.init();
-      this.debugger.addBrowserActors();
-      this.debugger.addActors("chrome://browser/content/dbg-metro-actors.js");
-    }
-
-    if (!pathOrPort) {
-      // If the "devtools.debugger.unix-domain-socket" pref is set, we use a unix socket.
-      // If not, we use a regular TCP socket.
-      try {
-        pathOrPort = Services.prefs.getCharPref("devtools.debugger.unix-domain-socket");
-      } catch (e) {
-        pathOrPort = Services.prefs.getIntPref("devtools.debugger.remote-port");
-      }
-    }
-
-    try {
-      this.debugger.openListener(pathOrPort);
-    } catch (e if e.result != Cr.NS_ERROR_NOT_AVAILABLE) {
-      dump("Unable to start debugger server (" + pathOrPort + "): " + e + "\n");
-    }
-  },
-
-  stop: function() {
-    this.debugger.closeListener(true);
-  },
-
-  // nsIObserver
-
-  observe: function (subject, topic, data) {
-    if (topic == "xpcom-shutdown")
-      this.uninit();
-    if (topic == "debugger-server-started")
-      this._onDebuggerStarted(data);
-    if (topic == "debugger-server-stopped")
-      this._onDebuggerStopped();
-  },
-
-  _onDebuggerStarted: function(portOrPath) {
-    let title = l10n.GetStringFromName("debuggerStartedAlert.title");
-    let port = Number(portOrPath);
-    let detail;
-    if (port) {
-      detail = l10n.formatStringFromName("debuggerStartedAlert.detailPort", [portOrPath], 1);
-    } else {
-      detail = l10n.formatStringFromName("debuggerStartedAlert.detailPath", [portOrPath], 1);
-    }
-    Alerts.showAlertNotification(null, title, detail, false, "", function(){});
-  },
-
-  _onDebuggerStopped: function() {
-    let title = l10n.GetStringFromName("debuggerStopped.title");
-    Alerts.showAlertNotification(null, title, null, false, "", function(){});
-  },
-};
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DebuggerServerController]);
--- a/browser/metro/components/components.manifest
+++ b/browser/metro/components/components.manifest
@@ -76,11 +76,8 @@ contract @mozilla.org/login-manager/prom
 
 #ifdef MOZ_SAFE_BROWSING
 # SafeBrowsing.js
 component {aadaed90-6c03-42d0-924a-fc61198ff283} SafeBrowsing.js
 contract @mozilla.org/safebrowsing/application;1 {aadaed90-6c03-42d0-924a-fc61198ff283}
 category app-startup SafeBrowsing service,@mozilla.org/safebrowsing/application;1
 #endif
 
-# DebuggerServerController.js
-component {0849238d-6fb7-4bc4-87b7-4019bb53e01b} DebuggerServerController.js
-contract @mozilla.org/devtools/DebuggerServerController;1 {0849238d-6fb7-4bc4-87b7-4019bb53e01b}
--- a/browser/metro/components/moz.build
+++ b/browser/metro/components/moz.build
@@ -11,17 +11,16 @@ XPIDL_SOURCES += [
 XPIDL_MODULE = 'components'
 
 # metro/components.manifest
 EXTRA_COMPONENTS += [
     'AlertsService.js',
     'BrowserStartup.js',
     'ContentDispatchChooser.js',
     'ContentPermissionPrompt.js',
-    'DebuggerServerController.js',
     'DirectoryProvider.js',
     'DownloadManagerUI.js',
     'HelperAppDialog.js',
     'LoginManagerPrompter.js',
     'PromptService.js',
     'Sidebar.js',
 ]
 
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -26,16 +26,19 @@ Cu.import("resource://gre/modules/access
 #endif
 
 XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
                                   "resource://gre/modules/PluralForm.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "sendMessageToJava",
                                   "resource://gre/modules/Messaging.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "DebuggerServer",
+                                  "resource://gre/modules/devtools/dbg-server.jsm");
+
 XPCOMUtils.defineLazyModuleGetter(this, "UserAgentOverrides",
                                   "resource://gre/modules/UserAgentOverrides.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerContent",
                                   "resource://gre/modules/LoginManagerContent.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
@@ -367,16 +370,17 @@ var BrowserApp = {
     ActivityObserver.init();
 #ifdef MOZ_ANDROID_SYNTHAPKS
     // TODO: replace with Android implementation of WebappOSUtils.isLaunchable.
     Cu.import("resource://gre/modules/Webapps.jsm");
     DOMApplicationRegistry.allAppsLaunchable = true;
 #else
     WebappsUI.init();
 #endif
+    RemoteDebugger.init();
     Reader.init();
     UserAgentOverrides.init();
     DesktopUserAgent.init();
     CastingApps.init();
     Distribution.init();
     Tabs.init();
 #ifdef ACCESSIBILITY
     AccessFu.attach(window);
@@ -745,16 +749,17 @@ var BrowserApp = {
     ViewportHandler.uninit();
     XPInstallObserver.uninit();
     HealthReportStatusListener.uninit();
     CharacterEncoding.uninit();
     SearchEngines.uninit();
 #ifndef MOZ_ANDROID_SYNTHAPKS
     WebappsUI.uninit();
 #endif
+    RemoteDebugger.uninit();
     Reader.uninit();
     UserAgentOverrides.uninit();
     DesktopUserAgent.uninit();
     ExternalApps.uninit();
     CastingApps.uninit();
     Distribution.uninit();
     Tabs.uninit();
   },
@@ -7311,16 +7316,127 @@ var WebappsUI = {
       } catch(e) {
         Cu.reportError(e);
       }
     });
   }
 }
 #endif
 
+var RemoteDebugger = {
+  init: function rd_init() {
+    Services.prefs.addObserver("devtools.debugger.", this, false);
+
+    if (this._isEnabled())
+      this._start();
+  },
+
+  observe: function rd_observe(aSubject, aTopic, aData) {
+    if (aTopic != "nsPref:changed")
+      return;
+
+    switch (aData) {
+      case "devtools.debugger.remote-enabled":
+        if (this._isEnabled())
+          this._start();
+        else
+          this._stop();
+        break;
+
+      case "devtools.debugger.remote-port":
+        if (this._isEnabled())
+          this._restart();
+        break;
+    }
+  },
+
+  uninit: function rd_uninit() {
+    Services.prefs.removeObserver("devtools.debugger.", this);
+    this._stop();
+  },
+
+  _getPort: function _rd_getPort() {
+    return Services.prefs.getIntPref("devtools.debugger.remote-port");
+  },
+
+  _isEnabled: function rd_isEnabled() {
+    return Services.prefs.getBoolPref("devtools.debugger.remote-enabled");
+  },
+
+  /**
+   * Prompt the user to accept or decline the incoming connection.
+   * This is passed to DebuggerService.init as a callback.
+   *
+   * @return true if the connection should be permitted, false otherwise
+   */
+  _showConnectionPrompt: function rd_showConnectionPrompt() {
+    let title = Strings.browser.GetStringFromName("remoteIncomingPromptTitle");
+    let msg = Strings.browser.GetStringFromName("remoteIncomingPromptMessage");
+    let disable = Strings.browser.GetStringFromName("remoteIncomingPromptDisable");
+    let cancel = Strings.browser.GetStringFromName("remoteIncomingPromptCancel");
+    let agree = Strings.browser.GetStringFromName("remoteIncomingPromptAccept");
+
+    // Make prompt. Note: button order is in reverse.
+    let prompt = new Prompt({
+      window: null,
+      hint: "remotedebug",
+      title: title,
+      message: msg,
+      buttons: [ agree, cancel, disable ],
+      priority: 1
+    });
+
+    // The debugger server expects a synchronous response, so spin on result since Prompt is async.
+    let result = null;
+
+    prompt.show(function(data) {
+      result = data.button;
+    });
+
+    // Spin this thread while we wait for a result.
+    let thread = Services.tm.currentThread;
+    while (result == null)
+      thread.processNextEvent(true);
+
+    if (result === 0)
+      return true;
+    if (result === 2) {
+      Services.prefs.setBoolPref("devtools.debugger.remote-enabled", false);
+      this._stop();
+    }
+    return false;
+  },
+
+  _restart: function rd_restart() {
+    this._stop();
+    this._start();
+  },
+
+  _start: function rd_start() {
+    try {
+      if (!DebuggerServer.initialized) {
+        DebuggerServer.init(this._showConnectionPrompt.bind(this));
+        DebuggerServer.addBrowserActors();
+        DebuggerServer.addActors("chrome://browser/content/dbg-browser-actors.js");
+      }
+
+      let port = this._getPort();
+      DebuggerServer.openListener(port);
+      dump("Remote debugger listening on port " + port);
+    } catch(e) {
+      dump("Remote debugger didn't start: " + e);
+    }
+  },
+
+  _stop: function rd_start() {
+    DebuggerServer.closeListener();
+    dump("Remote debugger stopped");
+  }
+};
+
 var Telemetry = {
   addData: function addData(aHistogramId, aValue) {
     let histogram = Services.telemetry.getHistogramById(aHistogramId);
     histogram.add(aValue);
   },
 };
 
 let Reader = {
deleted file mode 100644
--- a/mobile/android/components/DebuggerServerController.js
+++ /dev/null
@@ -1,145 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-const { interfaces: Ci, utils: Cu } = Components;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-
-XPCOMUtils.defineLazyServiceGetter(this,
-    "Alerts",
-    "@mozilla.org/alerts-service;1", "nsIAlertsService");
-
-XPCOMUtils.defineLazyModuleGetter(this,
-    "Prompt",
-    "resource://gre/modules/Prompt.jsm");
-
-let Strings = {};
-XPCOMUtils.defineLazyGetter(Strings, "debugger",
-    () => Services.strings.createBundle("chrome://global/locale/devtools/debugger.properties"));
-XPCOMUtils.defineLazyGetter(Strings, "browser",
-    () => Services.strings.createBundle("chrome://browser/locale/browser.properties"));
-
-function DebuggerServerController() {
-}
-
-DebuggerServerController.prototype = {
-  classID: Components.ID("{f6e8e269-ae4a-4c4a-bf80-fb4164fb072c}"),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDebuggerServerController, Ci.nsIObserver]),
-
-  init: function(debuggerServer) {
-    this.debugger = debuggerServer;
-    Services.obs.addObserver(this, "debugger-server-started", false);
-    Services.obs.addObserver(this, "debugger-server-stopped", false);
-    Services.obs.addObserver(this, "xpcom-shutdown", false);
-  },
-
-  uninit: function() {
-    this.debugger = null;
-    Services.obs.removeObserver(this, "debugger-server-started");
-    Services.obs.removeObserver(this, "debugger-server-stopped");
-    Services.obs.removeObserver(this, "xpcom-shutdown");
-  },
-
-  start: function(pathOrPort) {
-    if (!this.debugger.initialized) {
-      this.debugger.init(this.prompt.bind(this));
-      this.debugger.addBrowserActors();
-      this.debugger.addActors("chrome://browser/content/dbg-browser-actors.js");
-    }
-
-    if (!pathOrPort) {
-      // If the "devtools.debugger.unix-domain-socket" pref is set, we use a unix socket.
-      // If not, we use a regular TCP socket.
-      try {
-        pathOrPort = Services.prefs.getCharPref("devtools.debugger.unix-domain-socket");
-      } catch (e) {
-        pathOrPort = Services.prefs.getIntPref("devtools.debugger.remote-port");
-      }
-    }
-
-    try {
-      this.debugger.openListener(pathOrPort);
-    } catch (e) {
-      dump("Unable to start debugger server (" + pathOrPort + "): " + e + "\n");
-    }
-  },
-
-  stop: function() {
-    this.debugger.destroy();
-  },
-
-  prompt: function() {
-    let title = Strings.browser.GetStringFromName("remoteIncomingPromptTitle");
-    let msg = Strings.browser.GetStringFromName("remoteIncomingPromptMessage");
-    let disable = Strings.browser.GetStringFromName("remoteIncomingPromptDisable");
-    let cancel = Strings.browser.GetStringFromName("remoteIncomingPromptCancel");
-    let agree = Strings.browser.GetStringFromName("remoteIncomingPromptAccept");
-
-    // Make prompt. Note: button order is in reverse.
-    let prompt = new Prompt({
-      window: null,
-      hint: "remotedebug",
-      title: title,
-      message: msg,
-      buttons: [ agree, cancel, disable ],
-      priority: 1
-    });
-
-    // The debugger server expects a synchronous response, so spin on result since Prompt is async.
-    let result = null;
-
-    prompt.show(function(data) {
-      result = data.button;
-    });
-
-    // Spin this thread while we wait for a result.
-    let thread = Services.tm.currentThread;
-    while (result == null)
-      thread.processNextEvent(true);
-
-    if (result === 0)
-      return true;
-    if (result === 2) {
-      Services.prefs.setBoolPref("devtools.debugger.remote-enabled", false);
-      this.debugger.destroy();
-    }
-    return false;
-  },
-
-  // nsIObserver
-
-  observe: function (subject, topic, data) {
-    if (topic == "xpcom-shutdown")
-      this.uninit();
-    if (topic == "debugger-server-started")
-      this._onDebuggerStarted(data);
-    if (topic == "debugger-server-stopped")
-      this._onDebuggerStopped();
-  },
-
-  _onDebuggerStarted: function(portOrPath) {
-    if (!Services.prefs.getBoolPref("devtools.debugger.show-server-notifications"))
-      return;
-    let title = l10n.GetStringFromName("debuggerStartedAlert.title");
-    let port = Number(portOrPath);
-    let detail;
-    if (port) {
-      detail = l10n.formatStringFromName("debuggerStartedAlert.detailPort", [portOrPath], 1);
-    } else {
-      detail = l10n.formatStringFromName("debuggerStartedAlert.detailPath", [portOrPath], 1);
-    }
-    Alerts.showAlertNotification(null, title, detail, false, "", function(){});
-  },
-
-  _onDebuggerStopped: function() {
-    if (!Services.prefs.getBoolPref("devtools.debugger.show-server-notifications"))
-      return;
-    let title = l10n.GetStringFromName("debuggerStopped.title");
-    Alerts.showAlertNotification(null, title, null, false, "", function(){});
-  },
-};
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DebuggerServerController]);
--- a/mobile/android/components/MobileComponents.manifest
+++ b/mobile/android/components/MobileComponents.manifest
@@ -115,12 +115,8 @@ category update-timer Snippets @mozilla.
 component {8f7002cb-e959-4f0a-a2e8-563232564385} WebappsUpdateTimer.js
 contract @mozilla.org/webapps-update-timer;1 {8f7002cb-e959-4f0a-a2e8-563232564385}
 category update-timer WebappsUpdateTimer @mozilla.org/webapps-update-timer;1,getService,webapp-background-update-timer,browser.webapps.updateInterval,86400
 #endif
 
 # ColorPicker.js
 component {430b987f-bb9f-46a3-99a5-241749220b29} ColorPicker.js
 contract @mozilla.org/colorpicker;1 {430b987f-bb9f-46a3-99a5-241749220b29}
-
-# DebuggerServerController.js
-component {f6e8e269-ae4a-4c4a-bf80-fb4164fb072c} DebuggerServerController.js
-contract @mozilla.org/devtools/DebuggerServerController;1 {f6e8e269-ae4a-4c4a-bf80-fb4164fb072c}
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -415,21 +415,16 @@
 @BINPATH@/components/HealthReportService.js
 #endif
 
 #ifdef MOZ_CAPTIVEDETECT
 @BINPATH@/components/CaptivePortalDetectComponents.manifest
 @BINPATH@/components/captivedetect.js
 #endif
 
-; DevTools
-@BINPATH@/components/DevToolsComponents.manifest
-@BINPATH@/components/DevToolsAppStartup.js
-@BINPATH@/components/DebuggerServerController.js
-
 #ifdef MOZ_WEBSPEECH
 @BINPATH@/components/dom_webspeechsynth.xpt
 #endif
 
 #ifdef MOZ_DEBUG
 @BINPATH@/components/TestInterfaceJS.js
 @BINPATH@/components/TestInterfaceJS.manifest
 #endif
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -562,20 +562,17 @@ pref("toolkit.asyncshutdown.timeout.cras
 // Enable deprecation warnings.
 pref("devtools.errorconsole.deprecation_warnings", true);
 
 // Disable debugging chrome
 pref("devtools.chrome.enabled", false);
 
 // Disable remote debugging protocol logging
 pref("devtools.debugger.log", false);
-// Show notifications when server starts/stops
-pref("devtools.debugger.show-server-notifications", true);
-// Run a server debugger. Changing this pref dynamically will
-// stop / start the server debugger.
+// Disable remote debugging connections
 pref("devtools.debugger.remote-enabled", false);
 pref("devtools.debugger.remote-port", 6000);
 // Force debugger server binding on the loopback interface
 pref("devtools.debugger.force-local", true);
 // Display a prompt when a new connection starts to accept/reject it
 pref("devtools.debugger.prompt-connection", true);
 // Block tools from seeing / interacting with certified apps
 pref("devtools.debugger.forbid-certified-apps", true);
--- a/testing/mochitest/mochitest_options.py
+++ b/testing/mochitest/mochitest_options.py
@@ -510,16 +510,17 @@ class MochitestOptions(optparse.OptionPa
         if options.manifestFile and options.testManifest:
             self.error("Unable to support both --manifest and --test-manifest/--run-only-tests at the same time")
 
         if options.webapprtContent and options.webapprtChrome:
             self.error("Only one of --webapprt-content and --webapprt-chrome may be given.")
 
         if options.jsdebugger:
             options.extraPrefs += [
+                "devtools.debugger.remote-enabled=true",
                 "devtools.debugger.chrome-enabled=true",
                 "devtools.chrome.enabled=true",
                 "devtools.debugger.prompt-connection=false"
             ]
             options.autorun = False
 
         if options.debugOnFailure and not options.jsdebugger:
           self.error("--debug-on-failure should be used together with --jsdebugger.")
deleted file mode 100644
--- a/toolkit/devtools/DevToolsAppStartup.js
+++ /dev/null
@@ -1,133 +0,0 @@
-/* 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/. */
-
-/**
- * This component handles the -start-debugger-server command line
- * option. It also starts and stops the debugger server when the
- * devtools.debugger.remote-enable pref changes.
- */
-
-const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
-
-const REMOTE_ENABLED_PREF = "devtools.debugger.remote-enabled";
-const UNIX_SOCKET_PREF = "devtools.debugger.unix-domain-socket";
-const REMOTE_PORT_PREF = "devtools.debugger.remote-port";
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this,
-    "DebuggerServer",
-    "resource://gre/modules/devtools/dbg-server.jsm");
-
-function DevToolsAppStartup() {
-}
-
-DevToolsAppStartup.prototype = {
-  classID: Components.ID("{9ba9bbe7-5866-46f1-bea6-3299066b7933}"),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler, Ci.nsIObserver]),
-
-  get dbgPortOrPath() {
-    if (!this._dbgPortOrPath) {
-      // set default dbgPortOrPath value from preferences:
-      try {
-        this._dbgPortOrPath = Services.prefs.getCharPref(UNIX_SOCKET_PREF);
-      } catch(e) {
-        try {
-          this._dbgPortOrPath = Services.prefs.getIntPref(REMOTE_PORT_PREF);
-        } catch(e) {}
-      }
-    }
-    return this._dbgPortOrPath;
-  },
-
-  set dbgPortOrPath(value) {
-    this._dbgPortOrPath = value;
-  },
-
-  // nsICommandLineHandler
-
-  get helpInfo() {
-    let str = "";
-
-    // Starting the debugger is handled on the app side (not in /toolkit/).
-    // If the app didn't expose a debugger controller component, we don't
-    // support the -start-debugger-server option.
-
-    if (DebuggerServer.controller) {
-      str += "  -start-debugger-server [<port or unix domain socket path>]";
-      if (this.dbgPortOrPath) {
-        str += " (default: " + this.dbgPortOrPath + ")";
-      }
-      str += "\n";
-    }
-
-    return str;
-  },
-
-  handle: function(cmdLine) {
-    if (!DebuggerServer.controller) {
-      // This app doesn't expose a debugger controller.
-      // We can't handle the -start-debugger-server option
-      // or the remote-enable pref.
-      return;
-    }
-
-    let startDebuggerServerBecauseCmdLine = false;
-
-    try {
-      // Returns null if the argument was not specified. Throws
-      // NS_ERROR_INVALID_ARG if there is no parameter specified (because
-      // it was the last argument or the next argument starts with '-').
-      // However, someone could still explicitly pass an empty argument.
-      let param = cmdLine.handleFlagWithParam("start-debugger-server", false);
-      if (param) {
-        startDebuggerServerBecauseCmdLine = true;
-        this.dbgPortOrPath = param;
-      }
-    } catch(e) {
-      startDebuggerServerBecauseCmdLine = true;
-    }
-
-    // App has started and we handled the command line options (if any).
-    // Time to start the debugger if needed and observe the remote-enable
-    // pref.
-
-    if (startDebuggerServerBecauseCmdLine ||
-        Services.prefs.getBoolPref(REMOTE_ENABLED_PREF)) {
-      if (this.dbgPortOrPath) {
-        DebuggerServer.controller.start(this.dbgPortOrPath);
-      } else {
-        dump("Can't start debugger: no port or path specified\n");
-      }
-    }
-
-    Services.prefs.addObserver(REMOTE_ENABLED_PREF, this, false);
-    Services.prefs.addObserver(UNIX_SOCKET_PREF, this, false);
-    Services.prefs.addObserver(REMOTE_PORT_PREF, this, false);
-  },
-
-  // nsIObserver
-
-  observe: function (subject, topic, data) {
-    if (topic == "nsPref:changed") {
-      switch (data) {
-        case REMOTE_ENABLED_PREF:
-          if (Services.prefs.getBoolPref(data)) {
-            DebuggerServer.controller.start(this.dbgPortOrPath);
-          } else {
-            DebuggerServer.controller.stop();
-          }
-        break;
-        case UNIX_SOCKET_PREF:
-        case REMOTE_PORT_PREF:
-          // reset dbgPortOrPath value
-          this.dbgPortOrPath = null;
-        break;
-      }
-    }
-  },
-};
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DevToolsAppStartup]);
deleted file mode 100644
--- a/toolkit/devtools/DevToolsComponents.manifest
+++ /dev/null
@@ -1,3 +0,0 @@
-component {9ba9bbe7-5866-46f1-bea6-3299066b7933} DevToolsAppStartup.js
-contract @mozilla.org/toolkit/devtools-app-startup;1 {9ba9bbe7-5866-46f1-bea6-3299066b7933}
-category command-line-handler DevToolsAppStartup @mozilla.org/toolkit/devtools-app-startup;1
--- a/toolkit/devtools/apps/tests/debugger-protocol-helper.js
+++ b/toolkit/devtools/apps/tests/debugger-protocol-helper.js
@@ -9,26 +9,41 @@ const { DebuggerServer } = Cu.import("re
 const { DebuggerClient } = Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
 
 const { FileUtils } = Cu.import("resource://gre/modules/FileUtils.jsm");
 const { Services } = Cu.import("resource://gre/modules/Services.jsm");
 
 let gClient, gActor;
 
 function connect(onDone) {
-  Services.prefs.setBoolPref("devtools.debugger.prompt-connection", false);
-  let observer = {
-    observe: function (subject, topic, data) {
-      Services.obs.removeObserver(observer, "debugger-server-started");
-      let transport = debuggerSocketConnect("127.0.0.1", 6000);
-      startClient(transport, onDone);
-    }
-  };
-  Services.obs.addObserver(observer, "debugger-server-started", false);
-  DebuggerServer.controller.start(6000);
+
+  if (Services.appinfo.name == "B2G") {
+    // On b2g, we try to exercice the code that launches the production debugger server
+    let settingsService = Cc["@mozilla.org/settingsService;1"].getService(Ci.nsISettingsService);
+    settingsService.createLock().set("devtools.debugger.remote-enabled", true, null);
+    // We can't use `set` callback as it is fired before shell.js code listening for this setting
+    // is actually called. Same thing applies to mozsettings-changed obs notification.
+    // So listen to a custom event until bug 942756 lands
+    let observer = {
+      observe: function (subject, topic, data) {
+        Services.obs.removeObserver(observer, "debugger-server-started");
+        let transport = debuggerSocketConnect("127.0.0.1", 6000);
+        startClient(transport, onDone);
+      }
+    };
+    Services.obs.addObserver(observer, "debugger-server-started", false);
+  } else {
+    // Initialize a loopback remote protocol connection
+    DebuggerServer.init(function () { return true; });
+    // We need to register browser actors to have `listTabs` working
+    // and also have a root actor
+    DebuggerServer.addBrowserActors();
+    let transport = DebuggerServer.connectPipe();
+    startClient(transport, onDone);
+  }
 }
 
 function startClient(transport, onDone) {
   // Setup client and actor used in all tests
   gClient = new DebuggerClient(transport);
   gClient.connect(function onConnect() {
     gClient.listTabs(function onListTabs(aResponse) {
       gActor = aResponse.webappsActor;
--- a/toolkit/devtools/gcli/commands/listen.js
+++ b/toolkit/devtools/gcli/commands/listen.js
@@ -25,18 +25,25 @@ exports.items = [
         type: "number",
         get defaultValue() {
           return Services.prefs.getIntPref("devtools.debugger.chrome-debugging-port");
         },
         description: gcli.lookup("listenPortDesc"),
       }
     ],
     exec: function(args, context) {
-      DebuggerServer.controller.start(args.port);
-
-      if (!DebuggerServer._listener) {
-        return gcli.lookup("listenFailed");
+      if (!DebuggerServer.initialized) {
+        DebuggerServer.init();
+        DebuggerServer.addBrowserActors();
+      }
+      var reply = DebuggerServer.openListener(args.port);
+      if (!reply) {
+        throw new Error(gcli.lookup("listenDisabledOutput"));
       }
 
-      return gcli.lookupFormat("listenInitOutput", [ '' + args.port ]);
+      if (DebuggerServer.initialized) {
+        return gcli.lookupFormat("listenInitOutput", [ "" + args.port ]);
+      }
+
+      return gcli.lookup("listenNoInitOutput");
     },
   }
 ];
--- a/toolkit/devtools/moz.build
+++ b/toolkit/devtools/moz.build
@@ -11,15 +11,10 @@ PARALLEL_DIRS += [
     'sourcemap',
     'webconsole',
     'apps',
     'styleinspector',
     'acorn',
     'pretty-fast'
 ]
 
-EXTRA_COMPONENTS += [
-    'DevToolsAppStartup.js',
-    'DevToolsComponents.manifest'
-]
-
 MOCHITEST_CHROME_MANIFESTS += ['tests/mochitest/chrome.ini']
 XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
--- a/toolkit/devtools/server/main.js
+++ b/toolkit/devtools/server/main.js
@@ -147,40 +147,28 @@ function ModuleAPI() {
 
 /***
  * Public API
  */
 var DebuggerServer = {
   _listener: null,
   _initialized: false,
   _transportInitialized: false,
-  _controller: null,
   xpcInspector: null,
   // Number of currently open TCP connections.
   _socketConnections: 0,
   // Map of global actor names to actor constructors provided by extensions.
   globalActorFactories: {},
   // Map of tab actor names to actor constructors provided by extensions.
   tabActorFactories: {},
 
   LONG_STRING_LENGTH: 10000,
   LONG_STRING_INITIAL_LENGTH: 1000,
   LONG_STRING_READ_LENGTH: 65 * 1024,
 
-  get controller() {
-    if (!this._controller) {
-      let cid = "@mozilla.org/devtools/DebuggerServerController;1";
-      if (cid in Cc) {
-        this._controller = Cc[cid].createInstance(Ci.nsIDebuggerServerController);
-        this._controller.init(this);
-      }
-    }
-    return this._controller;
-  },
-
   /**
    * A handler function that prompts the user to accept or decline the incoming
    * connection.
    */
   _allowConnection: null,
 
   /**
    * The windowtype of the chrome window to use for actors that use the global
@@ -257,23 +245,16 @@ var DebuggerServer = {
     this._allowConnection = aAllowConnectionCallback ?
                             aAllowConnectionCallback :
                             this._defaultAllowConnection;
   },
 
   get initialized() this._initialized,
 
   /**
-   * Returns true if at least one connection is active.
-   */
-  isSocketConnected: function DS_isSocketConnected() {
-    return this._connections && Object.keys(this._connections).length > 0;
-  },
-
-  /**
    * Performs cleanup tasks before shutting down the debugger server. Such tasks
    * include clearing any actor constructors added at runtime. This method
    * should be called whenever a debugger server is no longer useful, to avoid
    * memory leaks. After this method returns, the debugger server must be
    * initialized again before use.
    */
   destroy: function DS_destroy() {
     if (!this._initialized) {
@@ -442,16 +423,19 @@ var DebuggerServer = {
   /**
    * Listens on the given port or socket file for remote debugger connections.
    *
    * @param aPortOrPath int, string
    *        If given an integer, the port to listen on.
    *        Otherwise, the path to the unix socket domain file to listen on.
    */
   openListener: function DS_openListener(aPortOrPath) {
+    if (!Services.prefs.getBoolPref("devtools.debugger.remote-enabled")) {
+      return false;
+    }
     this._checkInit();
 
     // Return early if the server is already listening.
     if (this._listener) {
       return true;
     }
 
     let flags = Ci.nsIServerSocket.KeepWhenOffline;
@@ -475,17 +459,16 @@ var DebuggerServer = {
       socket.asyncListen(this);
       this._listener = socket;
     } catch (e) {
       dumpn("Could not start debugging listener on '" + aPortOrPath + "': " + e);
       throw Cr.NS_ERROR_NOT_AVAILABLE;
     }
     this._socketConnections++;
 
-    Services.obs.notifyObservers(null, "debugger-server-started", aPortOrPath);
     return true;
   },
 
   /**
    * Close a previously-opened TCP listener.
    *
    * @param aForce boolean [optional]
    *        If set to true, then the socket will be closed, regardless of the
@@ -497,17 +480,16 @@ var DebuggerServer = {
     }
 
     // Only close the listener when the last connection is closed, or if the
     // aForce flag is passed.
     if (--this._socketConnections == 0 || aForce) {
       this._listener.close();
       this._listener = null;
       this._socketConnections = 0;
-      Services.obs.notifyObservers(null, "debugger-server-stopped", null);
     }
 
     return true;
   },
 
   /**
    * Creates a new connection to the local debugger speaking over a fake
    * transport. This connection results in straightforward calls to the onPacket
--- a/toolkit/devtools/server/moz.build
+++ b/toolkit/devtools/server/moz.build
@@ -4,17 +4,16 @@
 # 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/.
 
 BROWSER_CHROME_MANIFESTS += ['tests/browser/browser.ini']
 MOCHITEST_CHROME_MANIFESTS += ['tests/mochitest/chrome.ini']
 XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
 
 XPIDL_SOURCES += [
-    'nsIDebuggerServerController.idl',
     'nsIJSInspector.idl',
 ]
 
 XPIDL_MODULE = 'jsinspector'
 
 SOURCES += [
     'nsJSInspector.cpp',
 ]
deleted file mode 100644
--- a/toolkit/devtools/server/nsIDebuggerServerController.idl
+++ /dev/null
@@ -1,13 +0,0 @@
-/* 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"
-
-[scriptable, uuid(8ac35a92-0741-4ec8-9444-774b9ba19a63)]
-interface nsIDebuggerServerController: nsISupports
-{
-  void init(in jsval debugger);
-  void start(in string portOrPath);
-  void stop();
-};
--- a/toolkit/devtools/server/tests/unit/head_dbg.js
+++ b/toolkit/devtools/server/tests/unit/head_dbg.js
@@ -11,16 +11,18 @@ const { devtools } = Cu.import("resource
 const Services = devtools.require("Services");
 const { ActorPool, createExtraActors, appendExtraActors } = devtools.require("devtools/server/actors/common");
 const DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils.js");
 const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
 
 // Always log packets when running tests. runxpcshelltests.py will throw
 // the output away anyway, unless you give it the --verbose flag.
 Services.prefs.setBoolPref("devtools.debugger.log", true);
+// Enable remote debugging for the relevant tests.
+Services.prefs.setBoolPref("devtools.debugger.remote-enabled", true);
 
 function tryImport(url) {
   try {
     Cu.import(url);
   } catch (e) {
     dump("Error importing " + url + "\n");
     dump(DevToolsUtils.safeErrorString(e) + "\n");
     throw e;
--- a/toolkit/locales/en-US/chrome/global/devtools/debugger.properties
+++ b/toolkit/locales/en-US/chrome/global/devtools/debugger.properties
@@ -17,19 +17,8 @@ remoteIncomingPromptTitle=Incoming Conne
 # LOCALIZATION NOTE (remoteIncomingPromptMessage): The message displayed on the
 # dialog that prompts the user to allow the incoming connection.
 remoteIncomingPromptMessage=An incoming request to permit remote debugging connection was detected. A remote client can take complete control over your browser! Allow connection?
 
 # LOCALIZATION NOTE (remoteIncomingPromptDisable): The label displayed on the
 # third button in the incoming connection dialog that lets the user disable the
 # remote debugger server.
 remoteIncomingPromptDisable=Disable
-
-# LOCALIZATION NOTE (debuggerStarted.*): The message is displayed in a system
-# notification box when the debugger is started. %S is the port or path the
-# server is listening to.
-debuggerStartedAlert.title=Debugger Server now running
-debuggerStartedAlert.detailPort=Listening on port %S
-debuggerStartedAlert.detailPath=Listening on %S
-
-# LOCALIZATION NOTE (debuggerStopped.title): The message is displayed in a system
-# notification box when the debugger is stopped.
-debuggerStopped.title=Debugger Server stopped
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -29,16 +29,17 @@ const PREF_DISCOVERURL = "extensions.web
 const PREF_DISCOVER_ENABLED = "extensions.getAddons.showPane";
 const PREF_XPI_ENABLED = "xpinstall.enabled";
 const PREF_MAXRESULTS = "extensions.getAddons.maxResults";
 const PREF_GETADDONS_CACHE_ENABLED = "extensions.getAddons.cache.enabled";
 const PREF_GETADDONS_CACHE_ID_ENABLED = "extensions.%ID%.getAddons.cache.enabled";
 const PREF_UI_TYPE_HIDDEN = "extensions.ui.%TYPE%.hidden";
 const PREF_UI_LASTCATEGORY = "extensions.ui.lastCategory";
 const PREF_ADDON_DEBUGGING_ENABLED = "devtools.chrome.enabled";
+const PREF_REMOTE_DEBUGGING_ENABLED = "devtools.debugger.remote-enabled";
 
 const LOADING_MSG_DELAY = 100;
 
 const SEARCH_SCORE_MULTIPLIER_NAME = 2;
 const SEARCH_SCORE_MULTIPLIER_DESCRIPTION = 2;
 
 // Use integers so search scores are sortable by nsIXULSortService
 const SEARCH_SCORE_MATCH_WHOLEWORD = 10;
@@ -148,16 +149,17 @@ function initialize(event) {
   if ("arguments" in window && window.arguments.length > 0 &&
       window.arguments[0] !== null && "view" in window.arguments[0]) {
     view = window.arguments[0].view;
   }
 
   gViewController.loadInitialView(view);
 
   Services.prefs.addObserver(PREF_ADDON_DEBUGGING_ENABLED, debuggingPrefChanged, false);
+  Services.prefs.addObserver(PREF_REMOTE_DEBUGGING_ENABLED, debuggingPrefChanged, false);
 }
 
 function notifyInitialized() {
   if (!gIsInitializing)
     return;
 
   gPendingInitializations--;
   if (!gIsInitializing) {
@@ -169,16 +171,17 @@ function notifyInitialized() {
 
 function shutdown() {
   gCategories.shutdown();
   gSearchView.shutdown();
   gEventManager.shutdown();
   gViewController.shutdown();
   Services.obs.removeObserver(sendEMPong, "EM-ping");
   Services.prefs.removeObserver(PREF_ADDON_DEBUGGING_ENABLED, debuggingPrefChanged);
+  Services.prefs.removeObserver(PREF_REMOTE_DEBUGGING_ENABLED, debuggingPrefChanged);
 }
 
 function sendEMPong(aSubject, aTopic, aData) {
   Services.obs.notifyObservers(window, "EM-pong", "");
 }
 
 // Used by external callers to load a specific view into the manager
 function loadView(aViewId) {
@@ -1006,17 +1009,19 @@ var gViewController = {
     cmd_debugItem: {
       doCommand: function cmd_debugItem_doCommand(aAddon) {
         BrowserToolboxProcess.init({ addonID: aAddon.id });
       },
 
       isEnabled: function cmd_debugItem_isEnabled(aAddon) {
         let debuggerEnabled = Services.prefs.
                               getBoolPref(PREF_ADDON_DEBUGGING_ENABLED);
-        return aAddon && aAddon.isDebuggable && debuggerEnabled;
+        let remoteEnabled = Services.prefs.
+                            getBoolPref(PREF_REMOTE_DEBUGGING_ENABLED);
+        return aAddon && aAddon.isDebuggable && debuggerEnabled && remoteEnabled;
       }
     },
 
     cmd_showItemPreferences: {
       isEnabled: function cmd_showItemPreferences_isEnabled(aAddon) {
         if (!aAddon || !aAddon.isActive || !aAddon.optionsURL)
           return false;
         if (gViewController.currentViewObj == gDetailView &&
--- a/toolkit/mozapps/extensions/content/extensions.xml
+++ b/toolkit/mozapps/extensions/content/extensions.xml
@@ -1353,17 +1353,18 @@
 
           this.setAttribute("active", this.mAddon.isActive);
 
           var showProgress = this.mAddon.purchaseURL || (this.mAddon.install &&
                              this.mAddon.install.state != AddonManager.STATE_INSTALLED);
           this._showStatus(showProgress ? "progress" : "none");
 
           let debuggable = this.mAddon.isDebuggable &&
-                           Services.prefs.getBoolPref('devtools.chrome.enabled');
+                           Services.prefs.getBoolPref('devtools.chrome.enabled') &&
+                           Services.prefs.getBoolPref('devtools.debugger.remote-enabled');
 
           this._debugBtn.disabled = this._debugBtn.hidden = !debuggable
 
           if (this.mAddon.type == "experiment") {
             let prefix = "experiment.";
             let active = this.mAddon.isActive;
 
             let stateKey = prefix + "state." + (active ? "active" : "complete");
--- a/toolkit/mozapps/extensions/test/browser/browser_debug_button.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_debug_button.js
@@ -8,19 +8,18 @@
 
 let { Promise } = Components.utils.import("resource://gre/modules/Promise.jsm", {});
 let { Task } = Components.utils.import("resource://gre/modules/Task.jsm", {});
 
 const getDebugButton = node =>
     node.ownerDocument.getAnonymousElementByAttribute(node, "anonid", "debug-btn");
 const addonDebuggingEnabled = bool =>
   Services.prefs.setBoolPref("devtools.chrome.enabled", !!bool);
-
-Services.prefs.setBoolPref("devtools.debugger.show-server-notifications", false);
-
+const remoteDebuggingEnabled = bool =>
+  Services.prefs.setBoolPref("devtools.debugger.remote-enabled", !!bool);
 
 function test() {
   requestLongerTimeout(2);
 
   waitForExplicitFinish();
 
 
   var gProvider = new MockProvider();
@@ -33,42 +32,71 @@ function test() {
     id: "debuggable@tests.mozilla.org",
     name: "Debuggable",
     description: "bar",
     isDebuggable: true
   }]);
 
   Task.spawn(function* () {
     addonDebuggingEnabled(false);
+    remoteDebuggingEnabled(false);
 
     yield testDOM((nondebug, debuggable) => {
       is(nondebug.disabled, true,
         "addon:disabled::remote:disabled button is disabled for legacy addons");
       is(nondebug.hidden, true,
         "addon:disabled::remote:disabled button is hidden for legacy addons");
       is(debuggable.disabled, true,
         "addon:disabled::remote:disabled button is disabled for debuggable addons");
       is(debuggable.hidden, true,
         "addon:disabled::remote:disabled button is hidden for debuggable addons");
     });
     
     addonDebuggingEnabled(true);
+    remoteDebuggingEnabled(false);
+
+    yield testDOM((nondebug, debuggable) => {
+      is(nondebug.disabled, true,
+        "addon:enabled::remote:disabled button is disabled for legacy addons");
+      is(nondebug.disabled, true,
+        "addon:enabled::remote:disabled button is hidden for legacy addons");
+      is(debuggable.disabled, true,
+        "addon:enabled::remote:disabled button is disabled for debuggable addons");
+      is(debuggable.disabled, true,
+        "addon:enabled::remote:disabled button is hidden for debuggable addons");
+    });
+    
+    addonDebuggingEnabled(false);
+    remoteDebuggingEnabled(true);
+
+    yield testDOM((nondebug, debuggable) => {
+      is(nondebug.disabled, true,
+        "addon:disabled::remote:enabled button is disabled for legacy addons");
+      is(nondebug.disabled, true,
+        "addon:disabled::remote:enabled button is hidden for legacy addons");
+      is(debuggable.disabled, true,
+        "addon:disabled::remote:enabled button is disabled for debuggable addons");
+      is(debuggable.disabled, true,
+        "addon:disabled::remote:enabled button is hidden for debuggable addons");
+    });
+    
+    addonDebuggingEnabled(true);
+    remoteDebuggingEnabled(true);
 
     yield testDOM((nondebug, debuggable) => {
       is(nondebug.disabled, true,
         "addon:enabled::remote:enabled button is disabled for legacy addons");
       is(nondebug.disabled, true,
         "addon:enabled::remote:enabled button is hidden for legacy addons");
       is(debuggable.disabled, false,
         "addon:enabled::remote:enabled button is enabled for debuggable addons");
       is(debuggable.hidden, false,
         "addon:enabled::remote:enabled button is visible for debuggable addons");
     });
 
-    Services.prefs.clearUserPref("devtools.debugger.show-server-notifications");
     finish();
   });
 
   function testDOM (testCallback) {
     let deferred = Promise.defer();
     open_manager("addons://list/extension", function(aManager) {
       const {document} = aManager;
       const addonList = document.getElementById("addon-list");
--- a/webapprt/prefs.js
+++ b/webapprt/prefs.js
@@ -71,16 +71,17 @@ pref("dom.payment.provider.0.type", "moz
 pref("dom.payment.provider.0.requestMethod", "GET");
 #endif
 
 // Enable window resize and move
 pref("dom.always_allow_move_resize_window", true);
 
 pref("plugin.allowed_types", "application/x-shockwave-flash,application/futuresplash");
 
+pref("devtools.debugger.remote-enabled", true);
 pref("devtools.debugger.force-local", true);
 
 // The default for this pref reflects whether the build is capable of IPC.
 // (Turning it on in a no-IPC build will have no effect.)
 #ifdef XP_MACOSX
 // i386 ipc preferences
 pref("dom.ipc.plugins.enabled.i386", false);
 pref("dom.ipc.plugins.enabled.i386.flash player.plugin", true);