merge m-c to fx-team
authorTim Taubert <ttaubert@mozilla.com>
Thu, 20 Sep 2012 11:36:34 +0200
changeset 107600 2208b83cc81d7ea02fa30f27a03c9f3f5b766155
parent 107590 1e56d30168204432aaab87c4df3838c14d0fe50b (current diff)
parent 107599 f0a944193de7d52a41846f43652658c6896f486e (diff)
child 107693 2f53d8795b489a054a654b8c12b37487457b03a7
child 107702 98a4a0177f22f6b9e0ad0e2d6201fec6567b8283
push id82
push usershu@rfrn.org
push dateFri, 05 Oct 2012 13:20:22 +0000
milestone18.0a1
merge m-c to fx-team
browser/devtools/debugger/test/browser_dbg_contextactor-01.js
browser/devtools/debugger/test/browser_dbg_contextactor-02.js
browser/locales/en-US/chrome/browser/browser.dtd
--- a/b2g/chrome/content/dbg-browser-actors.js
+++ b/b2g/chrome/content/dbg-browser-actors.js
@@ -33,16 +33,17 @@ function DeviceRootActor(connection) {
 }
 
 DeviceRootActor.prototype = new BrowserRootActor();
 
 /**
  * Disconnects the actor from the browser window.
  */
 DeviceRootActor.prototype.disconnect = function DRA_disconnect() {
+  this._extraActors = null;
   let actor = this._tabActors.get(this.browser);
   if (actor) {
     actor.exit();
   }
 };
 
 /**
  * Handles the listTabs request.  Builds a list of actors for the single
@@ -56,29 +57,33 @@ DeviceRootActor.prototype.onListTabs = f
     // this.actorID is set by ActorPool when an actor is put into one.
     actor.parentID = this.actorID;
     this._tabActors.set(this.browser, actor);
   }
 
   let actorPool = new ActorPool(this.conn);
   actorPool.addActor(actor);
 
+  this._createExtraActors(DebuggerServer.globalActorFactories, actorPool);
+
   // Now drop the old actorID -> actor map. Actors that still mattered were
   // added to the new map, others will go away.
   if (this._tabActorPool) {
     this.conn.removeActorPool(this._tabActorPool);
   }
   this._tabActorPool = actorPool;
   this.conn.addActorPool(this._tabActorPool);
 
-  return {
+  let response = {
     'from': 'root',
     'selected': 0,
     'tabs': [actor.grip()]
   };
+  this._appendExtraActors(response);
+  return response;
 };
 
 /**
  * The request types this actor can handle.
  */
 DeviceRootActor.prototype.requestTypes = {
   'listTabs': DeviceRootActor.prototype.onListTabs
 };
@@ -98,21 +103,33 @@ function DeviceTabActor(connection, brow
 
 DeviceTabActor.prototype = new BrowserTabActor();
 
 DeviceTabActor.prototype.grip = function DTA_grip() {
   dbg_assert(!this.exited,
              'grip() should not be called on exited browser actor.');
   dbg_assert(this.actorID,
              'tab should have an actorID.');
-  return {
+
+  let response = {
     'actor': this.actorID,
     'title': this.browser.title,
     'url': this.browser.document.documentURI
+  };
+
+  // Walk over tab actors added by extensions and add them to a new ActorPool.
+  let actorPool = new ActorPool(this.conn);
+  this._createExtraActors(DebuggerServer.globalActorFactories, actorPool);
+  if (!actorPool.isEmpty()) {
+    this._tabActorPool = actorPool;
+    this.conn.addActorPool(this._tabActorPool);
   }
+
+  this._appendExtraActors(response);
+  return response;
 };
 
 /**
  * Creates a thread actor and a pool for context-lifetime actors. It then sets
  * up the content window for debugging.
  */
 DeviceTabActor.prototype._pushContext = function DTA_pushContext() {
   dbg_assert(!this._contextPool, "Can't push multiple contexts");
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -149,21 +149,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 #ifdef MOZ_SAFE_BROWSING
 XPCOMUtils.defineLazyGetter(this, "SafeBrowsing", function() {
   let tmp = {};
   Cu.import("resource:///modules/SafeBrowsing.jsm", tmp);
   return tmp.SafeBrowsing;
 });
 #endif
 
-XPCOMUtils.defineLazyGetter(this, "gBrowserNewTabPreloader", function () {
-  let tmp = {};
-  Cu.import("resource:///modules/BrowserNewTabPreloader.jsm", tmp);
-  return new tmp.BrowserNewTabPreloader();
-});
+XPCOMUtils.defineLazyModuleGetter(this, "gBrowserNewTabPreloader",
+  "resource://gre/modules/BrowserNewTabPreloader.jsm", "BrowserNewTabPreloader");
 
 let gInitialPages = [
   "about:blank",
   "about:newtab",
   "about:home",
   "about:privatebrowsing",
   "about:sessionrestore"
 ];
@@ -1404,22 +1401,16 @@ var gBrowserInit = {
     if (document.mozFullScreen)
       onMozEnteredDomFullscreen();
 
 #ifdef MOZ_SERVICES_SYNC
     // initialize the sync UI
     gSyncUI.init();
 #endif
 
-    // Don't preload new tab pages when the toolbar is hidden
-    // (i.e. when the current window is a popup window).
-    if (window.toolbar.visible) {
-      gBrowserNewTabPreloader.init(window);
-    }
-
     gBrowserThumbnails.init();
     TabView.init();
 
     setUrlAndSearchBarWidthForConditionalForwardButton();
     window.addEventListener("resize", function resizeHandler(event) {
       if (event.target == window)
         setUrlAndSearchBarWidthForConditionalForwardButton();
     });
@@ -1558,20 +1549,16 @@ var gBrowserInit = {
     CombinedStopReload.uninit();
 
     gGestureSupport.init(false);
 
     FullScreen.cleanup();
 
     Services.obs.removeObserver(gPluginHandler.pluginCrashed, "plugin-crashed");
 
-    if (!__lookupGetter__("gBrowserNewTabPreloader")) {
-      gBrowserNewTabPreloader.uninit();
-    }
-
     try {
       gBrowser.removeProgressListener(window.XULBrowserWindow);
       gBrowser.removeTabsProgressListener(window.TabsProgressListener);
     } catch (ex) {
     }
 
     PlacesStarButton.uninit();
 
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -1062,17 +1062,22 @@
       <chatbar id="pinnedchats" layer="true" mousethrough="always"/>
       <statuspanel id="statusbar-display" inactive="true"/>
     </vbox>
     <splitter id="devtools-side-splitter" hidden="true"/>
     <vbox id="devtools-sidebar-box" hidden="true"
           style="min-width: 18em; width: 22em; max-width: 42em;" persist="width">
       <toolbar id="devtools-sidebar-toolbar"
                class="devtools-toolbar"
-               nowindowdrag="true"/>
+               nowindowdrag="true">
+        <spacer flex="1"/>
+        <toolbarbutton tooltiptext="&inspectSidebarCloseButton.tooltiptext;"
+                       class="devtools-closebutton"
+                       command="Inspector:Sidebar"/>
+      </toolbar>
       <deck id="devtools-sidebar-deck" flex="1"/>
     </vbox>
     <splitter id="social-sidebar-splitter"
               class="chromeclass-extrachrome sidebar-splitter"
               observes="socialSidebarBroadcaster"/>
     <vbox id="social-sidebar-box"
           class="chromeclass-extrachrome"
           observes="socialSidebarBroadcaster">
new file mode 100644
--- /dev/null
+++ b/browser/base/content/newtab/preload.xhtml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+    "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+
+<!-- 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/. -->
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <title></title>
+  </head>
+  <body></body>
+</html>
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -41,16 +41,17 @@ browser.jar:
 *       content/browser/browser.css                   (content/browser.css)
 *       content/browser/browser.js                    (content/browser.js)
 *       content/browser/browser.xul                   (content/browser.xul)
 *       content/browser/browser-tabPreviews.xml       (content/browser-tabPreviews.xml)
         content/browser/content.js                    (content/content.js)
         content/browser/newtab/newTab.xul             (content/newtab/newTab.xul)
 *       content/browser/newtab/newTab.js              (content/newtab/newTab.js)
         content/browser/newtab/newTab.css             (content/newtab/newTab.css)
+        content/browser/newtab/preload.xhtml          (content/newtab/preload.xhtml)
 *       content/browser/pageinfo/pageInfo.xul         (content/pageinfo/pageInfo.xul)
 *       content/browser/pageinfo/pageInfo.js          (content/pageinfo/pageInfo.js)
         content/browser/pageinfo/pageInfo.css         (content/pageinfo/pageInfo.css)
         content/browser/pageinfo/pageInfo.xml         (content/pageinfo/pageInfo.xml)
         content/browser/pageinfo/feeds.js             (content/pageinfo/feeds.js)
         content/browser/pageinfo/feeds.xml            (content/pageinfo/feeds.xml)
         content/browser/pageinfo/permissions.js       (content/pageinfo/permissions.js)
         content/browser/pageinfo/security.js          (content/pageinfo/security.js)
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -33,16 +33,19 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource:///modules/webappsUI.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PageThumbs",
                                   "resource:///modules/PageThumbs.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "NewTabUtils",
                                   "resource:///modules/NewTabUtils.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "BrowserNewTabPreloader",
+                                  "resource:///modules/BrowserNewTabPreloader.jsm");
+
 XPCOMUtils.defineLazyModuleGetter(this, "PdfJs",
                                   "resource://pdf.js/PdfJs.jsm");
 
 const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser";
 const PREF_PLUGINS_UPDATEURL  = "plugins.update.url";
 
 // We try to backup bookmarks at idle times, to avoid doing that at shutdown.
 // Number of idle seconds before trying to backup bookmarks.  15 minutes.
@@ -340,16 +343,17 @@ BrowserGlue.prototype = {
 
     // handle any UI migration
     this._migrateUI();
 
     UserAgentOverrides.init();
     webappsUI.init();
     PageThumbs.init();
     NewTabUtils.init();
+    BrowserNewTabPreloader.init();
     SignInToWebsiteUX.init();
     PdfJs.init();
 
     Services.obs.notifyObservers(null, "browser-ui-startup-complete", "");
   },
 
   // the first browser window has finished initializing
   _onFirstWindowLoaded: function BG__onFirstWindowLoaded() {
@@ -365,16 +369,17 @@ BrowserGlue.prototype = {
 #endif
   },
 
   // profile shutdown handler (contains profile cleanup routines)
   _onProfileShutdown: function BG__onProfileShutdown() {
     this._shutdownPlaces();
     this._sanitizer.onShutdown();
     PageThumbs.uninit();
+    BrowserNewTabPreloader.uninit();
   },
 
   // All initial windows have opened.
   _onWindowsRestored: function BG__onWindowsRestored() {
     // Show about:rights notification, if needed.
     if (this._shouldShowRights()) {
       this._showRightsNotification();
 #ifdef MOZ_TELEMETRY_REPORTING
--- a/browser/devtools/debugger/DebuggerUI.jsm
+++ b/browser/devtools/debugger/DebuggerUI.jsm
@@ -461,50 +461,23 @@ function ChromeDebuggerProcess(aDebugger
 
 ChromeDebuggerProcess.prototype = {
 
   /**
    * Initializes the debugger server.
    */
   _initServer: function RDP__initServer() {
     if (!DebuggerServer.initialized) {
-      DebuggerServer.init(this._allowConnection);
+      DebuggerServer.init();
       DebuggerServer.addBrowserActors();
     }
-    DebuggerServer.closeListener();
     DebuggerServer.openListener(DebuggerPreferences.remotePort);
   },
 
   /**
-   * Prompt the user to accept or decline the incoming connection.
-   *
-   * @return true if the connection should be permitted, false otherwise
-   */
-  _allowConnection: function RDP__allowConnection() {
-    let title = L10N.getStr("remoteIncomingPromptTitle");
-    let msg = L10N.getStr("remoteIncomingPromptMessage");
-    let disableButton = L10N.getStr("remoteIncomingPromptDisable");
-    let prompt = Services.prompt;
-    let flags = prompt.BUTTON_POS_0 * prompt.BUTTON_TITLE_OK +
-                prompt.BUTTON_POS_1 * prompt.BUTTON_TITLE_CANCEL +
-                prompt.BUTTON_POS_2 * prompt.BUTTON_TITLE_IS_STRING +
-                prompt.BUTTON_POS_1_DEFAULT;
-    let result = prompt.confirmEx(null, title, msg, flags, null, null,
-                                  disableButton, null, { value: false });
-    if (result == 0) {
-      return true;
-    }
-    if (result == 2) {
-      DebuggerServer.closeListener();
-      Services.prefs.setBoolPref("devtools.debugger.remote-enabled", false);
-    }
-    return false;
-  },
-
-  /**
    * Initializes a profile for the remote debugger process.
    */
   _initProfile: function RDP__initProfile() {
     let profileService = Cc["@mozilla.org/toolkit/profile-service;1"]
       .createInstance(Ci.nsIToolkitProfileService);
 
     let dbgProfileName;
     try {
--- a/browser/devtools/debugger/test/Makefile.in
+++ b/browser/devtools/debugger/test/Makefile.in
@@ -15,18 +15,17 @@ MOCHITEST_BROWSER_TESTS = \
 	browser_dbg_createRemote.js \
 	browser_dbg_createChrome.js \
 	browser_dbg_debugger-tab-switch.js \
 	browser_dbg_debugger-tab-switch-window.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 \
+	browser_dbg_globalactor-01.js \
 	testactors.js \
 	browser_dbg_nav-01.js \
 	browser_dbg_propertyview-01.js \
 	browser_dbg_propertyview-02.js \
 	browser_dbg_propertyview-03.js \
 	browser_dbg_propertyview-04.js \
 	browser_dbg_propertyview-05.js \
 	browser_dbg_propertyview-06.js \
deleted file mode 100644
--- a/browser/devtools/debugger/test/browser_dbg_contextactor-01.js
+++ /dev/null
@@ -1,49 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Check extension-added context actor lifetimes.
- */
-
-var gTab1 = null;
-var gTab1Actor = null;
-
-var gClient = null;
-
-function test()
-{
-  DebuggerServer.addActors("chrome://mochitests/content/browser/browser/devtools/debugger/test/testactors.js");
-
-  let transport = DebuggerServer.connectPipe();
-  gClient = new DebuggerClient(transport);
-  gClient.connect(function(aType, aTraits) {
-    is(aType, "browser", "Root actor should identify itself as a browser.");
-    get_tab();
-  });
-}
-
-function get_tab()
-{
-  gTab1 = addTab(TAB1_URL, function() {
-    attach_tab_actor_for_url(gClient, TAB1_URL, function(aGrip) {
-      gTab1Actor = aGrip.actor;
-      gClient.request({ to: aGrip.actor, type: "testContextActor1" }, function(aResponse) {
-        ok(aResponse.actor, "testContextActor1 request should return an actor.");
-        ok(aResponse.actor.indexOf("testone") >= 0,
-           "testContextActor's actorPrefix should be used.");
-        gClient.request({ to: aResponse.actor, type: "ping" }, function(aResponse) {
-          is(aResponse.pong, "pong", "Actor should response to requests.");
-          finish_test();
-        });
-      });
-    });
-  });
-}
-
-function finish_test()
-{
-  gClient.close(function() {
-    removeTab(gTab1);
-    finish();
-  });
-};
rename from browser/devtools/debugger/test/browser_dbg_contextactor-02.js
rename to browser/devtools/debugger/test/browser_dbg_globalactor-01.js
--- a/browser/devtools/debugger/test/browser_dbg_contextactor-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_globalactor-01.js
@@ -1,59 +1,36 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
- * Check extension-added context actor lifetimes.
+ * Check extension-added global actor API.
  */
 
-var gTab1 = null;
-var gTab1Actor = null;
-
 var gClient = null;
 
 function test()
 {
   DebuggerServer.addActors("chrome://mochitests/content/browser/browser/devtools/debugger/test/testactors.js");
 
   let transport = DebuggerServer.connectPipe();
   gClient = new DebuggerClient(transport);
   gClient.connect(function(aType, aTraits) {
     is(aType, "browser", "Root actor should identify itself as a browser.");
-    get_tab();
-  });
-}
-
-function get_tab()
-{
-  gTab1 = addTab(TAB1_URL, function() {
-    get_tab_actor_for_url(gClient, TAB1_URL, function(aGrip) {
-      gTab1Actor = aGrip.actor;
-      gClient.request({ to: gTab1Actor, type: "attach" }, function(aResponse) {
-        gClient.request({ to: gTab1Actor, type: "testContextActor1" }, function(aResponse) {
-          navigate_tab(aResponse.actor);
-        });
+    gClient.listTabs(function(aResponse) {
+      let globalActor = aResponse.testGlobalActor1;
+      ok(globalActor, "Found the test tab actor.")
+      ok(globalActor.indexOf("testone") >= 0,
+         "testTabActor's actorPrefix should be used.");
+      gClient.request({ to: globalActor, type: "ping" }, function(aResponse) {
+        is(aResponse.pong, "pong", "Actor should respond to requests.");
+        finish_test();
       });
     });
   });
 }
 
-function navigate_tab(aTestActor)
-{
-  gClient.addOneTimeListener("tabNavigated", function(aEvent, aResponse) {
-    gClient.request({ to: aTestActor, type: "ping" }, function(aResponse) {
-      // TODO: Currently the client is supposed to clean up after tabNavigated
-      // events. We should remove this check, or even better, remove the whole
-      // test.
-      todo(aResponse.error, "noSuchActor", "testContextActor1 should have gone away with the navigation.");
-      finish_test();
-    });
-  });
-  gTab1.linkedBrowser.loadURI(TAB2_URL);
-}
-
 function finish_test()
 {
   gClient.close(function() {
-    removeTab(gTab1);
     finish();
   });
 }
--- a/browser/devtools/debugger/test/browser_dbg_tabactor-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_tabactor-01.js
@@ -19,27 +19,25 @@ function test()
   gClient.connect(function (aType, aTraits) {
     is(aType, "browser", "Root actor should identify itself as a browser.");
     get_tab();
   });
 }
 
 function get_tab()
 {
-  gTab1 = addTab(TAB1_URL, function () {
-    attach_tab_actor_for_url(gClient, TAB1_URL, function (aGrip) {
+  gTab1 = addTab(TAB1_URL, function() {
+    attach_tab_actor_for_url(gClient, TAB1_URL, function(aGrip) {
       gTab1Actor = aGrip.actor;
-      gClient.request({ to: aGrip.actor, type: "testTabActor1" }, function (aResponse) {
-        ok(aResponse.actor, "testTabActor1 request should return an actor.");
-        ok(aResponse.actor.indexOf("testone") >= 0,
-           "testTabActor's actorPrefix should be used.");
-        gClient.request({ to: aResponse.actor, type: "ping" }, function (aResponse) {
-          is(aResponse.pong, "pong", "Actor should response to requests.");
-          finish_test();
-        });
+      ok(aGrip.testTabActor1, "Found the test tab actor.")
+      ok(aGrip.testTabActor1.indexOf("testone") >= 0,
+         "testTabActor's actorPrefix should be used.");
+      gClient.request({ to: aGrip.testTabActor1, type: "ping" }, function(aResponse) {
+        is(aResponse.pong, "pong", "Actor should respond to requests.");
+        finish_test();
       });
     });
   });
 }
 
 function finish_test()
 {
   gClient.close(function() {
--- a/browser/devtools/debugger/test/browser_dbg_tabactor-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_tabactor-02.js
@@ -19,33 +19,43 @@ function test()
   gClient.connect(function (aType, aTraits) {
     is(aType, "browser", "Root actor should identify itself as a browser.");
     get_tab();
   });
 }
 
 function get_tab()
 {
-  gTab1 = addTab(TAB1_URL, function () {
-    attach_tab_actor_for_url(gClient, TAB1_URL, function (aGrip) {
+  gTab1 = addTab(TAB1_URL, function() {
+    attach_tab_actor_for_url(gClient, TAB1_URL, function(aGrip) {
       gTab1Actor = aGrip.actor;
-      gClient.request({ to: aGrip.actor, type: "testTabActor1" }, function (aResponse) {
+      ok(aGrip.testTabActor1, "Found the test tab actor.")
+      ok(aGrip.testTabActor1.indexOf("testone") >= 0,
+         "testTabActor's actorPrefix should be used.");
+      gClient.request({ to: aGrip.testTabActor1, type: "ping" }, function(aResponse) {
+        is(aResponse.pong, "pong", "Actor should respond to requests.");
         close_tab(aResponse.actor);
       });
     });
   });
 }
 
 function close_tab(aTestActor)
 {
   removeTab(gTab1);
-  gClient.request({ to: aTestActor, type: "ping" }, function (aResponse) {
-    is(aResponse.error, "noSuchActor", "testTabActor1 should have gone away with the tab.");
+  try {
+    gClient.request({ to: aTestActor, type: "ping" }, function (aResponse) {
+      is(aResponse, undefined, "testTabActor1 didn't go away with the tab.");
+      finish_test();
+    });
+  } catch (e) {
+    is(e.message, "'ping' request packet has no destination.",
+       "testTabActor1 should have gone away with the tab.");
     finish_test();
-  });
+  }
 }
 
 function finish_test()
 {
   gClient.close(function () {
     finish();
   });
 }
--- a/browser/devtools/debugger/test/head.js
+++ b/browser/devtools/debugger/test/head.js
@@ -19,16 +19,19 @@ const EXAMPLE_URL = "http://example.com/
 const TAB1_URL = EXAMPLE_URL + "browser_dbg_tab1.html";
 const TAB2_URL = EXAMPLE_URL + "browser_dbg_tab2.html";
 const STACK_URL = EXAMPLE_URL + "browser_dbg_stack.html";
 // Enable remote debugging for the relevant tests.
 let gEnableRemote = Services.prefs.getBoolPref("devtools.debugger.remote-enabled");
 Services.prefs.setBoolPref("devtools.debugger.remote-enabled", true);
 registerCleanupFunction(function() {
   Services.prefs.setBoolPref("devtools.debugger.remote-enabled", gEnableRemote);
+
+  // Properly shut down the server to avoid memory leaks.
+  DebuggerServer.destroy();
 });
 
 if (!DebuggerServer.initialized) {
   DebuggerServer.init(function () { return true; });
   DebuggerServer.addBrowserActors();
 }
 
 waitForExplicitFinish();
--- a/browser/devtools/debugger/test/testactors.js
+++ b/browser/devtools/debugger/test/testactors.js
@@ -1,58 +1,31 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-function TestActor1(aConnection, aTab, aOnDisconnect)
+function TestActor1(aConnection, aTab)
 {
   this.conn = aConnection;
   this.tab = aTab;
-  this.onDisconnect = aOnDisconnect;
 }
 
 TestActor1.prototype = {
   actorPrefix: "testone",
 
-  disconnect: function TA1_disconnect() {
-    this.onDisconnect();
-  },
-
   grip: function TA1_grip() {
     return { actor: this.actorID,
              test: "TestActor1" };
   },
 
   onPing: function TA1_onPing() {
     return { pong: "pong" };
   }
 };
 
 TestActor1.prototype.requestTypes = {
   "ping": TestActor1.prototype.onPing
 };
 
-DebuggerServer.addTabRequest("testTabActor1", function (aTab) {
-  if (aTab._testTabActor1) {
-    return aTab._testTabActor1.grip();
-  }
-
-  let actor = new TestActor1(aTab.conn, aTab.browser, function () {
-    delete aTab._testTabActor1;
-  });
-  aTab.tabActorPool.addActor(actor);
-  aTab._testTabActor1 = actor;
-  return actor.grip();
-});
-
+DebuggerServer.removeTabActor(TestActor1);
+DebuggerServer.removeGlobalActor(TestActor1);
 
-DebuggerServer.addTabRequest("testContextActor1", function (aTab, aRequest) {
-  if (aTab._testContextActor1) {
-    return aTab._testContextActor1.grip();
-  }
-
-  let actor = new TestActor1(aTab.conn, aTab.browser, function () {
-    delete aTab._testContextActor1;
-  });
-  aTab.contextActorPool.addActor(actor);
-  aTab._testContextActor1 = actor;
-  return actor.grip();
-});
-
+DebuggerServer.addTabActor(TestActor1, "testTabActor1");
+DebuggerServer.addGlobalActor(TestActor1, "testGlobalActor1");
--- a/browser/devtools/highlighter/inspector.jsm
+++ b/browser/devtools/highlighter/inspector.jsm
@@ -1642,18 +1642,19 @@ InspectorStyleSidebar.prototype = {
     let btn = this._chromeDoc.createElement("toolbarbutton");
     btn.setAttribute("label", aRegObj.label);
     btn.setAttribute("class", "devtools-toolbarbutton");
     btn.setAttribute("tooltiptext", aRegObj.tooltiptext);
     btn.setAttribute("accesskey", aRegObj.accesskey);
     btn.setAttribute("image", aRegObj.icon || "");
     btn.setAttribute("type", "radio");
     btn.setAttribute("group", "sidebar-tools");
-    this._toolbar.appendChild(btn);
 
+    let spacer = this._toolbar.querySelector("spacer");
+    this._toolbar.insertBefore(btn, spacer);
     // create tool iframe
     let frame = this._chromeDoc.createElement("iframe");
     frame.setAttribute("flex", "1");
     frame._toolID = aRegObj.id;
 
     // This is needed to enable tooltips inside the iframe document.
     frame.setAttribute("tooltip", "aHTMLTooltip");
 
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -230,16 +230,17 @@ These should match what Safari and other
   -  approximation to it or choose a word (or words) that means
   -  "simple discardable text editor". -->
 <!ENTITY scratchpad.label             "Scratchpad">
 <!ENTITY scratchpad.accesskey         "s">
 <!ENTITY scratchpad.keycode           "VK_F4">
 <!ENTITY scratchpad.keytext           "F4">
 
 <!ENTITY inspectCloseButton.tooltiptext "Close Inspector">
+<!ENTITY inspectSidebarCloseButton.tooltiptext "Close sidebar">
 
 <!ENTITY devToolbarCloseButton.tooltiptext "Close Developer Toolbar">
 <!ENTITY devToolbarMenu.label              "Developer Toolbar">
 <!ENTITY devToolbarMenu.accesskey          "v">
 <!ENTITY devToolbar.keycode                "VK_F2">
 <!ENTITY devToolbar.keytext                "F2">
 <!ENTITY devToolbarOtherToolsButton.label  "More Tools">
 
--- a/browser/locales/en-US/chrome/browser/devtools/debugger.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/debugger.properties
@@ -121,29 +121,16 @@ breakpointMenuItem.deleteAll=Remove all 
 loadingText=Loading\u2026
 
 # LOCALIZATION NOTE (loadingError):
 # This is the error message that is displayed on failed attempts to load an
 # external resource file.
 # %1$S=URL, %2$S=status code
 loadingError=Error loading %1$S: %2$S
 
-# LOCALIZATION NOTE (remoteIncomingPromptTitle): The title displayed on the
-# dialog that prompts the user to allow the incoming connection.
-remoteIncomingPromptTitle=Incoming Connection
-
-# 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 (emptyVariablesText): The text that is displayed in the
 # variables pane when there are no variables to display.
 emptyVariablesText=No variables to display.
 
 # LOCALIZATION NOTE (scopeLabel): The text that is displayed in the variables
 # pane as a header for each variable scope (e.g. "Global scope, "With scope",
 # etc.).
 scopeLabel=%S scope
--- a/browser/modules/BrowserNewTabPreloader.jsm
+++ b/browser/modules/BrowserNewTabPreloader.jsm
@@ -2,159 +2,225 @@
  * 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";
 
 let EXPORTED_SYMBOLS = ["BrowserNewTabPreloader"];
 
 const Cu = Components.utils;
+const Cc = Components.classes;
+const Ci = Components.interfaces;
 
 Cu.import("resource://gre/modules/Services.jsm");
-
-const PREF_NEWTAB_URL = "browser.newtab.url";
-const PREF_NEWTAB_PRELOAD = "browser.newtab.preload";
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
-function BrowserNewTabPreloader() {
-}
-
-BrowserNewTabPreloader.prototype = {
-  _url: null,
-  _window: null,
-  _browser: null,
-  _enabled: null,
+const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+const PREF_BRANCH = "browser.newtab.";
 
-  init: function Preloader_init(aWindow) {
-    if (this._window) {
-      return;
-    }
+let BrowserNewTabPreloader =  {
+  init: function Preloader_init() {
+    Preferences.init();
 
-    this._window = aWindow;
-    this._enabled = Preferences.enabled;
-    this._url = Preferences.url;
-    Preferences.addObserver(this);
-
-    if (this._enabled) {
-      this._createBrowser();
+    if (Preferences.enabled) {
+      HiddenBrowser.create();
     }
   },
 
   uninit: function Preloader_uninit() {
-    if (!this._window) {
-      return;
+    HostFrame.destroy();
+    Preferences.uninit();
+    HiddenBrowser.destroy();
+  },
+
+  newTab: function Preloader_newTab(aTab) {
+    HiddenBrowser.swapWithNewTab(aTab);
+  }
+};
+
+Object.freeze(BrowserNewTabPreloader);
+
+let Preferences = {
+  _enabled: null,
+  _branch: null,
+  _url: null,
+
+  get enabled() {
+    if (this._enabled === null) {
+      this._enabled = this._branch.getBoolPref("preload") &&
+                      !this._branch.prefHasUserValue("url") &&
+                      this.url && this.url != "about:blank";
+    }
+
+    return this._enabled;
+  },
+
+  get url() {
+    if (this._url === null) {
+      this._url = this._branch.getCharPref("url");
     }
 
+    return this._url;
+  },
+
+  init: function Preferences_init() {
+    this._branch = Services.prefs.getBranch(PREF_BRANCH);
+    this._branch.addObserver("", this, false);
+  },
+
+  uninit: function Preferences_uninit() {
+    if (this._branch) {
+      this._branch.removeObserver("", this);
+      this._branch = null;
+    }
+  },
+
+  observe: function Preferences_observe(aSubject, aTopic, aData) {
+    let {url, enabled} = this;
+    this._url = this._enabled = null;
+
+    if (enabled && !this.enabled) {
+      HiddenBrowser.destroy();
+    } else if (!enabled && this.enabled) {
+      HiddenBrowser.create();
+    } else if (this._browser && url != this.url) {
+      HiddenBrowser.update(this.url);
+    }
+  },
+};
+
+let HiddenBrowser = {
+  get isPreloaded() {
+    return this._browser &&
+           this._browser.contentDocument &&
+           this._browser.contentDocument.readyState == "complete" &&
+           this._browser.currentURI.spec == Preferences.url;
+  },
+
+  swapWithNewTab: function HiddenBrowser_swapWithNewTab(aTab) {
+    if (this.isPreloaded) {
+      let tabbrowser = aTab.ownerDocument.defaultView.gBrowser;
+      if (tabbrowser) {
+        tabbrowser.swapNewTabWithBrowser(aTab, this._browser);
+      }
+    }
+  },
+
+  create: function HiddenBrowser_create() {
+    HostFrame.getFrame(function (aFrame) {
+      let doc = aFrame.document;
+      this._browser = doc.createElementNS(XUL_NS, "browser");
+      this._browser.setAttribute("type", "content");
+      this._browser.setAttribute("src", Preferences.url);
+      doc.documentElement.appendChild(this._browser);
+    }.bind(this));
+  },
+
+  update: function HiddenBrowser_update(aURL) {
+    this._browser.setAttribute("src", aURL);
+  },
+
+  destroy: function HiddenBrowser_destroy() {
     if (this._browser) {
       this._browser.parentNode.removeChild(this._browser);
       this._browser = null;
     }
+  }
+};
 
-    this._window = null;
-    Preferences.removeObserver(this);
+let HostFrame = {
+  _listener: null,
+  _privilegedFrame: null,
+
+  _privilegedContentTypes: {
+    "application/vnd.mozilla.xul+xml": true,
+    "application/xhtml+xml": true
   },
 
-  newTab: function Preloader_newTab(aTab) {
-    if (!this._window || !this._enabled) {
-      return;
-    }
-
-    let tabbrowser = this._window.gBrowser;
-    if (tabbrowser && this._isPreloaded()) {
-      tabbrowser.swapNewTabWithBrowser(aTab, this._browser);
-    }
+  get _frame() {
+    delete this._frame;
+    return this._frame = Services.appShell.hiddenDOMWindow;
   },
 
-  observe: function Preloader_observe(aEnabled, aURL) {
-    if (this._url != aURL) {
-      this._url = aURL;
+  get _isReady() {
+    let readyState = this._frame.document.readyState;
+    return (readyState == "complete" || readyState == "interactive");
+  },
 
-      if (this._enabled && aEnabled) {
-        // We're still enabled but the newtab URL has changed.
-        this._browser.setAttribute("src", aURL);
-        return;
-      }
+  get _isPrivileged() {
+    return (this._frame.location.protocol == "chrome:" &&
+            this._frame.document.contentType in this._privilegedContentTypes);
+  },
+
+  getFrame: function HostFrame_getFrame(aCallback) {
+    if (this._isReady && !this._isPrivileged) {
+      this._createPrivilegedFrame();
     }
 
-    if (this._enabled && !aEnabled) {
-      // We got disabled. Remove the browser.
-      this._browser.parentNode.removeChild(this._browser);
-      this._browser = null;
-      this._enabled = false;
-    } else if (!this._enabled && aEnabled) {
-      // We got enabled. Create a browser and start preloading.
-      this._createBrowser();
-      this._enabled = true;
+    if (this._isReady) {
+      aCallback(this._frame);
+    } else {
+      this._waitUntilLoaded(aCallback);
     }
   },
 
-  _createBrowser: function Preloader_createBrowser() {
-    let document = this._window.document;
-    this._browser = document.createElement("browser");
-    this._browser.setAttribute("type", "content");
-    this._browser.setAttribute("src", this._url);
-    this._browser.collapsed = true;
-
-    let panel = document.getElementById("browser-panel");
-    panel.appendChild(this._browser);
+  destroy: function HostFrame_destroy() {
+    delete this._frame;
+    this._listener = null;
   },
 
-  _isPreloaded: function Preloader_isPreloaded()  {
-    return this._browser &&
-           this._browser.contentDocument &&
-           this._browser.contentDocument.readyState == "complete" &&
-           this._browser.currentURI.spec == this._url;
+  _createPrivilegedFrame: function HostFrame_createPrivilegedFrame() {
+    let doc = this._frame.document;
+    let iframe = doc.createElement("iframe");
+    iframe.setAttribute("src", "chrome://browser/content/newtab/preload.xhtml");
+    doc.documentElement.appendChild(iframe);
+    this._frame = iframe.contentWindow;
+  },
+
+  _waitUntilLoaded: function HostFrame_waitUntilLoaded(aCallback) {
+    this._listener = new HiddenWindowLoadListener(this._frame, function () {
+      HostFrame.getFrame(aCallback);
+    });
   }
 };
 
-let Preferences = {
-  _observers: [],
+
+function HiddenWindowLoadListener(aWindow, aCallback) {
+  this._window = aWindow;
+  this._callback = aCallback;
+
+  let docShell = Services.appShell.hiddenWindow.docShell;
+  this._webProgress = docShell.QueryInterface(Ci.nsIWebProgress);
+  this._webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATE_ALL);
+}
 
-  get _branch() {
-    delete this._branch;
-    return this._branch = Services.prefs.getBranch("browser.newtab.");
+HiddenWindowLoadListener.prototype = {
+  _window: null,
+  _callback: null,
+  _webProgress: null,
+
+  _destroy: function HiddenWindowLoadListener_destroy() {
+    this._webProgress.removeProgressListener(this);
+    this._window = null;
+    this._callback = null;
+    this._webProgress = null;
   },
 
-  get enabled() {
-    if (!this._branch.getBoolPref("preload")) {
-      return false;
-    }
-
-    if (this._branch.prefHasUserValue("url")) {
-      return false;
-    }
-
-    let url = this.url;
-    return url && url != "about:blank";
-  },
-
-  get url() {
-    return this._branch.getCharPref("url");
-  },
-
-  addObserver: function Preferences_addObserver(aObserver) {
-    let index = this._observers.indexOf(aObserver);
-    if (index == -1) {
-      if (this._observers.length == 0) {
-        this._branch.addObserver("", this, false);
-      }
-      this._observers.push(aObserver);
+  onStateChange:
+  function HiddenWindowLoadListener_onStateChange(aWebProgress, aRequest,
+                                                  aStateFlags, aStatus) {
+    if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+        aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+        aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW &&
+        this._window == aWebProgress.DOMWindow) {
+      this._callback();
+      this._destroy();
     }
   },
 
-  removeObserver: function Preferences_removeObserver(aObserver) {
-    let index = this._observers.indexOf(aObserver);
-    if (index > -1) {
-      if (this._observers.length == 1) {
-        this._branch.removeObserver("", this);
-      }
-      this._observers.splice(index, 1);
-    }
-  },
+  onStatusChange: function () {},
+  onLocationChange: function () {},
+  onProgressChange: function () {},
+  onSecurityChange: function () {},
 
-  observe: function Preferences_observe(aSubject, aTopic, aData) {
-    let url = this.url;
-    let enabled = this.enabled;
-
-    for (let obs of this._observers) {
-      obs.observe(enabled, url);
-    }
-  }
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
+                                         Ci.nsISupportsWeakReference])
 };
--- a/mobile/android/chrome/content/dbg-browser-actors.js
+++ b/mobile/android/chrome/content/dbg-browser-actors.js
@@ -38,60 +38,65 @@ DeviceRootActor.prototype = new BrowserR
  * until at least the next listTabs request.
  */
 DeviceRootActor.prototype.onListTabs = function DRA_onListTabs() {
   // Get actors for all the currently-running tabs (reusing
   // existing actors where applicable), and store them in
   // an ActorPool.
 
   let actorPool = new ActorPool(this.conn);
-  let actorList = [];
+  let tabActorList = [];
 
   let win = windowMediator.getMostRecentWindow("navigator:browser");
   this.browser = win.BrowserApp.selectedBrowser;
 
   // Watch the window for tab closes so we can invalidate
   // actors as needed.
   this.watchWindow(win);
 
   let tabs = win.BrowserApp.tabs;
   let selected;
 
   for each (let tab in tabs) {
     let browser = tab.browser;
 
     if (browser == this.browser) {
-      selected = actorList.length;
+      selected = tabActorList.length;
     }
 
     let actor = this._tabActors.get(browser);
     if (!actor) {
       actor = new BrowserTabActor(this.conn, browser);
       actor.parentID = this.actorID;
       this._tabActors.set(browser, actor);
     }
 
     actorPool.addActor(actor);
-    actorList.push(actor);
+    tabActorList.push(actor);
   }
 
+  this._createExtraActors(DebuggerServer.globalActorFactories, actorPool);
+
   // Now drop the old actorID -> actor map.  Actors that still
   // mattered were added to the new map, others will go
   // away.
   if (this._tabActorPool) {
     this.conn.removeActorPool(this._tabActorPool);
   }
 
   this._tabActorPool = actorPool;
   this.conn.addActorPool(this._tabActorPool);
 
-  return { "from": "root",
-           "selected": selected,
-           "tabs": [actor.grip()
-                    for each (actor in actorList)] };
+  let response = {
+    "from": "root",
+    "selected": selected,
+    "tabs": [actor.grip() for (actor of tabActorList)]
+  };
+  this._appendExtraActors(response);
+  return response;
 };
 
 /**
  * Return the tab container for the specified window.
  */
 DeviceRootActor.prototype.getTabContainer = function DRA_getTabContainer(aWindow) {
   return aWindow.document.getElementById("browsers");
 };
--- a/toolkit/devtools/debugger/dbg-client.jsm
+++ b/toolkit/devtools/debugger/dbg-client.jsm
@@ -161,17 +161,18 @@ const ThreadStateTypes = {
 
 /**
  * Set of protocol messages that are sent by the server without a prior request
  * by the client.
  */
 const UnsolicitedNotifications = {
   "newScript": "newScript",
   "tabDetached": "tabDetached",
-  "tabNavigated": "tabNavigated"
+  "tabNavigated": "tabNavigated",
+  "profilerStateChanged": "profilerStateChanged"
 };
 
 /**
  * Set of pause types that are sent by the server and not as an immediate
  * response to a client request.
  */
 const UnsolicitedPauses = {
   "resumeLimit": "resumeLimit",
--- a/toolkit/devtools/debugger/server/dbg-browser-actors.js
+++ b/toolkit/devtools/debugger/server/dbg-browser-actors.js
@@ -26,37 +26,42 @@ function createRootActor(aConnection)
  * @param aConnection DebuggerServerConnection
  *        The conection to the client.
  */
 function BrowserRootActor(aConnection)
 {
   this.conn = aConnection;
   this._tabActors = new WeakMap();
   this._tabActorPool = null;
-  this._actorFactories = null;
+  // A map of actor names to actor instances provided by extensions.
+  this._extraActors = {};
 
   this.onTabClosed = this.onTabClosed.bind(this);
   windowMediator.addListener(this);
 }
 
 BrowserRootActor.prototype = {
+
   /**
    * Return a 'hello' packet as specified by the Remote Debugging Protocol.
    */
   sayHello: function BRA_sayHello() {
-    return { from: "root",
-             applicationType: "browser",
-             traits: [] };
+    return {
+      from: "root",
+      applicationType: "browser",
+      traits: {}
+    };
   },
 
   /**
    * Disconnects the actor from the browser window.
    */
   disconnect: function BRA_disconnect() {
     windowMediator.removeListener(this);
+    this._extraActors = null;
 
     // We may have registered event listeners on browser windows to
     // watch for tab closes, remove those.
     let e = windowMediator.getEnumerator("navigator:browser");
     while (e.hasMoreElements()) {
       let win = e.getNext();
       this.unwatchWindow(win);
       // Signal our imminent shutdown.
@@ -72,17 +77,17 @@ BrowserRootActor.prototype = {
    * until at least the next listTabs request.
    */
   onListTabs: function BRA_onListTabs() {
     // Get actors for all the currently-running tabs (reusing
     // existing actors where applicable), and store them in
     // an ActorPool.
 
     let actorPool = new ActorPool(this.conn);
-    let actorList = [];
+    let tabActorList = [];
 
     // Walk over open browser windows.
     let e = windowMediator.getEnumerator("navigator:browser");
     let top = windowMediator.getMostRecentWindow("navigator:browser");
     let selected;
     while (e.hasMoreElements()) {
       let win = e.getNext();
 
@@ -90,42 +95,74 @@ BrowserRootActor.prototype = {
       // actors as needed.
       this.watchWindow(win);
 
       // List the tabs in this browser.
       let selectedBrowser = win.getBrowser().selectedBrowser;
       let browsers = win.getBrowser().browsers;
       for each (let browser in browsers) {
         if (browser == selectedBrowser && win == top) {
-          selected = actorList.length;
+          selected = tabActorList.length;
         }
         let actor = this._tabActors.get(browser);
         if (!actor) {
           actor = new BrowserTabActor(this.conn, browser, win.gBrowser);
           actor.parentID = this.actorID;
           this._tabActors.set(browser, actor);
         }
         actorPool.addActor(actor);
-        actorList.push(actor);
+        tabActorList.push(actor);
       }
     }
 
+    this._createExtraActors(DebuggerServer.globalActorFactories, actorPool);
+
     // Now drop the old actorID -> actor map.  Actors that still
     // mattered were added to the new map, others will go
     // away.
     if (this._tabActorPool) {
       this.conn.removeActorPool(this._tabActorPool);
     }
     this._tabActorPool = actorPool;
     this.conn.addActorPool(this._tabActorPool);
 
-    return { "from": "root",
-             "selected": selected,
-             "tabs": [actor.grip()
-                      for each (actor in actorList)] };
+    let response = {
+      "from": "root",
+      "selected": selected,
+      "tabs": [actor.grip() for (actor of tabActorList)]
+    };
+    this._appendExtraActors(response);
+    return response;
+  },
+
+  /**
+   * Adds dynamically-added actors from add-ons to the provided pool.
+   */
+  _createExtraActors: function BRA_createExtraActors(aFactories, aPool) {
+    // Walk over global actors added by extensions.
+    for (let name in aFactories) {
+      let actor = this._extraActors[name];
+      if (!actor) {
+        actor = aFactories[name].bind(null, this.conn);
+        actor.prototype = aFactories[name].prototype;
+        actor.parentID = this.actorID;
+        this._extraActors[name] = actor;
+      }
+      aPool.addActor(actor);
+    }
+  },
+
+  /**
+   * Appends the extra actors to the specified object.
+   */
+  _appendExtraActors: function BRA_appendExtraActors(aObject) {
+    for (let name in this._extraActors) {
+      let actor = this._extraActors[name];
+      aObject[name] = actor.actorID;
+    }
   },
 
   /**
    * Watch a window that was visited during onListTabs for
    * tab closures.
    */
   watchWindow: function BRA_watchWindow(aWindow) {
     this.getTabContainer(aWindow).addEventListener("TabClose",
@@ -198,17 +235,22 @@ BrowserRootActor.prototype.requestTypes 
  * @param aTabBrowser tabbrowser
  *        The tabbrowser that can receive nsIWebProgressListener events.
  */
 function BrowserTabActor(aConnection, aBrowser, aTabBrowser)
 {
   this.conn = aConnection;
   this._browser = aBrowser;
   this._tabbrowser = aTabBrowser;
+  this._tabActorPool = null;
+  // A map of actor names to actor instances provided by extensions.
+  this._extraActors = {};
 
+  this._createExtraActors = BrowserRootActor.prototype._createExtraActors.bind(this);
+  this._appendExtraActors = BrowserRootActor.prototype._appendExtraActors.bind(this);
   this._onWindowCreated = this.onWindowCreated.bind(this);
 }
 
 // XXX (bug 710213): BrowserTabActor attach/detach/exit/disconnect is a
 // *complete* mess, needs to be rethought asap.
 
 BrowserTabActor.prototype = {
   get browser() { return this._browser; },
@@ -240,37 +282,53 @@ BrowserTabActor.prototype = {
    *
    * @param string aActor
    *        The actor ID.
    */
   removeFromBreakpointPool: function BTA_removeFromBreakpointPool(aActor) {
     this.conn.removeActor(aActor);
   },
 
+  // A constant prefix that will be used to form the actor ID by the server.
   actorPrefix: "tab",
 
   grip: function BTA_grip() {
     dbg_assert(!this.exited,
                "grip() shouldn't be called on exited browser actor.");
     dbg_assert(this.actorID,
                "tab should have an actorID.");
-    return { actor: this.actorID,
-             title: this.browser.contentTitle,
-             url: this.browser.currentURI.spec }
+
+    let response = {
+      actor: this.actorID,
+      title: this.browser.contentTitle,
+      url: this.browser.currentURI.spec
+    };
+
+    // Walk over tab actors added by extensions and add them to a new ActorPool.
+    let actorPool = new ActorPool(this.conn);
+    this._createExtraActors(DebuggerServer.tabActorFactories, actorPool);
+    if (!actorPool.isEmpty()) {
+      this._tabActorPool = actorPool;
+      this.conn.addActorPool(this._tabActorPool);
+    }
+
+    this._appendExtraActors(response);
+    return response;
   },
 
   /**
    * Called when the actor is removed from the connection.
    */
   disconnect: function BTA_disconnect() {
     this._detach();
 
     if (this._progressListener) {
       this._progressListener.destroy();
     }
+    this._extraActors = null;
   },
 
   /**
    * Called by the root actor when the underlying tab is closed.
    */
   exit: function BTA_exit() {
     if (this.exited) {
       return;
@@ -365,16 +423,20 @@ BrowserTabActor.prototype = {
     this.browser.removeEventListener("DOMWindowCreated", this._onWindowCreated, true);
     this.browser.removeEventListener("pageshow", this._onWindowCreated, true);
 
     this._popContext();
 
     // Shut down actors that belong to this tab's pool.
     this.conn.removeActorPool(this._tabPool);
     this._tabPool = null;
+    if (this._tabActorPool) {
+      this.conn.removeActorPool(this._tabActorPool);
+      this._tabActorPool = null;
+    }
 
     this._attached = false;
   },
 
   // Protocol Request Handlers
 
   onAttach: function BTA_onAttach(aRequest) {
     if (this.exited) {
@@ -530,25 +592,106 @@ DebuggerProgressListener.prototype = {
    */
   destroy: function DPL_destroy() {
     this._tabActor._tabbrowser.removeProgressListener(this);
     this._tabActor._progressListener = null;
     this._tabActor = null;
   }
 };
 
+// DebuggerServer extension API.
+
 /**
- * Registers handlers for new request types defined dynamically. This is used
- * for example by add-ons to augment the functionality of the tab actor.
+ * Registers handlers for new tab-scoped request types defined dynamically.
+ * This is used for example by add-ons to augment the functionality of the tab
+ * actor.
+ * TODO: remove this API in the next release after bug 753401 lands, once all
+ * our experimental add-ons have been converted to the new API.
  *
  * @param aName string
  *        The name of the new request type.
  * @param aFunction function
  *        The handler for this request type.
  */
 DebuggerServer.addTabRequest = function DS_addTabRequest(aName, aFunction) {
   BrowserTabActor.prototype.requestTypes[aName] = function(aRequest) {
     if (!this.attached) {
       return { error: "wrongState" };
     }
     return aFunction(this, aRequest);
   }
 };
+
+/**
+ * Registers handlers for new tab-scoped request types defined dynamically.
+ * This is used for example by add-ons to augment the functionality of the tab
+ * actor.
+ *
+ * @param aFunction function
+ *        The constructor function for this request type.
+ * @param aName string [optional]
+ *        The name of the new request type. If this is not present, the
+ *        actorPrefix property of the constructor prototype is used.
+ */
+DebuggerServer.addTabActor = function DS_addTabActor(aFunction, aName) {
+  let name = aName ? aName : aFunction.prototype.actorPrefix;
+  if (["title", "url", "actor"].indexOf(name) != -1) {
+    throw Error(name + " is not allowed");
+  }
+  if (DebuggerServer.tabActorFactories.hasOwnProperty(name)) {
+    throw Error(name + " already exists");
+  }
+  DebuggerServer.tabActorFactories[name] = aFunction;
+};
+
+/**
+ * Unregisters the handler for the specified tab-scoped request type.
+ * This may be used for example by add-ons when shutting down or upgrading.
+ *
+ * @param aFunction function
+ *        The constructor function for this request type.
+ */
+DebuggerServer.removeTabActor = function DS_removeTabActor(aFunction) {
+  for (let name in DebuggerServer.tabActorFactories) {
+    let handler = DebuggerServer.tabActorFactories[name];
+    if (handler.name == aFunction.name) {
+      delete DebuggerServer.tabActorFactories[name];
+    }
+  }
+};
+
+/**
+ * Registers handlers for new browser-scoped request types defined dynamically.
+ * This is used for example by add-ons to augment the functionality of the root
+ * actor.
+ *
+ * @param aFunction function
+ *        The constructor function for this request type.
+ * @param aName string [optional]
+ *        The name of the new request type. If this is not present, the
+ *        actorPrefix property of the constructor prototype is used.
+ */
+DebuggerServer.addGlobalActor = function DS_addGlobalActor(aFunction, aName) {
+  let name = aName ? aName : aFunction.prototype.actorPrefix;
+  if (["from", "tabs", "selected"].indexOf(name) != -1) {
+    throw Error(name + " is not allowed");
+  }
+  if (DebuggerServer.globalActorFactories.hasOwnProperty(name)) {
+    throw Error(name + " already exists");
+  }
+  DebuggerServer.globalActorFactories[name] = aFunction;
+};
+
+/**
+ * Unregisters the handler for the specified browser-scoped request type.
+ * This may be used for example by add-ons when shutting down or upgrading.
+ *
+ * @param aFunction function
+ *        The constructor function for this request type.
+ */
+DebuggerServer.removeGlobalActor = function DS_removeGlobalActor(aFunction) {
+  for (let name in DebuggerServer.globalActorFactories) {
+    let handler = DebuggerServer.globalActorFactories[name];
+    if (handler.name == aFunction.name) {
+      delete DebuggerServer.globalActorFactories[name];
+    }
+  }
+};
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/debugger/server/dbg-profiler-actors.js
@@ -0,0 +1,90 @@
+/* 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";
+
+/**
+ * Creates a ProfilerActor. ProfilerActor provides remote access to the
+ * built-in profiler module.
+ */
+function ProfilerActor(aConnection)
+{
+  this._conn = aConnection;
+  this._profiler = Cc["@mozilla.org/tools/profiler;1"].getService(Ci.nsIProfiler);
+  this._started = false;
+
+  Cu.import("resource://gre/modules/Services.jsm");
+  Services.obs.addObserver(this, "profiler-started", false);
+  Services.obs.addObserver(this, "profiler-stopped", false);
+}
+
+ProfilerActor.prototype = {
+  actorPrefix: "profiler",
+
+  disconnect: function() {
+    if (this._profiler && this._started) {
+      this._profiler.StopProfiler();
+    }
+    this._profiler = null;
+  },
+
+  onStartProfiler: function(aRequest) {
+    this._profiler.StartProfiler(aRequest.entries, aRequest.interval,
+                           aRequest.features, aRequest.features.length);
+    this._started = true;
+    return { "msg": "profiler started" }
+  },
+  onStopProfiler: function(aRequest) {
+    this._profiler.StopProfiler();
+    this._started = false;
+    return { "msg": "profiler stopped" }
+  },
+  onGetProfileStr: function(aRequest) {
+    var profileStr = this._profiler.GetProfile();
+    return { "profileStr": profileStr }
+  },
+  onGetProfile: function(aRequest) {
+    var profile = this._profiler.getProfileData();
+    return { "profile": profile }
+  },
+  onIsActive: function(aRequest) {
+    var isActive = this._profiler.IsActive();
+    return { "isActive": isActive }
+  },
+  onGetResponsivenessTimes: function(aRequest) {
+    var times = this._profiler.GetResponsivenessTimes({});
+    return { "responsivenessTimes": times }
+  },
+  onGetFeatures: function(aRequest) {
+    var features = this._profiler.GetFeatures([]);
+    return { "features": features }
+  },
+  onGetSharedLibraryInformation: function(aRequest) {
+    var sharedLibraries = this._profiler.getSharedLibraryInformation();
+    return { "sharedLibraryInformation": sharedLibraries }
+  },
+  observe: function(aSubject, aTopic, aData) {
+    if (aTopic == "profiler-started") {
+      this.conn.send({ from: this.actorID, type: "profilerStateChanged", isActive: true });
+    } else if (aTopic == "profiler-stopped") {
+      this.conn.send({ from: this.actorID, type: "profilerStateChanged", isActive: false });
+    }
+  },
+};
+
+/**
+ * The request types this actor can handle.
+ */
+ProfilerActor.prototype.requestTypes = {
+  "startProfiler": ProfilerActor.prototype.onStartProfiler,
+  "stopProfiler": ProfilerActor.prototype.onStopProfiler,
+  "getProfileStr": ProfilerActor.prototype.onGetProfileStr,
+  "getProfile": ProfilerActor.prototype.onGetProfile,
+  "isActive": ProfilerActor.prototype.onIsActive,
+  "getResponsivenessTimes": ProfilerActor.prototype.onGetResponsivenessTimes,
+  "getFeatures": ProfilerActor.prototype.onGetFeatures,
+  "getSharedLibraryInformation": ProfilerActor.prototype.onGetSharedLibraryInformation
+};
+
+DebuggerServer.addGlobalActor(ProfilerActor, "profilerActor");
--- a/toolkit/devtools/debugger/server/dbg-server.js
+++ b/toolkit/devtools/debugger/server/dbg-server.js
@@ -10,18 +10,20 @@
  * debugging global.
  */
 
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 const CC = Components.Constructor;
 const Cu = Components.utils;
 const Cr = Components.results;
+const DBG_STRINGS_URI = "chrome://global/locale/devtools/debugger.properties";
 
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 let wantLogging = Services.prefs.getBoolPref("devtools.debugger.log");
 
 Cu.import("resource://gre/modules/jsdebugger.jsm");
 addDebuggerToGlobal(this);
 
 function dumpn(str) {
   if (wantLogging) {
     dump("DBG-SERVER: " + str + "\n");
@@ -54,36 +56,70 @@ const ServerSocket = CC("@mozilla.org/ne
 
 /***
  * Public API
  */
 var DebuggerServer = {
   _listener: null,
   _transportInitialized: false,
   xpcInspector: null,
-  _allowConnection: null,
+  // Number of currently open TCP connections.
+  _socketConnections: 0,
+  // Map of global actor names to actor constructors provided by extensions.
+  globalActorFactories: null,
+  // Map of tab actor names to actor constructors provided by extensions.
+  tabActorFactories: null,
 
   LONG_STRING_LENGTH: 10000,
   LONG_STRING_INITIAL_LENGTH: 1000,
 
   /**
+   * Prompt the user to accept or decline the incoming connection.
+   *
+   * @return true if the connection should be permitted, false otherwise
+   */
+  _allowConnection: function DH__allowConnection() {
+    let title = L10N.getStr("remoteIncomingPromptTitle");
+    let msg = L10N.getStr("remoteIncomingPromptMessage");
+    let disableButton = L10N.getStr("remoteIncomingPromptDisable");
+    let prompt = Services.prompt;
+    let flags = prompt.BUTTON_POS_0 * prompt.BUTTON_TITLE_OK +
+                prompt.BUTTON_POS_1 * prompt.BUTTON_TITLE_CANCEL +
+                prompt.BUTTON_POS_2 * prompt.BUTTON_TITLE_IS_STRING +
+                prompt.BUTTON_POS_1_DEFAULT;
+    let result = prompt.confirmEx(null, title, msg, flags, null, null,
+                                  disableButton, null, { value: false });
+    if (result == 0) {
+      return true;
+    }
+    if (result == 2) {
+      DebuggerServer.closeListener(true);
+      Services.prefs.setBoolPref("devtools.debugger.remote-enabled", false);
+    }
+    return false;
+  },
+
+  /**
    * Initialize the debugger server.
    *
    * @param function aAllowConnectionCallback
    *        The embedder-provider callback, that decides whether an incoming
    *        remote protocol conection should be allowed or refused.
    */
   init: function DH_init(aAllowConnectionCallback) {
     if (this.initialized) {
       return;
     }
 
     this.xpcInspector = Cc["@mozilla.org/jsinspector;1"].getService(Ci.nsIJSInspector);
     this.initTransport(aAllowConnectionCallback);
     this.addActors("chrome://global/content/devtools/dbg-script-actors.js");
+
+    this.globalActorFactories = {};
+    this.tabActorFactories = {};
   },
 
   /**
    * Initialize the debugger server's transport variables.  This can be
    * in place of init() for cases where the jsdebugger isn't needed.
    *
    * @param function aAllowConnectionCallback
    *        The embedder-provider callback, that decides whether an incoming
@@ -92,20 +128,37 @@ var DebuggerServer = {
   initTransport: function DH_initTransport(aAllowConnectionCallback) {
     if (this._transportInitialized) {
       return;
     }
 
     this._connections = {};
     this._nextConnID = 0;
     this._transportInitialized = true;
-    this._allowConnection = aAllowConnectionCallback;
+    if (aAllowConnectionCallback) {
+      this._allowConnection = aAllowConnectionCallback;
+    }
   },
 
-  get initialized() { return !!this.xpcInspector; },
+  get initialized() { return !!this.globalActorFactories; },
+
+  /**
+   * Performs cleanup tasks before shutting down the debugger server, if no
+   * connections are currently open. 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 DH_destroy() {
+    if (Object.keys(this._connections).length == 0) {
+      dumpn("Shutting down debugger server.");
+      delete this.globalActorFactories;
+      delete this.tabActorFactories;
+    }
+  },
 
   /**
    * Load a subscript into the debugging global.
    *
    * @param aURL string A url that will be loaded as a subscript into the
    *        debugging global.  The user must load at least one script
    *        that implements a createRootActor() function to create the
    *        server's root actor.
@@ -114,64 +167,77 @@ var DebuggerServer = {
     loadSubScript.call(this, aURL);
   },
 
   /**
    * Install Firefox-specific actors.
    */
   addBrowserActors: function DH_addBrowserActors() {
     this.addActors("chrome://global/content/devtools/dbg-browser-actors.js");
+    if ("nsIProfiler" in Ci)
+      this.addActors("chrome://global/content/devtools/dbg-profiler-actors.js");
   },
 
   /**
    * Listens on the given port for remote debugger connections.
    *
    * @param aPort int
    *        The port to listen on.
    */
   openListener: function DH_openListener(aPort) {
     if (!Services.prefs.getBoolPref("devtools.debugger.remote-enabled")) {
       return false;
     }
     this._checkInit();
 
+    // Return early if the server is already listening.
     if (this._listener) {
-      throw "Debugging listener already open.";
+      return true;
     }
 
     let localOnly = false;
     // A preference setting can force binding on the loopback interface.
     if (Services.prefs.getBoolPref("devtools.debugger.force-local")) {
       localOnly = true;
     }
 
     try {
       let socket = new ServerSocket(aPort, localOnly, 4);
       socket.asyncListen(this);
       this._listener = socket;
     } catch (e) {
       dumpn("Could not start debugging listener on port " + aPort + ": " + e);
       throw Cr.NS_ERROR_NOT_AVAILABLE;
     }
+    this._socketConnections++;
 
     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
+   *        number of open connections.
    */
-  closeListener: function DH_closeListener() {
+  closeListener: function DH_closeListener(aForce) {
     this._checkInit();
 
-    if (!this._listener) {
+    if (!this._listener || this._socketConnections == 0) {
       return false;
     }
 
-    this._listener.close();
-    this._listener = null;
+    // 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;
+    }
 
     return true;
   },
 
   /**
    * Creates a new connection to the local debugger speaking over an
    * nsIPipe.
    *
@@ -192,16 +258,19 @@ var DebuggerServer = {
 
     return new DebuggerTransport(toClient.inputStream, toServer.outputStream);
   },
 
 
   // nsIServerSocketListener implementation
 
   onSocketAccepted: function DH_onSocketAccepted(aSocket, aTransport) {
+    if (!this._allowConnection()) {
+      return;
+    }
     dumpn("New debugging connection on " + aTransport.host + ":" + aTransport.port);
 
     try {
       let input = aTransport.openInputStream(0, 0, 0);
       let output = aTransport.openOutputStream(0, 0, 0);
       let transport = new DebuggerTransport(input, output);
       DebuggerServer._onConnection(transport);
     } catch (e) {
@@ -224,35 +293,34 @@ var DebuggerServer = {
     }
   },
 
   /**
    * Create a new debugger connection for the given transport.  Called
    * after connectPipe() or after an incoming socket connection.
    */
   _onConnection: function DH_onConnection(aTransport) {
-    if (!this._allowConnection()) {
-      return;
-    }
     let connID = "conn" + this._nextConnID++ + '.';
     let conn = new DebuggerServerConnection(connID, aTransport);
     this._connections[connID] = conn;
 
     // Create a root actor for the connection and send the hello packet.
     conn.rootActor = this.createRootActor(conn);
     conn.addActor(conn.rootActor);
     aTransport.send(conn.rootActor.sayHello());
     aTransport.ready();
   },
 
   /**
-   * Remove the connection from the debugging server.
+   * Remove the connection from the debugging server and shut down the server
+   * if no other connections are open.
    */
   _connectionClosed: function DH_connectionClosed(aConnection) {
     delete this._connections[aConnection.prefix];
+    this.destroy();
   }
 };
 
 /**
  * Construct an ActorPool.
  *
  * ActorPools are actorID -> actor mapping and storage.  These are
  * used to accumulate and quickly dispose of groups of actors that
@@ -273,17 +341,21 @@ ActorPool.prototype = {
    * @param aActor object
    *        The actor implementation.  If the object has a
    *        'disconnected' property, it will be called when the actor
    *        pool is cleaned up.
    */
   addActor: function AP_addActor(aActor) {
     aActor.conn = this.conn;
     if (!aActor.actorID) {
-      aActor.actorID = this.conn.allocID(aActor.actorPrefix || undefined);
+      let prefix = aActor.actorPrefix;
+      if (typeof aActor == "function") {
+        prefix = aActor.prototype.actorPrefix;
+      }
+      aActor.actorID = this.conn.allocID(prefix || undefined);
     }
 
     if (aActor.registeredPool) {
       aActor.registeredPool.removeActor(aActor);
     }
     aActor.registeredPool = this;
 
     this._actors[aActor.actorID] = aActor;
@@ -296,16 +368,23 @@ ActorPool.prototype = {
     return this._actors[aActorID];
   },
 
   has: function AP_has(aActorID) {
     return aActorID in this._actors;
   },
 
   /**
+   * Returns true if the pool is empty.
+   */
+  isEmpty: function AP_isEmpty() {
+    return Object.keys(this._actors).length == 0;
+  },
+
+  /**
    * Remove an actor from the actor pool.
    */
   removeActor: function AP_remove(aActorID) {
     delete this._actors[aActorID];
     delete this._cleanups[aActorID];
   },
 
   /**
@@ -432,16 +511,34 @@ DebuggerServerConnection.prototype = {
   onPacket: function DSC_onPacket(aPacket) {
     let actor = this.getActor(aPacket.to);
     if (!actor) {
       this.transport.send({ from: aPacket.to ? aPacket.to : "root",
                             error: "noSuchActor" });
       return;
     }
 
+    // Dyamically-loaded actors have to be created lazily.
+    if (typeof actor == "function") {
+      let instance;
+      try {
+        instance = new actor();
+      } catch (e) {
+        Cu.reportError(e);
+        this.transport.send({
+          error: "unknownError",
+          message: ("error occurred while creating actor '" + actor.name +
+                    "': " + safeErrorString(e))
+        });
+      }
+      actor.registeredPool.addActor(instance);
+      actor.registeredPool.removeActor(actor);
+      actor = instance;
+    }
+
     var ret = null;
 
     // Dispatch the request to the actor.
     if (actor.requestTypes && actor.requestTypes[aPacket.type]) {
       try {
         ret = actor.requestTypes[aPacket.type].bind(actor)(aPacket);
       } catch(e) {
         Cu.reportError(e);
@@ -482,8 +579,28 @@ DebuggerServerConnection.prototype = {
     this._actorPool.cleanup();
     this._actorPool = null;
     this._extraPools.map(function(p) { p.cleanup(); });
     this._extraPools = null;
 
     DebuggerServer._connectionClosed(this);
   }
 };
+
+/**
+ * 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);
+});
--- a/toolkit/devtools/debugger/tests/unit/test_dbgsocket.js
+++ b/toolkit/devtools/debugger/tests/unit/test_dbgsocket.js
@@ -22,17 +22,22 @@ function really_long() {
   for (let i = 0; i < 18; i++) {
     ret += ret;
   }
   return ret;
 }
 
 function test_socket_conn()
 {
-  DebuggerServer.openListener(2929);
+  do_check_eq(DebuggerServer._socketConnections, 0);
+  do_check_true(DebuggerServer.openListener(2929));
+  do_check_eq(DebuggerServer._socketConnections, 1);
+  // Make sure opening the listener twice does nothing.
+  do_check_true(DebuggerServer.openListener(2929));
+  do_check_eq(DebuggerServer._socketConnections, 1);
 
   let unicodeString = "(╯°□°)╯︵ ┻━┻";
   let transport = debuggerSocketConnect("127.0.0.1", 2929);
   transport.hooks = {
     onPacket: function(aPacket) {
       this.onPacket = function(aPacket) {
         do_check_eq(aPacket.unicode, unicodeString);
         transport.close();
@@ -49,17 +54,22 @@ function test_socket_conn()
       run_next_test();
     },
   };
   transport.ready();
 }
 
 function test_socket_shutdown()
 {
-  DebuggerServer.closeListener();
+  do_check_eq(DebuggerServer._socketConnections, 1);
+  do_check_true(DebuggerServer.closeListener());
+  do_check_eq(DebuggerServer._socketConnections, 0);
+  // Make sure closing the listener twice does nothing.
+  do_check_false(DebuggerServer.closeListener());
+  do_check_eq(DebuggerServer._socketConnections, 0);
 
   let transport = debuggerSocketConnect("127.0.0.1", 2929);
   transport.hooks = {
     onPacket: function(aPacket) {
       // Shouldn't reach this, should never connect.
       do_check_true(false);
     },
 
--- a/toolkit/devtools/jar.mn
+++ b/toolkit/devtools/jar.mn
@@ -2,8 +2,9 @@
 # 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/.
 
 toolkit.jar:
   content/global/devtools/dbg-transport.js        (debugger/dbg-transport.js)
   content/global/devtools/dbg-server.js        		(debugger/server/dbg-server.js)
   content/global/devtools/dbg-script-actors.js    (debugger/server/dbg-script-actors.js)
   content/global/devtools/dbg-browser-actors.js   (debugger/server/dbg-browser-actors.js)
+  content/global/devtools/dbg-profiler-actors.js  (debugger/server/dbg-profiler-actors.js)
new file mode 100644
--- /dev/null
+++ b/toolkit/locales/en-US/chrome/global/devtools/debugger.properties
@@ -0,0 +1,24 @@
+# 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/.
+
+# LOCALIZATION NOTE These strings are used inside the Debugger
+# which is available from the Web Developer sub-menu -> '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 (remoteIncomingPromptTitle): The title displayed on the
+# dialog that prompts the user to allow the incoming connection.
+remoteIncomingPromptTitle=Incoming Connection
+
+# 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
--- a/toolkit/locales/jar.mn
+++ b/toolkit/locales/jar.mn
@@ -24,16 +24,17 @@
   locale/@AB_CD@/global/console.dtd                     (%chrome/global/console.dtd)
   locale/@AB_CD@/global/console.properties              (%chrome/global/console.properties)
   locale/@AB_CD@/global/contentAreaCommands.properties  (%chrome/global/contentAreaCommands.properties)
   locale/@AB_CD@/global/customizeCharset.dtd            (%chrome/global/customizeCharset.dtd)
   locale/@AB_CD@/global/customizeToolbar.dtd            (%chrome/global/customizeToolbar.dtd)
   locale/@AB_CD@/global/customizeToolbar.properties     (%chrome/global/customizeToolbar.properties)
   locale/@AB_CD@/global/datetimepicker.dtd              (%chrome/global/datetimepicker.dtd)
   locale/@AB_CD@/global/dateFormat.properties           (%chrome/global/dateFormat.properties)
+  locale/@AB_CD@/global/devtools/debugger.properties    (%chrome/global/devtools/debugger.properties)
   locale/@AB_CD@/global/dialogOverlay.dtd               (%chrome/global/dialogOverlay.dtd)
   locale/@AB_CD@/global/downloadProgress.properties     (%chrome/global/downloadProgress.properties)
   locale/@AB_CD@/global/editMenuOverlay.dtd             (%chrome/global/editMenuOverlay.dtd)
   locale/@AB_CD@/global/filefield.properties            (%chrome/global/filefield.properties)
   locale/@AB_CD@/global/filepicker.dtd                  (%chrome/global/filepicker.dtd)
   locale/@AB_CD@/global/filepicker.properties           (%chrome/global/filepicker.properties)
   locale/@AB_CD@/global/findbar.dtd                     (%chrome/global/findbar.dtd)
   locale/@AB_CD@/global/findbar.properties              (%chrome/global/findbar.properties)