merge m-c to fx-team
authorTim Taubert <tim.taubert@gmx.de>
Tue, 08 May 2012 08:53:50 +0200
changeset 93455 1092c1a3ac50e7d13571b8f4fef352f01d527676
parent 93445 fafd873d469c75a1d540a47f7f28f9aa0a065ed2 (current diff)
parent 93454 d0dc53efd7dd4602bd0af3b5ad3668b0a01a262d (diff)
child 93456 e4f9e2eab6b16eda0d84aea52b2685386dc6fa30
child 93755 6d6e0475b348e27c723629785204d05808c85261
push id9156
push useremorley@mozilla.com
push dateTue, 08 May 2012 10:57:11 +0000
treeherdermozilla-inbound@4ab51d9e902b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone15.0a1
first release with
nightly linux32
1092c1a3ac50 / 15.0a1 / 20120508030517 / files
nightly linux64
1092c1a3ac50 / 15.0a1 / 20120508030517 / files
nightly mac
1092c1a3ac50 / 15.0a1 / 20120508030517 / files
nightly win32
1092c1a3ac50 / 15.0a1 / 20120508030517 / files
nightly win64
1092c1a3ac50 / 15.0a1 / 20120508030517 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge m-c to fx-team
browser/themes/gnomestripe/browser.css
browser/themes/pinstripe/browser.css
browser/themes/winstripe/browser.css
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1050,16 +1050,19 @@ pref("devtools.inspector.activeSidebar",
 pref("devtools.layoutview.enabled", false);
 pref("devtools.layoutview.open", false);
 
 // Enable the Debugger
 pref("devtools.debugger.enabled", false);
 pref("devtools.debugger.remote-enabled", false);
 pref("devtools.debugger.remote-host", "localhost");
 pref("devtools.debugger.remote-port", 6000);
+pref("devtools.debugger.remote-autoconnect", false);
+pref("devtools.debugger.remote-connection-retries", 3);
+pref("devtools.debugger.remote-timeout", 3000);
 
 // The default Debugger UI height
 pref("devtools.debugger.ui.height", 250);
 pref("devtools.debugger.ui.remote-win.width", 900);
 pref("devtools.debugger.ui.remote-win.height", 400);
 
 // Enable the style inspector
 pref("devtools.styleinspector.enabled", true);
--- a/browser/devtools/debugger/DebuggerUI.jsm
+++ b/browser/devtools/debugger/DebuggerUI.jsm
@@ -41,16 +41,17 @@
  * ***** END LICENSE BLOCK ***** */
 "use strict";
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 const DBG_XUL = "chrome://browser/content/debugger.xul";
+const DBG_STRINGS_URI = "chrome://browser/locale/devtools/debugger.properties";
 const REMOTE_PROFILE_NAME = "_remote-debug";
 
 Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 let EXPORTED_SYMBOLS = ["DebuggerUI"];
@@ -77,71 +78,89 @@ DebuggerUI.prototype = {
     if (tab._scriptDebugger) {
       tab._scriptDebugger.close();
       return null;
     }
     return new DebuggerPane(tab);
   },
 
   /**
-   * Starts a remote debugger in a new process, or stops it if already started.
-   * @see DebuggerProcess.constructor
-   * @return DebuggerProcess if the debugger is started, null if it's stopped.
+   * Starts a remote debugger in a new window, or stops it if already started.
+   * @return RemoteDebuggerWindow if the debugger is started, null if stopped.
    */
-  toggleRemoteDebugger: function DUI_toggleRemoteDebugger(aOnClose, aOnRun) {
+  toggleRemoteDebugger: function DUI_toggleRemoteDebugger() {
     let win = this.chromeWindow;
 
     if (win._remoteDebugger) {
       win._remoteDebugger.close();
       return null;
     }
-    return new DebuggerProcess(win, aOnClose, aOnRun);
+    return new RemoteDebuggerWindow(this);
   },
 
   /**
    * Starts a chrome debugger in a new process, or stops it if already started.
-   * @see DebuggerProcess.constructor
-   * @return DebuggerProcess if the debugger is started, null if it's stopped.
+   * @return ChromeDebuggerProcess if the debugger is started, null if stopped.
    */
   toggleChromeDebugger: function DUI_toggleChromeDebugger(aOnClose, aOnRun) {
     let win = this.chromeWindow;
 
     if (win._chromeDebugger) {
       win._chromeDebugger.close();
       return null;
     }
-    return new DebuggerProcess(win, aOnClose, aOnRun, true);
+    return new ChromeDebuggerProcess(win, aOnClose, aOnRun, true);
   },
 
   /**
    * Get the debugger for a specified tab.
-   * @return DebuggerPane if a debugger exists for the tab, null otherwise
+   * @return DebuggerPane if a debugger exists for the tab, null otherwise.
    */
   getDebugger: function DUI_getDebugger(aTab) {
     return aTab._scriptDebugger;
   },
 
   /**
+   * Get the remote debugger for the current chrome window.
+   * @return RemoteDebuggerWindow if a remote debugger exists, null otherwise.
+   */
+  getRemoteDebugger: function DUI_getRemoteDebugger() {
+    let win = this.chromeWindow;
+    return '_remoteDebugger' in win ? win._remoteDebugger : null;
+  },
+
+  /**
+   * Get the chrome debugger for the current firefox instance.
+   * @return ChromeDebuggerProcess if a chrome debugger exists, null otherwise.
+   */
+  getChromeDebugger: function DUI_getChromeDebugger() {
+    let win = this.chromeWindow;
+    return '_chromeDebugger' in win ? win._chromeDebugger : null;
+  },
+
+  /**
    * Get the preferences associated with the debugger frontend.
    * @return object
    */
   get preferences() {
     return DebuggerPreferences;
   }
 };
 
 /**
  * Creates a pane that will host the debugger.
  *
+ * @param DebuggerUI aDebuggerUI
+ *        The parent instance creating the new debugger.
  * @param XULElement aTab
  *        The tab in which to create the debugger.
  */
 function DebuggerPane(aTab) {
   this._tab = aTab;
-  
+
   this._initServer();
   this._create();
 }
 
 DebuggerPane.prototype = {
 
   /**
    * Initializes the debugger server.
@@ -176,17 +195,17 @@ DebuggerPane.prototype = {
     let self = this;
 
     this._frame.addEventListener("Debugger:Loaded", function dbgLoaded() {
       self._frame.removeEventListener("Debugger:Loaded", dbgLoaded, true);
       self._frame.addEventListener("Debugger:Close", self.close, true);
       self._frame.addEventListener("unload", self.close, true);
 
       // Bind shortcuts for accessing the breakpoint methods in the debugger.
-      let bkp = self.debuggerWindow.DebuggerController.Breakpoints;
+      let bkp = self.contentWindow.DebuggerController.Breakpoints;
       self.addBreakpoint = bkp.addBreakpoint;
       self.removeBreakpoint = bkp.removeBreakpoint;
       self.getBreakpoint = bkp.getBreakpoint;
     }, true);
 
     this._frame.setAttribute("src", DBG_XUL);
   },
 
@@ -211,57 +230,134 @@ DebuggerPane.prototype = {
     this._frame = null;
     this._nbox = null;
   },
 
   /**
    * Gets the debugger content window.
    * @return nsIDOMWindow if a debugger window exists, null otherwise
    */
-  get debuggerWindow() {
+  get contentWindow() {
     return this._frame ? this._frame.contentWindow : null;
   },
 
   /**
    * Shortcut for accessing the list of breakpoints in the debugger.
    * @return object if a debugger window exists, null otherwise
    */
   get breakpoints() {
-    let debuggerWindow = this.debuggerWindow;
-    if (debuggerWindow) {
-      return debuggerWindow.DebuggerController.Breakpoints.store;
+    let contentWindow = this.contentWindow;
+    if (contentWindow) {
+      return contentWindow.DebuggerController.Breakpoints.store;
     }
     return null;
   }
 };
 
 /**
- * Creates a process that will hold the remote debugger.
+ * Creates a window that will host a remote debugger.
+ *
+ * @param DebuggerUI aDebuggerUI
+ *        The parent instance creating the new debugger.
+ */
+function RemoteDebuggerWindow(aDebuggerUI) {
+  this._globalUI = aDebuggerUI;
+  this._win = aDebuggerUI.chromeWindow;
+
+  this._create();
+}
+
+RemoteDebuggerWindow.prototype = {
+
+  /**
+   * Creates and initializes the widgets containing the remote debugger UI.
+   */
+  _create: function DP__create() {
+    this._win._remoteDebugger = this;
+
+    this._dbgwin = this._globalUI.chromeWindow.open(DBG_XUL,
+      L10N.getStr("remoteDebuggerWindowTitle"),
+      "width=" + DebuggerPreferences.remoteWinWidth + "," +
+      "height=" + DebuggerPreferences.remoteWinHeight + "," +
+      "chrome,dependent,resizable,centerscreen");
+
+    this._dbgwin._remoteFlag = true;
+
+    this.close = this.close.bind(this);
+    let self = this;
+
+    this._dbgwin.addEventListener("Debugger:Loaded", function dbgLoaded() {
+      self._dbgwin.removeEventListener("Debugger:Loaded", dbgLoaded, true);
+      self._dbgwin.addEventListener("Debugger:Close", self.close, true);
+      self._dbgwin.addEventListener("unload", self.close, true);
+
+      // Bind shortcuts for accessing the breakpoint methods in the debugger.
+      let bkp = self.contentWindow.DebuggerController.Breakpoints;
+      self.addBreakpoint = bkp.addBreakpoint;
+      self.removeBreakpoint = bkp.removeBreakpoint;
+      self.getBreakpoint = bkp.getBreakpoint;
+    }, true);
+  },
+
+  /**
+   * Closes the remote debugger, along with the parent window if necessary.
+   */
+  close: function DP_close() {
+    if (!this._win) {
+      return;
+    }
+    delete this._win._remoteDebugger;
+    this._win = null;
+
+    this._dbgwin.close();
+    this._dbgwin = null;
+  },
+
+  /**
+   * Gets the remote debugger content window.
+   * @return nsIDOMWindow if a debugger window exists, null otherwise.
+   */
+  get contentWindow() {
+    return this._dbgwin;
+  },
+
+  /**
+   * Shortcut for accessing the list of breakpoints in the remote debugger.
+   * @return object if a debugger window exists, null otherwise.
+   */
+  get breakpoints() {
+    let contentWindow = this.contentWindow;
+    if (contentWindow) {
+      return contentWindow.DebuggerController.Breakpoints.store;
+    }
+    return null;
+  }
+};
+
+/**
+ * Creates a process that will hold a chrome debugger.
  *
  * @param function aOnClose
  *        Optional, a function called when the process exits.
  * @param function aOnRun
  *        Optional, a function called when the process starts running.
- * @param boolean aInitServerFlag
- *        True to initialize the server. This should happen only in the chrome
- *        debugging case. This should also be true by default after bug #747429.
  * @param nsIDOMWindow aWindow
- *        The chrome window for which the remote debugger instance is created.
+ *        The chrome window for which the debugger instance is created.
  */
-function DebuggerProcess(aWindow, aOnClose, aOnRun, aInitServerFlag) {
+function ChromeDebuggerProcess(aWindow, aOnClose, aOnRun) {
   this._win = aWindow;
   this._closeCallback = aOnClose;
   this._runCallback = aOnRun;
 
-  aInitServerFlag && this._initServer();
+  this._initServer();
   this._initProfile();
   this._create();
 }
 
-DebuggerProcess.prototype = {
+ChromeDebuggerProcess.prototype = {
 
   /**
    * Initializes the debugger server.
    */
   _initServer: function RDP__initServer() {
     if (!DebuggerServer.initialized) {
       DebuggerServer.init();
       DebuggerServer.addBrowserActors();
@@ -288,17 +384,17 @@ DebuggerProcess.prototype = {
     this._dbgProfile = profileService.createProfile(null, null, dbgProfileName);
     profileService.flush();
   },
 
   /**
    * Creates and initializes the profile & process for the remote debugger.
    */
   _create: function RDP__create() {
-    this._win._remoteDebugger = this;
+    this._win._chromeDebugger = this;
 
     let file = FileUtils.getFile("CurProcD",
       [Services.appinfo.OS == "WINNT" ? "firefox.exe"
                                       : "firefox-bin"]);
 
     let process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
     process.init(file);
 
@@ -318,17 +414,17 @@ DebuggerProcess.prototype = {
 
   /**
    * Closes the remote debugger, removing the profile and killing the process.
    */
   close: function RDP_close() {
     if (!this._win) {
       return;
     }
-    delete this._win._remoteDebugger;
+    delete this._win._chromeDebugger;
     this._win = null;
 
     if (this._dbgProcess.isRunning) {
       this._dbgProcess.kill();
     }
     if (this._dbgProfile) {
       this._dbgProfile.remove(false);
     }
@@ -337,16 +433,36 @@ DebuggerProcess.prototype = {
     }
 
     this._dbgProcess = null;
     this._dbgProfile = null;
   }
 };
 
 /**
+ * Localization convenience methods.
+ */
+let L10N = {
+
+  /**
+   * L10N shortcut function.
+   *
+   * @param string aName
+   * @return string
+   */
+  getStr: function L10N_getStr(aName) {
+    return this.stringBundle.GetStringFromName(aName);
+  }
+};
+
+XPCOMUtils.defineLazyGetter(L10N, "stringBundle", function() {
+  return Services.strings.createBundle(DBG_STRINGS_URI);
+});
+
+/**
  * Various debugger preferences.
  */
 let DebuggerPreferences = {
 
   /**
    * Gets the preferred height of the debugger pane.
    * @return number
    */
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -82,16 +82,17 @@ let DebuggerController = {
     }
     this._isInitialized = true;
     window.removeEventListener("DOMContentLoaded", this._startupDebugger, true);
 
     DebuggerView.initializeEditor();
     DebuggerView.StackFrames.initialize();
     DebuggerView.Properties.initialize();
     DebuggerView.Scripts.initialize();
+    DebuggerView.showCloseButton(!this._isRemoteDebugger && !this._isChromeDebugger);
 
     this.dispatchEvent("Debugger:Loaded");
     this._connect();
   },
 
   /**
    * Destroys the debugger view, disconnects the debugger client and cleans up
    * any active listeners.
@@ -109,27 +110,74 @@ let DebuggerController = {
     DebuggerView.Properties.destroy();
 
     DebuggerController.SourceScripts.disconnect();
     DebuggerController.StackFrames.disconnect();
     DebuggerController.ThreadState.disconnect();
 
     this.dispatchEvent("Debugger:Unloaded");
     this._disconnect();
-    this._isRemote && this._quitApp();
+    this._isChromeDebugger && this._quitApp();
+  },
+
+  /**
+   * Prepares the hostname and port number for a remote debugger connection
+   * and handles connection retries and timeouts.
+   *
+   * @return boolean true if connection should proceed normally
+   */
+  _prepareConnection: function DC__prepareConnection() {
+    // If we exceeded the total number of connection retries, bail.
+    if (this._remoteConnectionTry === Prefs.remoteConnectionRetries) {
+      Services.prompt.alert(null,
+        L10N.getStr("remoteDebuggerPromptTitle"),
+        L10N.getStr("remoteDebuggerConnectionFailedMessage"));
+      this.dispatchEvent("Debugger:Close");
+      return false;
+    }
+
+    // TODO: This is ugly, need to rethink the design for the UI in #751677.
+    if (!Prefs.remoteAutoConnect) {
+      let prompt = new RemoteDebuggerPrompt();
+      let result = prompt.show(!!this._remoteConnectionTimeout);
+      if (!result) {
+        this.dispatchEvent("Debugger:Close");
+        return false;
+      }
+      Prefs.remoteHost = prompt.uri.host;
+      Prefs.remotePort = prompt.uri.port;
+    }
+
+    // If this debugger is connecting remotely to a server, we need to check
+    // after a while if the connection actually succeeded.
+    this._remoteConnectionTry = ++this._remoteConnectionTry || 1;
+    this._remoteConnectionTimeout = window.setTimeout(function() {
+      // If we couldn't connect to any server yet, try again...
+      if (!DebuggerController.activeThread) {
+        DebuggerController._connect();
+      }
+    }, Prefs.remoteTimeout);
+
+    return true;
   },
 
   /**
    * Initializes a debugger client and connects it to the debugger server,
    * wiring event handlers as necessary.
    */
   _connect: function DC__connect() {
-    let transport =
-      this._isRemote ? debuggerSocketConnect(Prefs.remoteHost, Prefs.remotePort)
-                     : DebuggerServer.connectPipe();
+    if (this._isRemoteDebugger) {
+      if (!this._prepareConnection()) {
+        return;
+      }
+    }
+
+    let transport = (this._isChromeDebugger || this._isRemoteDebugger)
+      ? debuggerSocketConnect(Prefs.remoteHost, Prefs.remotePort)
+      : DebuggerServer.connectPipe();
 
     let client = this.client = new DebuggerClient(transport);
 
     client.addListener("tabNavigated", this._onTabNavigated);
     client.addListener("tabDetached", this._onTabDetached);
 
     client.connect(function(aType, aTraits) {
       client.listTabs(function(aResponse) {
@@ -218,18 +266,26 @@ let DebuggerController = {
       }.bind(this));
     }.bind(this));
   },
 
   /**
    * Returns true if this is a remote debugger instance.
    * @return boolean
    */
-  get _isRemote() {
-    return !window.parent.content;
+  get _isRemoteDebugger() {
+    return window._remoteFlag;
+  },
+
+  /**
+   * Returns true if this is a chrome debugger instance.
+   * @return boolean
+   */
+  get _isChromeDebugger() {
+    return !window.parent.content && !this._isRemoteDebugger;
   },
 
   /**
    * Attempts to quit the current process if allowed.
    */
   _quitApp: function DC__quitApp() {
     let canceled = Cc["@mozilla.org/supports-PRBool;1"]
       .createInstance(Ci.nsISupportsPRBool);
@@ -1320,17 +1376,39 @@ let L10N = {
 
 XPCOMUtils.defineLazyGetter(L10N, "stringBundle", function() {
   return Services.strings.createBundle(DBG_STRINGS_URI);
 });
 
 /**
  * Shortcuts for accessing various debugger preferences.
  */
-let Prefs = {};
+let Prefs = {
+
+  /**
+   * Gets a flag specifying if the the debugger should automatically connect to
+   * the default host and port number.
+   * @return boolean
+   */
+  get remoteAutoConnect() {
+    if (this._autoConn === undefined) {
+      this._autoConn = Services.prefs.getBoolPref("devtools.debugger.remote-autoconnect");
+    }
+    return this._autoConn;
+  },
+
+  /**
+   * Sets a flag specifying if the the debugger should automatically connect.
+   * @param boolean value
+   */
+  set remoteAutoConnect(value) {
+    Services.prefs.setBoolPref("devtools.debugger.remote-autoconnect", value);
+    this._autoConn = value;
+  }
+};
 
 /**
  * Gets the preferred default remote debugging host.
  * @return string
  */
 XPCOMUtils.defineLazyGetter(Prefs, "remoteHost", function() {
   return Services.prefs.getCharPref("devtools.debugger.remote-host");
 });
@@ -1339,16 +1417,32 @@ XPCOMUtils.defineLazyGetter(Prefs, "remo
  * Gets the preferred default remote debugging port.
  * @return number
  */
 XPCOMUtils.defineLazyGetter(Prefs, "remotePort", function() {
   return Services.prefs.getIntPref("devtools.debugger.remote-port");
 });
 
 /**
+ * Gets the max number of attempts to reconnect to a remote server.
+ * @return number
+ */
+XPCOMUtils.defineLazyGetter(Prefs, "remoteConnectionRetries", function() {
+  return Services.prefs.getIntPref("devtools.debugger.remote-connection-retries");
+});
+
+/**
+ * Gets the remote debugging connection timeout (in milliseconds).
+ * @return number
+ */
+XPCOMUtils.defineLazyGetter(Prefs, "remoteTimeout", function() {
+  return Services.prefs.getIntPref("devtools.debugger.remote-timeout");
+});
+
+/**
  * Preliminary setup for the DebuggerController object.
  */
 DebuggerController.init();
 DebuggerController.ThreadState = new ThreadState();
 DebuggerController.StackFrames = new StackFrames();
 DebuggerController.SourceScripts = new SourceScripts();
 DebuggerController.Breakpoints = new Breakpoints();
 
--- a/browser/devtools/debugger/debugger-view.js
+++ b/browser/devtools/debugger/debugger-view.js
@@ -77,16 +77,71 @@ let DebuggerView = {
   },
 
   /**
    * The load event handler for the source editor. This method does post-load
    * editor initialization.
    */
   _onEditorLoad: function DV__onEditorLoad() {
     DebuggerController.Breakpoints.initialize();
+  },
+
+  /**
+   * Sets the close button hidden or visible. It's hidden by default.
+   * @param boolean aVisibleFlag
+   */
+  showCloseButton: function DV_showCloseButton(aVisibleFlag) {
+    document.getElementById("close").setAttribute("hidden", !aVisibleFlag);
+  }
+};
+
+/**
+ * A simple way of displaying a "Connect to..." prompt.
+ */
+function RemoteDebuggerPrompt() {
+
+  /**
+   * The remote uri the user wants to connect to.
+   */
+  this.uri = null;
+}
+
+RemoteDebuggerPrompt.prototype = {
+
+  /**
+   * Shows the prompt and sets the uri using the user input.
+   *
+   * @param boolean aIsReconnectingFlag
+   *                True to show the reconnect message instead.
+   */
+  show: function RDP_show(aIsReconnectingFlag) {
+    let check = { value: Prefs.remoteAutoConnect };
+    let input = { value: "http://" + Prefs.remoteHost +
+                               ":" + Prefs.remotePort + "/" };
+
+    while (true) {
+      let result = Services.prompt.prompt(null,
+        L10N.getStr("remoteDebuggerPromptTitle"),
+        L10N.getStr(aIsReconnectingFlag
+          ? "remoteDebuggerReconnectMessage"
+          : "remoteDebuggerPromptMessage"), input,
+        L10N.getStr("remoteDebuggerPromptCheck"), check);
+
+      Prefs.remoteAutoConnect = check.value;
+
+      try {
+        let uri = Services.io.newURI(input.value, null, null);
+        let url = uri.QueryInterface(Ci.nsIURL);
+
+        // If a url could be successfully retrieved, then the uri is correct.
+        this.uri = uri;
+        return result;
+      }
+      catch(e) { }
+    }
   }
 };
 
 /**
  * Functions handling the scripts UI.
  */
 function ScriptsView() {
   this._onScriptsChange = this._onScriptsChange.bind(this);
--- a/browser/devtools/debugger/debugger.xul
+++ b/browser/devtools/debugger/debugger.xul
@@ -70,17 +70,17 @@
   </xul:popupset>
 
   <xul:commandset id="editMenuCommands"/>
   <xul:commandset id="sourceEditorCommands"/>
   <xul:keyset id="sourceEditorKeys"/>
 
   <div id="body" class="vbox flex">
     <xul:toolbar id="dbg-toolbar">
-      <xul:button id="close">&debuggerUI.closeButton;</xul:button>
+      <xul:button id="close" hidden="false">&debuggerUI.closeButton;</xul:button>
       <xul:button id="resume"/>
       <xul:button id="step-over">&debuggerUI.stepOverButton;</xul:button>
       <xul:button id="step-in">&debuggerUI.stepInButton;</xul:button>
       <xul:button id="step-out">&debuggerUI.stepOutButton;</xul:button>
       <xul:menulist id="scripts"/>
       <xul:textbox id="scripts-search" type="search"
                    emptytext="&debuggerUI.emptyFilterText;"/>
     </xul:toolbar>
--- a/browser/devtools/debugger/test/Makefile.in
+++ b/browser/devtools/debugger/test/Makefile.in
@@ -42,16 +42,17 @@ srcdir          = @srcdir@
 VPATH           = @srcdir@
 relativesrcdir  = browser/devtools/debugger/test
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _BROWSER_TEST_FILES = \
 	browser_dbg_createRemote.js \
+	browser_dbg_createChrome.js \
 	browser_dbg_debuggerstatement.js \
 	browser_dbg_listtabs.js \
 	browser_dbg_tabactor-01.js \
 	browser_dbg_tabactor-02.js \
 	browser_dbg_contextactor-01.js \
 	browser_dbg_contextactor-02.js \
 	testactors.js \
 	browser_dbg_nav-01.js \
--- a/browser/devtools/debugger/test/browser_dbg_bug723069_editor-breakpoints.js
+++ b/browser/devtools/debugger/test/browser_dbg_bug723069_editor-breakpoints.js
@@ -25,17 +25,17 @@ function test()
   let framesAdded = false;
   let resumed = false;
   let testStarted = false;
 
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
-    gDebugger = gPane.debuggerWindow;
+    gDebugger = gPane.contentWindow;
     resumed = true;
 
     gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
       framesAdded = true;
       executeSoon(startTest);
     });
 
     executeSoon(function() {
--- a/browser/devtools/debugger/test/browser_dbg_bug731394_editor-contextmenu.js
+++ b/browser/devtools/debugger/test/browser_dbg_bug731394_editor-contextmenu.js
@@ -23,17 +23,17 @@ function test()
   let framesAdded = false;
   let resumed = false;
   let testStarted = false;
 
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
-    gDebugger = gPane.debuggerWindow;
+    gDebugger = gPane.contentWindow;
     resumed = true;
 
     gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
       framesAdded = true;
       executeSoon(startTest);
     });
 
     executeSoon(function() {
--- a/browser/devtools/debugger/test/browser_dbg_clean-exit.js
+++ b/browser/devtools/debugger/test/browser_dbg_clean-exit.js
@@ -10,17 +10,17 @@ var gTab = null;
 var gDebugger = null;
 
 const DEBUGGER_TAB_URL = EXAMPLE_URL + "browser_dbg_debuggerstatement.html";
 
 function test() {
   debug_tab_pane(DEBUGGER_TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gPane = aPane;
-    gDebugger = gPane.debuggerWindow;
+    gDebugger = gPane.contentWindow;
 
     testCleanExit();
   });
 }
 
 function testCleanExit() {
   gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_createChrome.js
@@ -0,0 +1,89 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that a chrome debugger can be created in a new process.
+
+var gProcess = null;
+var gTab = null;
+var gDebuggee = null;
+
+function test() {
+  debug_chrome(STACK_URL, aOnClosing, function(aTab, aDebuggee, aProcess) {
+    gTab = aTab;
+    gDebuggee = aDebuggee;
+    gProcess = aProcess;
+
+    testSimpleCall();
+  });
+}
+
+function testSimpleCall() {
+  Services.tm.currentThread.dispatch({ run: function() {
+
+    ok(gProcess._dbgProcess,
+      "The remote debugger process wasn't created properly!");
+    ok(gProcess._dbgProcess.isRunning,
+      "The remote debugger process isn't running!");
+    is(typeof gProcess._dbgProcess.pid, "number",
+      "The remote debugger process doesn't have a pid (?!)");
+
+    info("process location: " + gProcess._dbgProcess.location);
+    info("process pid: " + gProcess._dbgProcess.pid);
+    info("process name: " + gProcess._dbgProcess.processName);
+    info("process sig: " + gProcess._dbgProcess.processSignature);
+
+    ok(gProcess._dbgProfile,
+      "The remote debugger profile wasn't created properly!");
+    ok(gProcess._dbgProfile.localDir,
+      "The remote debugger profile doesn't have a localDir...");
+    ok(gProcess._dbgProfile.rootDir,
+      "The remote debugger profile doesn't have a rootDir...");
+    ok(gProcess._dbgProfile.name,
+      "The remote debugger profile doesn't have a name...");
+
+    info("profile localDir: " + gProcess._dbgProfile.localDir);
+    info("profile rootDir: " + gProcess._dbgProfile.rootDir);
+    info("profile name: " + gProcess._dbgProfile.name);
+
+    let profileService = Cc["@mozilla.org/toolkit/profile-service;1"]
+      .createInstance(Ci.nsIToolkitProfileService);
+
+    let profile = profileService.getProfileByName(gProcess._dbgProfile.name);
+
+    ok(profile,
+      "The remote debugger profile wasn't *actually* created properly!");
+    is(profile.localDir.path, gProcess._dbgProfile.localDir.path,
+      "The remote debugger profile doesn't have the correct localDir!");
+    is(profile.rootDir.path, gProcess._dbgProfile.rootDir.path,
+      "The remote debugger profile doesn't have the correct rootDir!");
+
+    DebuggerUI.toggleChromeDebugger();
+  }}, 0);
+}
+
+function aOnClosing() {
+  ok(!gProcess._dbgProcess.isRunning,
+    "The remote debugger process isn't closed as it should be!");
+  is(gProcess._dbgProcess.exitValue, (Services.appinfo.OS == "WINNT" ? 0 : 256),
+    "The remote debugger process didn't die cleanly.");
+
+  info("process exit value: " + gProcess._dbgProcess.exitValue);
+
+  info("profile localDir: " + gProcess._dbgProfile.localDir.path);
+  info("profile rootDir: " + gProcess._dbgProfile.rootDir.path);
+  info("profile name: " + gProcess._dbgProfile.name);
+
+  executeSoon(function() {
+    finish();
+  });
+}
+
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gProcess = null;
+  gTab = null;
+  gDebuggee = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_createRemote.js
+++ b/browser/devtools/debugger/test/browser_dbg_createRemote.js
@@ -1,86 +1,80 @@
-/* vim:set ts=2 sw=2 sts=2 et: */
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
-var gProcess = null;
+
+// Tests that a remote debugger can be created in a new window.
+
+var gWindow = null;
 var gTab = null;
-var gDebuggee = null;
+var gAutoConnect = null;
+
+const TEST_URL = EXAMPLE_URL + "browser_dbg_iframes.html";
 
 function test() {
-  remote_debug_tab_pane(STACK_URL, aOnClosing, function(aTab, aDebuggee, aProcess) {
+  debug_remote(TEST_URL, function(aTab, aDebuggee, aWindow) {
     gTab = aTab;
-    gDebuggee = aDebuggee;
-    gProcess = aProcess;
+    gWindow = aWindow;
+    let gDebugger = gWindow.contentWindow;
 
-    testSimpleCall();
-  });
-}
+    is(gDebugger.document.getElementById("close").getAttribute("hidden"), "true",
+      "The close button should be hidden in a remote debugger.");
 
-function testSimpleCall() {
-  Services.tm.currentThread.dispatch({ run: function() {
+    is(gDebugger.DebuggerController.activeThread.paused, false,
+      "Should be running after debug_remote.");
 
-    ok(gProcess._dbgProcess,
-      "The remote debugger process wasn't created properly!");
-    ok(gProcess._dbgProcess.isRunning,
-      "The remote debugger process isn't running!");
-    is(typeof gProcess._dbgProcess.pid, "number",
-      "The remote debugger process doesn't have a pid (?!)");
+    gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
+      Services.tm.currentThread.dispatch({ run: function() {
+
+        let frames = gDebugger.DebuggerView.StackFrames._frames;
+        let childNodes = frames.childNodes;
 
-    info("process location: " + gProcess._dbgProcess.location);
-    info("process pid: " + gProcess._dbgProcess.pid);
-    info("process name: " + gProcess._dbgProcess.processName);
-    info("process sig: " + gProcess._dbgProcess.processSignature);
+        is(gDebugger.DebuggerController.activeThread.paused, true,
+          "Should be paused after an interrupt request.");
 
-    ok(gProcess._dbgProfile,
-      "The remote debugger profile wasn't created properly!");
-    ok(gProcess._dbgProfile.localDir,
-      "The remote debugger profile doesn't have a localDir...");
-    ok(gProcess._dbgProfile.rootDir,
-      "The remote debugger profile doesn't have a rootDir...");
-    ok(gProcess._dbgProfile.name,
-      "The remote debugger profile doesn't have a name...");
+        is(frames.querySelectorAll(".dbg-stackframe").length, 1,
+          "Should have one frame in the stack.");
 
-    info("profile localDir: " + gProcess._dbgProfile.localDir);
-    info("profile rootDir: " + gProcess._dbgProfile.rootDir);
-    info("profile name: " + gProcess._dbgProfile.name);
-
-    let profileService = Cc["@mozilla.org/toolkit/profile-service;1"]
-      .createInstance(Ci.nsIToolkitProfileService);
+        gDebugger.DebuggerController.activeThread.addOneTimeListener("resumed", function() {
+          Services.tm.currentThread.dispatch({ run: function() {
+            closeDebuggerAndFinish(gTab, true);
+          }}, 0);
+        });
 
-    let profile = profileService.getProfileByName(gProcess._dbgProfile.name);
+        EventUtils.sendMouseEvent({ type: "click" },
+          gDebugger.document.getElementById("resume"),
+          gDebugger);
+      }}, 0);
+    });
 
-    ok(profile,
-      "The remote debugger profile wasn't *actually* created properly!");
-    is(profile.localDir.path, gProcess._dbgProfile.localDir.path,
-      "The remote debugger profile doesn't have the correct localDir!");
-    is(profile.rootDir.path, gProcess._dbgProfile.rootDir.path,
-      "The remote debugger profile doesn't have the correct rootDir!");
+    let iframe = gTab.linkedBrowser.contentWindow.wrappedJSObject.frames[0];
+
+    is(iframe.document.title, "Browser Debugger Test Tab", "Found the iframe");
 
-    DebuggerUI.toggleRemoteDebugger();
-  }}, 0);
-}
+    iframe.runDebuggerStatement();
+  },
+  function beforeTabAdded() {
+    if (!DebuggerServer.initialized) {
+      DebuggerServer.init();
+      DebuggerServer.addBrowserActors();
+    }
+    DebuggerServer.closeListener();
 
-function aOnClosing() {
-  ok(!gProcess._dbgProcess.isRunning,
-    "The remote debugger process isn't closed as it should be!");
-  is(gProcess._dbgProcess.exitValue, (Services.appinfo.OS == "WINNT" ? 0 : 256),
-    "The remote debugger process didn't die cleanly.");
+    gAutoConnect = Services.prefs.getBoolPref("devtools.debugger.remote-autoconnect");
+    Services.prefs.setBoolPref("devtools.debugger.remote-autoconnect", true);
 
-  info("process exit value: " + gProcess._dbgProcess.exitValue);
-
-  info("profile localDir: " + gProcess._dbgProfile.localDir.path);
-  info("profile rootDir: " + gProcess._dbgProfile.rootDir.path);
-  info("profile name: " + gProcess._dbgProfile.name);
-
-  executeSoon(function() {
-    finish();
+    // Open the listener at some point in the future to test automatic reconnect.
+    window.setTimeout(function() {
+      DebuggerServer.openListener(
+        Services.prefs.getIntPref("devtools.debugger.remote-port"));
+    }, Math.random() * 1000);
   });
 }
 
 registerCleanupFunction(function() {
+  Services.prefs.setBoolPref("devtools.debugger.remote-autoconnect", gAutoConnect);
   removeTab(gTab);
-  gProcess = null;
+  gWindow = null;
   gTab = null;
-  gDebuggee = null;
+  gAutoConnect = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_displayName.js
+++ b/browser/devtools/debugger/test/browser_dbg_displayName.js
@@ -10,17 +10,17 @@ var gDebugger = null;
 
 const TAB_URL = EXAMPLE_URL + "browser_dbg_displayName.html";
 
 function test() {
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
-    gDebugger = gPane.debuggerWindow;
+    gDebugger = gPane.contentWindow;
 
     testAnonCall();
   });
 }
 
 function testAnonCall() {
   gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
--- a/browser/devtools/debugger/test/browser_dbg_iframes.js
+++ b/browser/devtools/debugger/test/browser_dbg_iframes.js
@@ -9,17 +9,20 @@ var gPane = null;
 var gTab = null;
 
 const TEST_URL = EXAMPLE_URL + "browser_dbg_iframes.html";
 
 function test() {
   debug_tab_pane(TEST_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gPane = aPane;
-    let gDebugger = gPane.debuggerWindow;
+    let gDebugger = gPane.contentWindow;
+
+    is(gDebugger.document.getElementById("close").getAttribute("hidden"), "false",
+      "The close button should be visible in a normal content debugger.");
 
     is(gDebugger.DebuggerController.activeThread.paused, false,
       "Should be running after debug_tab_pane.");
 
     gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
       Services.tm.currentThread.dispatch({ run: function() {
 
         let frames = gDebugger.DebuggerView.StackFrames._frames;
--- a/browser/devtools/debugger/test/browser_dbg_location-changes.js
+++ b/browser/devtools/debugger/test/browser_dbg_location-changes.js
@@ -12,17 +12,17 @@ var gDebuggee = null;
 var gDebugger = null;
 
 function test()
 {
   debug_tab_pane(STACK_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
-    gDebugger = gPane.debuggerWindow;
+    gDebugger = gPane.contentWindow;
 
     testSimpleCall();
   });
 }
 
 function testSimpleCall() {
   gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
     Services.tm.currentThread.dispatch({
--- a/browser/devtools/debugger/test/browser_dbg_pause-resume.js
+++ b/browser/devtools/debugger/test/browser_dbg_pause-resume.js
@@ -7,17 +7,17 @@
 var gPane = null;
 var gTab = null;
 var gDebugger = null;
 
 function test() {
   debug_tab_pane(STACK_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gPane = aPane;
-    gDebugger = gPane.debuggerWindow;
+    gDebugger = gPane.contentWindow;
 
     testPause();
   });
 }
 
 function testPause() {
   is(gDebugger.DebuggerController.activeThread.paused, false,
     "Should be running after debug_tab_pane.");
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-01.js
@@ -8,17 +8,17 @@ var gTab = null;
 var gDebuggee = null;
 var gDebugger = null;
 
 function test() {
   debug_tab_pane(STACK_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
-    gDebugger = gPane.debuggerWindow;
+    gDebugger = gPane.contentWindow;
 
     testSimpleCall();
   });
 }
 
 function testSimpleCall() {
   gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-02.js
@@ -8,17 +8,17 @@ var gTab = null;
 var gDebuggee = null;
 var gDebugger = null;
 
 function test() {
   debug_tab_pane(STACK_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
-    gDebugger = gPane.debuggerWindow;
+    gDebugger = gPane.contentWindow;
 
     testSimpleCall();
   });
 }
 
 function testSimpleCall() {
   gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-03.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-03.js
@@ -8,17 +8,17 @@ var gTab = null;
 var gDebuggee = null;
 var gDebugger = null;
 
 function test() {
   debug_tab_pane(STACK_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
-    gDebugger = gPane.debuggerWindow;
+    gDebugger = gPane.contentWindow;
 
     testSimpleCall();
   });
 }
 
 function testSimpleCall() {
   gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-04.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-04.js
@@ -8,17 +8,17 @@ var gTab = null;
 var gDebuggee = null;
 var gDebugger = null;
 
 function test() {
   debug_tab_pane(STACK_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
-    gDebugger = gPane.debuggerWindow;
+    gDebugger = gPane.contentWindow;
 
     testSimpleCall();
   });
 }
 
 function testSimpleCall() {
   gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-05.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-05.js
@@ -8,17 +8,17 @@ var gTab = null;
 var gDebuggee = null;
 var gDebugger = null;
 
 function test() {
   debug_tab_pane(STACK_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
-    gDebugger = gPane.debuggerWindow;
+    gDebugger = gPane.contentWindow;
 
     testSimpleCall();
   });
 }
 
 function testSimpleCall() {
   gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-06.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-06.js
@@ -8,28 +8,31 @@ var gTab = null;
 var gDebuggee = null;
 var gDebugger = null;
 
 function test() {
   debug_tab_pane(STACK_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
-    gDebugger = gPane.debuggerWindow;
+    gDebugger = gPane.contentWindow;
 
     testSimpleCall();
   });
 }
 
 function testSimpleCall() {
   gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
 
       let globalScope = gDebugger.DebuggerView.Properties.globalScope;
       let localScope = gDebugger.DebuggerView.Properties.localScope;
+      globalScope.empty();
+      localScope.empty();
+
       let windowVar = globalScope.addVar("window");
       let documentVar = globalScope.addVar("document");
       let localVar0 = localScope.addVar("localVariable");
       let localVar1 = localScope.addVar("localVar1");
       let localVar2 = localScope.addVar("localVar2");
       let localVar3 = localScope.addVar("localVar3");
       let localVar4 = localScope.addVar("localVar4");
       let localVar5 = localScope.addVar("localVar5");
@@ -74,20 +77,28 @@ function testSimpleCall() {
       ok(localVar0, "The localVar0 hasn't been created correctly.");
       ok(localVar1, "The localVar1 hasn't been created correctly.");
       ok(localVar2, "The localVar2 hasn't been created correctly.");
       ok(localVar3, "The localVar3 hasn't been created correctly.");
       ok(localVar4, "The localVar4 hasn't been created correctly.");
       ok(localVar5, "The localVar5 hasn't been created correctly.");
 
 
+      for each (let elt in globalScope.querySelector(".details").childNodes) {
+        info("globalScope :: " + {
+          id: elt.id, className: elt.className }.toSource());
+      }
       is(globalScope.querySelector(".details").childNodes.length, 2,
         "The globalScope doesn't contain all the created variable elements.");
 
-      is(localScope.querySelector(".details").childNodes.length, 7,
+      for each (let elt in localScope.querySelector(".details").childNodes) {
+        info("localScope :: " + {
+          id: elt.id, className: elt.className }.toSource());
+      }
+      is(localScope.querySelector(".details").childNodes.length, 6,
         "The localScope doesn't contain all the created variable elements.");
 
 
       is(localVar5.querySelector(".details").childNodes.length, 6,
         "The localVar5 doesn't contain all the created properties.");
 
       is(localVar5.someProp5.querySelector(".details").childNodes.length, 6,
         "The localVar5.someProp5 doesn't contain all the created properties.");
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-07.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-07.js
@@ -12,17 +12,17 @@ var gPane = null;
 var gTab = null;
 var gDebugger = null;
 
 function test()
 {
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gPane = aPane;
-    gDebugger = gPane.debuggerWindow;
+    gDebugger = gPane.contentWindow;
 
     testFrameParameters();
   });
 }
 
 function testFrameParameters()
 {
   dump("Started testFrameParameters!\n");
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-08.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-08.js
@@ -12,17 +12,17 @@ var gPane = null;
 var gTab = null;
 var gDebugger = null;
 
 function test()
 {
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gPane = aPane;
-    gDebugger = gPane.debuggerWindow;
+    gDebugger = gPane.contentWindow;
 
     testFrameParameters();
   });
 }
 
 function testFrameParameters()
 {
   dump("Started testFrameParameters!\n");
--- a/browser/devtools/debugger/test/browser_dbg_script-switching.js
+++ b/browser/devtools/debugger/test/browser_dbg_script-switching.js
@@ -20,17 +20,17 @@ function test()
   let framesAdded = false;
   let resumed = false;
   let testStarted = false;
 
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
-    gDebugger = gPane.debuggerWindow;
+    gDebugger = gPane.contentWindow;
     resumed = true;
 
     gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
       framesAdded = true;
       executeSoon(startTest);
     });
 
     executeSoon(function() {
--- a/browser/devtools/debugger/test/browser_dbg_scripts-searching-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_scripts-searching-01.js
@@ -15,17 +15,17 @@ function test()
 {
   let scriptShown = false;
   let framesAdded = false;
 
   debug_tab_pane(STACK_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
-    gDebugger = gPane.debuggerWindow;
+    gDebugger = gPane.contentWindow;
 
     gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
       framesAdded = true;
       runTest();
     });
 
     gDebuggee.simpleCall();
   });
--- a/browser/devtools/debugger/test/browser_dbg_scripts-searching-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_scripts-searching-02.js
@@ -17,17 +17,17 @@ function test()
 {
   let scriptShown = false;
   let framesAdded = false;
 
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
-    gDebugger = gPane.debuggerWindow;
+    gDebugger = gPane.contentWindow;
 
     gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
       framesAdded = true;
       runTest();
     });
 
     gDebuggee.firstCall();
   });
--- a/browser/devtools/debugger/test/browser_dbg_scripts-sorting.js
+++ b/browser/devtools/debugger/test/browser_dbg_scripts-sorting.js
@@ -8,17 +8,17 @@ var gTab = null;
 var gDebuggee = null;
 var gDebugger = null;
 
 function test() {
   debug_tab_pane(STACK_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
-    gDebugger = gPane.debuggerWindow;
+    gDebugger = gPane.contentWindow;
 
     testSimpleCall();
   });
 }
 
 function testSimpleCall() {
   gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
--- a/browser/devtools/debugger/test/browser_dbg_select-line.js
+++ b/browser/devtools/debugger/test/browser_dbg_select-line.js
@@ -19,17 +19,17 @@ var gDebugger = null;
 var gScripts = null;
 
 function test()
 {
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
-    gDebugger = gPane.debuggerWindow;
+    gDebugger = gPane.contentWindow;
 
     testSelectLine();
   });
 }
 
 function testSelectLine() {
   gDebugger.DebuggerController.activeThread.addOneTimeListener("scriptsadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
--- a/browser/devtools/debugger/test/browser_dbg_stack-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_stack-01.js
@@ -9,17 +9,17 @@ var gTab = null;
 var gDebuggee = null;
 var gDebugger = null;
 
 function test() {
   debug_tab_pane(STACK_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
-    gDebugger = gPane.debuggerWindow;
+    gDebugger = gPane.contentWindow;
 
     testSimpleCall();
   });
 }
 
 function testSimpleCall() {
   gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
--- a/browser/devtools/debugger/test/browser_dbg_stack-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_stack-02.js
@@ -9,17 +9,17 @@ var gTab = null;
 var gDebuggee = null;
 var gDebugger = null;
 
 function test() {
   debug_tab_pane(STACK_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
-    gDebugger = gPane.debuggerWindow;
+    gDebugger = gPane.contentWindow;
 
     testEvalCall();
   });
 }
 
 function testEvalCall() {
   gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
--- a/browser/devtools/debugger/test/browser_dbg_stack-03.js
+++ b/browser/devtools/debugger/test/browser_dbg_stack-03.js
@@ -9,17 +9,17 @@ var gTab = null;
 var gDebuggee = null;
 var gDebugger = null;
 
 function test() {
   debug_tab_pane(STACK_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
-    gDebugger = gPane.debuggerWindow;
+    gDebugger = gPane.contentWindow;
 
     testRecurse();
   });
 }
 
 function testRecurse() {
   gDebuggee.gRecurseLimit = (gDebugger.DebuggerController.StackFrames.pageSize * 2) + 1;
 
--- a/browser/devtools/debugger/test/browser_dbg_stack-04.js
+++ b/browser/devtools/debugger/test/browser_dbg_stack-04.js
@@ -9,17 +9,17 @@ var gTab = null;
 var gDebuggee = null;
 var gDebugger = null;
 
 function test() {
   debug_tab_pane(STACK_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
-    gDebugger = gPane.debuggerWindow;
+    gDebugger = gPane.contentWindow;
 
     testEvalCallResume();
   });
 }
 
 function testEvalCallResume() {
   gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
--- a/browser/devtools/debugger/test/browser_dbg_stack-05.js
+++ b/browser/devtools/debugger/test/browser_dbg_stack-05.js
@@ -17,17 +17,17 @@ var gDebugger = null;
 function test() {
   let scriptShown = false;
   let framesAdded = false;
 
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
-    gDebugger = gPane.debuggerWindow;
+    gDebugger = gPane.contentWindow;
 
     gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
       framesAdded = true;
       runTest();
     });
 
     gDebuggee.firstCall();
   });
--- a/browser/devtools/debugger/test/browser_dbg_update-editor-mode.js
+++ b/browser/devtools/debugger/test/browser_dbg_update-editor-mode.js
@@ -24,17 +24,17 @@ function test()
   let framesAdded = false;
   let testStarted = false;
   let resumed = false;
 
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
-    gDebugger = gPane.debuggerWindow;
+    gDebugger = gPane.contentWindow;
     resumed = true;
 
     gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
       framesAdded = true;
       executeSoon(startTest);
     });
 
     executeSoon(function() {
--- a/browser/devtools/debugger/test/head.js
+++ b/browser/devtools/debugger/test/head.js
@@ -44,22 +44,26 @@ function addTab(aURL, aOnload)
 
   return tab;
 }
 
 function removeTab(aTab) {
   gBrowser.removeTab(aTab);
 }
 
-function closeDebuggerAndFinish(aTab) {
+function closeDebuggerAndFinish(aTab, aRemoteFlag) {
   DebuggerUI.chromeWindow.addEventListener("Debugger:Shutdown", function cleanup() {
     DebuggerUI.chromeWindow.removeEventListener("Debugger:Shutdown", cleanup, false);
     finish();
   }, false);
-  DebuggerUI.getDebugger(aTab).close();
+  if (!aRemoteFlag) {
+    DebuggerUI.getDebugger(aTab).close();
+  } else {
+    DebuggerUI.getRemoteDebugger().close();
+  }
 }
 
 function get_tab_actor_for_url(aClient, aURL, aCallback) {
   aClient.listTabs(function(aResponse) {
     for each (let tab in aResponse.tabs) {
       if (tab.url == aURL) {
         aCallback(tab);
         return;
@@ -94,28 +98,49 @@ function debug_tab_pane(aURL, aOnDebuggi
     gBrowser.selectedTab = gTab;
     let debuggee = tab.linkedBrowser.contentWindow.wrappedJSObject;
 
     let pane = DebuggerUI.toggleDebugger();
     pane._frame.addEventListener("Debugger:Connecting", function dbgConnected() {
       pane._frame.removeEventListener("Debugger:Connecting", dbgConnected, true);
 
       // Wait for the initial resume...
-      pane.debuggerWindow.gClient.addOneTimeListener("resumed", function() {
+      pane.contentWindow.gClient.addOneTimeListener("resumed", function() {
         aOnDebugging(tab, debuggee, pane);
       });
     }, true);
   });
 }
 
-function remote_debug_tab_pane(aURL, aOnClosing, aOnDebugging)
+function debug_remote(aURL, aOnDebugging, aBeforeTabAdded)
+{
+  // Make any necessary preparations (start the debugger server etc.)
+  aBeforeTabAdded();
+
+  let tab = addTab(aURL, function() {
+    gBrowser.selectedTab = gTab;
+    let debuggee = tab.linkedBrowser.contentWindow.wrappedJSObject;
+
+    let win = DebuggerUI.toggleRemoteDebugger();
+    win._dbgwin.addEventListener("Debugger:Connecting", function dbgConnected() {
+      win._dbgwin.removeEventListener("Debugger:Connecting", dbgConnected, true);
+
+      // Wait for the initial resume...
+      win.contentWindow.gClient.addOneTimeListener("resumed", function() {
+        aOnDebugging(tab, debuggee, win);
+      });
+    }, true);
+  });
+}
+
+function debug_chrome(aURL, aOnClosing, aOnDebugging)
 {
   let tab = addTab(aURL, function() {
     gBrowser.selectedTab = gTab;
     let debuggee = tab.linkedBrowser.contentWindow.wrappedJSObject;
 
-    DebuggerUI.toggleRemoteDebugger(aOnClosing, function dbgRan(process) {
+    DebuggerUI.toggleChromeDebugger(aOnClosing, function dbgRan(process) {
 
       // Wait for the remote debugging process to start...
       aOnDebugging(tab, debuggee, process);
     });
   });
 }
--- a/browser/devtools/webconsole/GcliCommands.jsm
+++ b/browser/devtools/webconsole/GcliCommands.jsm
@@ -224,17 +224,17 @@ gcli.addCommand({
       name: "file",
       type: {
         name: "selection",
         data: function() {
           let win = HUDService.currentContext();
           let dbg = win.DebuggerUI.getDebugger(win.gBrowser.selectedTab);
           let files = [];
           if (dbg) {
-            let scriptsView = dbg.debuggerWindow.DebuggerView.Scripts;
+            let scriptsView = dbg.contentWindow.DebuggerView.Scripts;
             for each (let script in scriptsView.scriptLocations) {
               files.push(script);
             }
           }
           return files;
         }
       },
       description: gcli.lookup("breakaddlineFileDesc")
--- a/browser/devtools/webconsole/test/browser_gcli_break.js
+++ b/browser/devtools/webconsole/test/browser_gcli_break.js
@@ -68,27 +68,27 @@ function testCreateCommands() {
   type("break add line");
   is(requisition.getStatus().toString(), "ERROR", "break add line is ERROR");
 
   let pane = DebuggerUI.toggleDebugger();
   pane._frame.addEventListener("Debugger:Connecting", function dbgConnected() {
     pane._frame.removeEventListener("Debugger:Connecting", dbgConnected, true);
 
     // Wait for the initial resume.
-    pane.debuggerWindow.gClient.addOneTimeListener("resumed", function() {
-      pane.debuggerWindow.gClient.activeThread.addOneTimeListener("framesadded", function() {
+    pane.contentWindow.gClient.addOneTimeListener("resumed", function() {
+      pane.contentWindow.gClient.activeThread.addOneTimeListener("framesadded", function() {
         type("break add line " + TEST_URI + " " + content.wrappedJSObject.line0);
         is(requisition.getStatus().toString(), "VALID", "break add line is VALID");
         requisition.exec();
 
         type("break list");
         is(requisition.getStatus().toString(), "VALID", "break list is VALID");
         requisition.exec();
 
-        pane.debuggerWindow.gClient.activeThread.resume(function() {
+        pane.contentWindow.gClient.activeThread.resume(function() {
           type("break del 0");
           is(requisition.getStatus().toString(), "VALID", "break del 0 is VALID");
           requisition.exec();
 
           closeConsole();
           finishTest();
         });
       });
--- a/browser/locales/en-US/chrome/browser/devtools/debugger.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/debugger.dtd
@@ -12,17 +12,17 @@
 <!ENTITY debuggerMenu.label          "Script Debugger">
 
 <!-- LOCALIZATION NOTE (remoteDebuggerMenu.label): This is the label for the
   -  application menu item that opens the remote debugger UI. -->
 <!ENTITY remoteDebuggerMenu.label    "Remote Debugger">
 
 <!-- LOCALIZATION NOTE (chromeDebuggerMenu.label): This is the label for the
   -  application menu item that opens the browser debugger UI. -->
-<!ENTITY chromeDebuggerMenu.label   "Browser Debugger">
+<!ENTITY chromeDebuggerMenu.label    "Browser Debugger">
 
 <!-- LOCALIZATION NOTE (debuggerMenu.commandkey): This is the command key that
   -  launches the debugger UI. Do not translate this one! -->
 <!ENTITY debuggerMenu.commandkey     "S">
 
 <!-- LOCALIZATION NOTE (debuggerUI.closeButton): This is the label for the
   -  button that closes the debugger UI. -->
 <!ENTITY debuggerUI.closeButton      "Close">
--- a/browser/locales/en-US/chrome/browser/devtools/debugger.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/debugger.properties
@@ -1,16 +1,40 @@
 # LOCALIZATION NOTE These strings are used inside the Script Debugger
 # which is available from the Web Developer sub-menu -> 'Script Debugger'.
 # The correct localization of this file might be to keep it in
 # English, or another language commonly spoken among web developers.
 # You want to make that choice consistent across the developer tools.
 # A good criteria is the language in which you'd find the best
 # documentation on web development on the web.
 
+# LOCALIZATION NOTE (remoteDebuggerWindowTitle): The title displayed for the
+# remote debugger window.
+remoteDebuggerWindowTitle=Remote Debugger
+
+# LOCALIZATION NOTE (remoteDebuggerPromptTitle): The title displayed on the
+# debugger prompt asking for the remote host and port to connect to.
+remoteDebuggerPromptTitle=Remote Connection
+
+# LOCALIZATION NOTE (remoteDebuggerPromptMessage): The message displayed on the
+# debugger prompt asking for the remote host and port to connect to.
+remoteDebuggerPromptMessage=Enter hostname and port number (host:port)
+
+# LOCALIZATION NOTE (remoteDebuggerPromptCheck): The message displayed on the
+# debugger prompt asking if the prompt should be shown again next time.
+remoteDebuggerPromptCheck=Don't ask me again
+
+# LOCALIZATION NOTE (remoteDebuggerReconnectMessage): The message displayed on the
+# debugger prompt asking for the remote host and port to connect to.
+remoteDebuggerReconnectMessage=Server not found. Try again? (host:port)
+
+# LOCALIZATION NOTE (remoteDebuggerReconnectMessage): The message displayed on the
+# debugger prompt asking for the remote host and port to connect to.
+remoteDebuggerConnectionFailedMessage=Could not find a server at the specified hostname and port number.
+
 # LOCALIZATION NOTE (pauseLabel): The label that is displayed on the pause
 # button when the debugger is in a running state.
 pauseLabel=Pause
 
 # LOCALIZATION NOTE (resumeLabel): The label that is displayed on the pause
 # button when the debugger is in a paused state.
 resumeLabel=Resume
 
--- a/browser/themes/gnomestripe/browser.css
+++ b/browser/themes/gnomestripe/browser.css
@@ -1737,17 +1737,17 @@ richlistitem[type~="action"][actiontype=
 }
 
 .alltabs-item[busy] > .menu-iconic-left > .menu-iconic-icon {
   list-style-image: url("chrome://global/skin/icons/loading_16.png");
 }
 
 .alltabs-item[tabIsVisible] {
   /* box-shadow instead of background-color to work around native styling */
-  box-shadow: inset 0 0 0 2em hsla(0,0%,50%,.15);
+  box-shadow: inset -5px 0 ThreeDShadow;
 }
 
 /* Sidebar */
 #sidebar-header > .tabs-closebutton {
   margin-bottom: 0px !important;
   padding: 0px 2px 0px 2px !important;
 }
 
--- a/browser/themes/pinstripe/browser.css
+++ b/browser/themes/pinstripe/browser.css
@@ -2191,17 +2191,17 @@ toolbarbutton.chevron > .toolbarbutton-m
 }
 
 .alltabs-item[busy] > .menu-iconic-left > .menu-iconic-icon {
   list-style-image: url("chrome://global/skin/icons/loading_16.png") !important;
 }
 
 .alltabs-item[tabIsVisible] {
   /* box-shadow instead of background-color to work around native styling */
-  box-shadow: inset 0 0 0 2em hsla(0,0%,50%,.15);
+  box-shadow: inset -5px 0 ThreeDShadow;
 }
 
 /* Tabstrip close button */
 .tabs-closebutton {
   -moz-padding-end: 4px;
   list-style-image: url("chrome://global/skin/icons/close.png");
   -moz-image-region: rect(0, 16px, 16px, 0);
   border: none;
--- a/browser/themes/winstripe/browser.css
+++ b/browser/themes/winstripe/browser.css
@@ -2081,17 +2081,17 @@ richlistitem[type~="action"][actiontype=
 }
 
 .alltabs-item[busy] > .menu-iconic-left > .menu-iconic-icon {
   list-style-image: url("chrome://global/skin/icons/loading_16.png");
 }
 
 .alltabs-item[tabIsVisible] {
   /* box-shadow instead of background-color to work around native styling */
-  box-shadow: inset 0 0 0 2em hsla(0,0%,50%,.15);
+  box-shadow: inset -5px 0 ThreeDShadow;
 }
 
 /* Tabstrip close button */
 .tabs-closebutton {
   -moz-appearance: none;
   list-style-image: url("chrome://global/skin/icons/close.png");
   -moz-image-region: rect(0, 16px, 16px, 0);
   padding: 4px 2px;
--- a/toolkit/content/tests/chrome/test_tabbox.xul
+++ b/toolkit/content/tests/chrome/test_tabbox.xul
@@ -178,42 +178,47 @@ function test_tabpanels_State(tabpanels,
 function test_tabbox_focus()
 {
   $("tabboxes").hidden = true;
   $(document.activeElement).blur();
 
   var tabbox = $("tabbox-nofocus");
   var tab = $("tab-nofocus");
 
-  tab.addEventListener("focus", function () {
-    tab.removeEventListener("focus", arguments.callee, true);
+  when_tab_focused(tab, function () {
     ok(document.activeElement, tab, "focus in tab with no focusable elements");
 
     tabbox.selectedIndex = 0;
     $("tab-nofocus-button").focus();
 
-    tab.addEventListener("focus", function () {
-      tab.removeEventListener("focus", arguments.callee, true);
+    when_tab_focused(tab, function () {
       ok(document.activeElement, tab, "focus in tab with no focusable elements, but with something in another tab focused");
 
       var textboxExtra = $("textbox-extra");
       textboxExtra.addEventListener("focus", function () {
         textboxExtra.removeEventListener("focus", arguments.callee, true);
         ok(document.activeElement, textboxExtra, "focus in tab with focus currently in textbox that is sibling of tabs");
 
         SimpleTest.finish();
       }, true);
 
       tabbox.selectedIndex = 0;
       textboxExtra.hidden = false;
       synthesizeMouseAtCenter(tab, { });
-    }, true);
+    });
 
     synthesizeMouseAtCenter(tab, { });
-  }, true);
+  });
 
   synthesizeMouseAtCenter(tab, { });
 }
 
+function when_tab_focused(tab, callback) {
+  tab.addEventListener("focus", function onFocused() {
+    tab.removeEventListener("focus", onFocused, true);
+    SimpleTest.executeSoon(callback);
+  }, true);
+}
+
 ]]>
 </script>
 
 </window>
--- a/toolkit/content/widgets/tabbox.xml
+++ b/toolkit/content/widgets/tabbox.xml
@@ -786,23 +786,33 @@
 
     <handlers>
       <handler event="mousedown" button="0">
       <![CDATA[
         if (this.disabled)
           return;
 
         if (this != this.parentNode.selectedItem) { // Not selected yet
-          // Select new tab after short delay so that PostHandleEvent() doesn't see
-          // the tab as selected yet, otherwise it will focus the tab for us --
-          // the CSS for tab has -moz-user-focus: normal only for selected="true".
-          function setTab(tab) {
-            tab.parentNode._selectNewTab(tab);
+          // Call this before setting the 'ignorefocus' attribute because this
+          // will pass on focus if the formerly selected tab was focused as well.
+          this.parentNode._selectNewTab(this);
+
+          var isTabFocused = false;
+          try {
+            isTabFocused = (document.commandDispatcher.focusedElement == this);
+          } catch (e) {}
+
+          // Set '-moz-user-focus' to 'ignore' so that PostHandleEvent() can't
+          // focus the tab; we only want tabs to be focusable by the mouse if
+          // they are already focused. After a short timeout we'll reset
+          // '-moz-user-focus' so that tabs can be focused by keyboard again.
+          if (!isTabFocused) {
+            this.setAttribute("ignorefocus", "true");
+            setTimeout(function (tab) tab.removeAttribute("ignorefocus"), 0, this);
           }
-          setTimeout(setTab, 0, this);
         }
         // Otherwise this tab is already selected and we will fall
         // through to mousedown behavior which sets focus on the current tab,
         // Only a click on an already selected tab should focus the tab itself.
       ]]>
       </handler>
 
       <handler event="keypress" keycode="VK_LEFT">
--- a/toolkit/content/xul.css
+++ b/toolkit/content/xul.css
@@ -647,17 +647,17 @@ tabs {
 }
 
 tab {
   -moz-binding: url("chrome://global/content/bindings/tabbox.xml#tab");
   -moz-box-align: center;
   -moz-box-pack: center;
 }
 
-tab[selected="true"] {
+tab[selected="true"]:not([ignorefocus="true"]) {
   -moz-user-focus: normal;
 }
 
 tabpanels {
   -moz-binding: url("chrome://global/content/bindings/tabbox.xml#tabpanels");
   display: -moz-deck;
 }
 
--- a/toolkit/devtools/debugger/server/dbg-server.js
+++ b/toolkit/devtools/debugger/server/dbg-server.js
@@ -42,16 +42,17 @@
  * Toolkit glue for the remote debugging protocol, loaded into the
  * debugging global.
  */
 
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 const CC = Components.Constructor;
 const Cu = Components.utils;
+const Cr = Components.results;
 
 Cu.import("resource://gre/modules/Services.jsm");
 let wantLogging = Services.prefs.getBoolPref("devtools.debugger.log");
 
 Cu.import("resource://gre/modules/jsdebugger.jsm");
 addDebuggerToGlobal(this);
 
 function dumpn(str) {