Merge fx-team to m-c.
authorDave Camp <dcamp@mozilla.com>
Fri, 01 Jun 2012 21:40:26 -0700
changeset 95478 5199196b65ec6bafc1fe877729f336c4db7d9abd
parent 95477 68abc86fde1c736611ea09db5099d2363cc1c24e (current diff)
parent 95401 13d899f2545494d17090c8ed5156af649e5c8807 (diff)
child 95479 c9b7a566ff056cf34913f482c0ec65b2aa804046
child 95488 65f748c63f4ccb8964aea77abf4e4234baaa36a5
push id818
push userdcamp@campd.org
push dateSat, 02 Jun 2012 04:41:09 +0000
treeherderfx-team@5199196b65ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone15.0a1
Merge fx-team to m-c.
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -503,17 +503,18 @@ var WebappsHelper = {
         break;
     }
   }
 }
 
 // Start the debugger server.
 function startDebugger() {
   if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
+    // Allow remote connections.
+    DebuggerServer.init(function () { return true; });
     DebuggerServer.addActors('chrome://browser/content/dbg-browser-actors.js');
   }
 
   let port = Services.prefs.getIntPref('devtools.debugger.port') || 6000;
   try {
     DebuggerServer.openListener(port, false);
   } catch (e) {
     dump('Unable to start debugger server: ' + e + '\n');
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1031,19 +1031,22 @@ pref("devtools.inspector.enabled", true)
 pref("devtools.inspector.htmlHeight", 112);
 pref("devtools.inspector.htmlPanelOpen", false);
 pref("devtools.inspector.sidebarOpen", false);
 pref("devtools.inspector.activeSidebar", "ruleview");
 pref("devtools.inspector.highlighterShowVeil", true);
 pref("devtools.inspector.highlighterShowInfobar", true);
 
 // Enable the Layout View
-pref("devtools.layoutview.enabled", false);
+pref("devtools.layoutview.enabled", true);
 pref("devtools.layoutview.open", false);
 
+// Enable the Responsive UI tool
+pref("devtools.responsiveUI.enabled", true);
+
 // Enable the Debugger
 pref("devtools.debugger.enabled", true);
 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);
--- a/browser/base/content/browser-appmenu.inc
+++ b/browser/base/content/browser-appmenu.inc
@@ -155,16 +155,22 @@
                     command="Tools:WebConsole"
                     key="key_webConsole"/>
           <menuitem id="appmenu_pageInspect"
                     hidden="true"
                     label="&inspectMenu.label;"
                     type="checkbox"
                     command="Tools:Inspect"
                     key="key_inspect"/>
+          <menuitem id="appmenu_responsiveUI"
+                    hidden="true"
+                    label="&responsiveUI.label;"
+                    type="checkbox"
+                    command="Tools:ResponsiveUI"
+                    key="key_responsiveUI"/>
           <menuitem id="appmenu_debugger"
                     hidden="true"
                     type="checkbox"
                     label="&debuggerMenu.label2;"
                     key="key_debugger"
                     command="Tools:Debugger"/>
           <menuitem id="appmenu_remoteDebugger"
                     hidden="true"
--- a/browser/base/content/browser-menubar.inc
+++ b/browser/base/content/browser-menubar.inc
@@ -530,16 +530,23 @@
                             command="Tools:WebConsole"/>
                   <menuitem id="menu_pageinspect"
                             type="checkbox"
                             hidden="true"
                             label="&inspectMenu.label;"
                             accesskey="&inspectMenu.accesskey;"
                             key="key_inspect"
                             command="Tools:Inspect"/>
+                  <menuitem id="menu_responsiveUI"
+                            type="checkbox"
+                            hidden="true"
+                            label="&responsiveUI.label;"
+                            accesskey="&responsiveUI.accesskey;"
+                            key="key_responsiveUI"
+                            command="Tools:ResponsiveUI"/>
                   <menuitem id="menu_debugger"
                             hidden="true"
                             type="checkbox"
                             label="&debuggerMenu.label2;"
                             key="key_debugger"
                             command="Tools:Debugger"/>
                   <menuitem id="menu_remoteDebugger"
                             hidden="true"
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -91,16 +91,17 @@
     <command id="Tools:DevToolbar" oncommand="DeveloperToolbar.toggle();" disabled="true"/>
     <command id="Tools:WebConsole" oncommand="HUDConsoleUI.toggleHUD();"/>
     <command id="Tools:Inspect" oncommand="InspectorUI.toggleInspectorUI();" disabled="true"/>
     <command id="Tools:Debugger" oncommand="DebuggerUI.toggleDebugger();" disabled="true"/>
     <command id="Tools:RemoteDebugger" oncommand="DebuggerUI.toggleRemoteDebugger();" disabled="true"/>
     <command id="Tools:ChromeDebugger" oncommand="DebuggerUI.toggleChromeDebugger();" disabled="true"/>
     <command id="Tools:Scratchpad" oncommand="Scratchpad.openScratchpad();" disabled="true"/>
     <command id="Tools:StyleEditor" oncommand="StyleEditor.openChrome();" disabled="true"/>
+    <command id="Tools:ResponsiveUI" oncommand="ResponsiveUI.toggle();" disabled="true"/>
     <command id="Tools:Addons" oncommand="BrowserOpenAddonsMgr();"/>
     <command id="Tools:Sanitize"
      oncommand="Cc['@mozilla.org/browser/browserglue;1'].getService(Ci.nsIBrowserGlue).sanitize(window);"/>
     <command id="Tools:PrivateBrowsing" oncommand="gPrivateBrowsingUI.toggleMode();"/>
     <command id="History:UndoCloseTab" oncommand="undoCloseTab();"/>
     <command id="History:UndoCloseWindow" oncommand="undoCloseWindow();"/>
     <command id="Browser:ToggleAddonBar" oncommand="toggleAddonBar();"/>
   </commandset>
@@ -247,16 +248,23 @@
     />
     <key id="key_inspect" key="&inspectMenu.commandkey;" command="Inspector:Inspect"
 #ifdef XP_MACOSX
         modifiers="accel,alt"
 #else
         modifiers="accel,shift"
 #endif
     />
+    <key id="key_responsiveUI" key="&responsiveUI.commandkey;" command="Tools:ResponsiveUI"
+#ifdef XP_MACOSX
+        modifiers="accel,alt"
+#else
+        modifiers="accel,shift"
+#endif
+    />
     <key id="key_scratchpad" keycode="&scratchpad.keycode;" modifiers="shift"
          keytext="&scratchpad.keytext;" command="Tools:Scratchpad"/>
     <key id="key_styleeditor" keycode="&styleeditor.keycode;" modifiers="shift"
          keytext="&styleeditor.keytext;" command="Tools:StyleEditor"/>
     <key id="openFileKb" key="&openFileCmd.commandkey;" command="Browser:OpenFile"  modifiers="accel"/>
     <key id="key_savePage" key="&savePageCmd.commandkey;" command="Browser:SavePage" modifiers="accel"/>
     <key id="printKb" key="&printCmd.commandkey;" command="cmd_print"  modifiers="accel"/>
     <key id="key_close" key="&closeCmd.key;" command="cmd_close" modifiers="accel"/>
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -548,20 +548,45 @@ statuspanel[inactive][previoustype=overL
 /* tabview menus */
 .tabview-menuitem {
   max-width: 32em;
 }
 
 /* highlighter */
 %include highlighter.css
 
+/* gcli */
+
 html|*#gcli-tooltip-frame,
 html|*#gcli-output-frame,
 #gcli-output,
 #gcli-tooltip {
   overflow-x: hidden;
 }
 
 .gclitoolbar-input-node,
 .gclitoolbar-complete-node,
 .gclitoolbar-prompt {
   direction: ltr;
 }
+
+/* Responsive Mode */
+
+vbox[anonid=browserContainer][responsivemode] {
+  overflow: auto;
+}
+
+.devtools-responsiveui-toolbar:-moz-locale-dir(rtl) {
+  -moz-box-pack: end;
+}
+
+stack[anonid=browserStack][responsivemode] {
+  -moz-transition-duration: 200ms;
+  -moz-transition-timing-function: linear;
+}
+
+stack[anonid=browserStack][responsivemode] {
+  -moz-transition-properties: min-width, max-width, min-height, max-height;
+}
+
+stack[anonid=browserStack][responsivemode][notransition] {
+  -moz-transition: none;
+}
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1719,16 +1719,27 @@ function delayedStartup(isLoadingBlank, 
   // If the user (or the locale) hasn't enabled the top-level "Character
   // Encoding" menu via the "browser.menu.showCharacterEncoding" preference,
   // hide it.
   if ("true" != gPrefService.getComplexValue("browser.menu.showCharacterEncoding",
                                              Ci.nsIPrefLocalizedString).data)
     document.getElementById("appmenu_charsetMenu").hidden = true;
 #endif
 
+  // Enable Responsive UI?
+  let responsiveUIEnabled = gPrefService.getBoolPref("devtools.responsiveUI.enabled");
+  if (responsiveUIEnabled) {
+    document.getElementById("menu_responsiveUI").hidden = false;
+    document.getElementById("Tools:ResponsiveUI").removeAttribute("disabled");
+#ifdef MENUBAR_CAN_AUTOHIDE
+    document.getElementById("appmenu_responsiveUI").hidden = false;
+#endif
+    document.getElementById("developer-toolbar-responsiveui").hidden = false;
+  }
+
   let appMenuButton = document.getElementById("appmenu-button");
   let appMenuPopup = document.getElementById("appmenu-popup");
   if (appMenuButton && appMenuPopup) {
     let appMenuOpening = null;
     appMenuButton.addEventListener("mousedown", function(event) {
       if (event.button == 0)
         appMenuOpening = new Date();
     }, false);
@@ -9297,16 +9308,28 @@ var Scratchpad = {
 };
 
 XPCOMUtils.defineLazyGetter(Scratchpad, "ScratchpadManager", function() {
   let tmp = {};
   Cu.import("resource:///modules/devtools/scratchpad-manager.jsm", tmp);
   return tmp.ScratchpadManager;
 });
 
+var ResponsiveUI = {
+  toggle: function RUI_toggle() {
+    this.ResponsiveUIManager.toggle(window, gBrowser.selectedTab);
+  }
+};
+
+XPCOMUtils.defineLazyGetter(ResponsiveUI, "ResponsiveUIManager", function() {
+  let tmp = {};
+  Cu.import("resource:///modules/devtools/responsivedesign.jsm", tmp);
+  return tmp.ResponsiveUIManager;
+});
+
 var StyleEditor = {
   prefEnabledName: "devtools.styleeditor.enabled",
   /**
    * Opens the style editor. If the UI is already open, it will be focused.
    *
    * @param {CSSStyleSheet} [aSelectedStyleSheet] default Stylesheet.
    * @param {Number} [aLine] Line to which the caret should be moved (one-indexed).
    * @param {Number} [aCol] Column to which the caret should be moved (one-indexed).
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -1044,16 +1044,21 @@
                          label="&webConsoleButton.label;"
                          class="devtools-toolbarbutton"
                          command="Tools:WebConsole"/>
           <toolbarbutton id="developer-toolbar-inspector"
                          label="&inspectorButton.label;"
                          class="devtools-toolbarbutton"
                          hidden="true"
                          command="Tools:Inspect"/>
+          <toolbarbutton id="developer-toolbar-responsiveui"
+                         label="&responsiveUI.label;"
+                         class="devtools-toolbarbutton"
+                         hidden="true"
+                         command="Tools:ResponsiveUI"/>
           <toolbarbutton id="developer-toolbar-debugger"
                          label="&scriptsButton.label;"
                          class="devtools-toolbarbutton"
                          hidden="true"
                          command="Tools:Debugger"/>
 #ifndef XP_MACOSX
           <toolbarbutton id="developer-toolbar-closebutton"
                          class="devtools-closebutton"
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -21,20 +21,22 @@
 
     <content>
       <xul:stringbundle anonid="tbstringbundle" src="chrome://browser/locale/tabbrowser.properties"/>
       <xul:tabbox anonid="tabbox" class="tabbrowser-tabbox"
                   flex="1" eventnode="document" xbl:inherits="handleCtrlPageUpDown"
                   onselect="if (event.target.localName == 'tabpanels') this.parentNode.updateCurrentBrowser();">
         <xul:tabpanels flex="1" class="plain" selectedIndex="0" anonid="panelcontainer">
           <xul:notificationbox flex="1">
-            <xul:stack flex="1" anonid="browserStack">
-              <xul:browser type="content-primary" message="true" disablehistory="true"
-                           xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup"/>
-            </xul:stack>
+            <xul:vbox flex="1" anonid="browserContainer">
+              <xul:stack flex="1" anonid="browserStack">
+                <xul:browser type="content-primary" message="true" disablehistory="true"
+                             xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup"/>
+              </xul:stack>
+            </xul:vbox>
           </xul:notificationbox>
         </xul:tabpanels>
       </xul:tabbox>
       <children/>
     </content>
     <implementation implements="nsIDOMEventListener">
 
       <property name="tabContextMenu" readonly="true"
@@ -288,16 +290,25 @@
         ]]>
         </body>
       </method>
 
       <method name="getNotificationBox">
         <parameter name="aBrowser"/>
         <body>
           <![CDATA[
+            return this.getBrowserContainer(aBrowser).parentNode;
+          ]]>
+        </body>
+      </method>
+
+      <method name="getBrowserContainer">
+        <parameter name="aBrowser"/>
+        <body>
+          <![CDATA[
             return (aBrowser || this.mCurrentBrowser).parentNode.parentNode;
           ]]>
         </body>
       </method>
 
       <method name="getTabModalPromptBox">
         <parameter name="aBrowser"/>
         <body>
@@ -1279,22 +1290,30 @@
             // Create the browserStack container
             var stack = document.createElementNS(
                                     "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
                                     "stack");
             stack.setAttribute("anonid", "browserStack");
             stack.appendChild(b);
             stack.setAttribute("flex", "1");
 
+            // Create the browserContainer
+            var box = document.createElementNS(
+                                    "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
+                                    "vbox");
+            box.setAttribute("anonid", "browserContainer");
+            box.appendChild(stack);
+            box.setAttribute("flex", "1");
+
             // Add the Message and the Browser to the box
             var notificationbox = document.createElementNS(
                                     "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
                                     "notificationbox");
             notificationbox.setAttribute("flex", "1");
-            notificationbox.appendChild(stack);
+            notificationbox.appendChild(box);
 
             var position = this.tabs.length - 1;
             var uniqueId = "panel" + Date.now() + position;
             notificationbox.id = uniqueId;
             t.linkedPanel = uniqueId;
             t.linkedBrowser = b;
             t._tPos = position;
             if (t.previousSibling.selected)
@@ -1709,22 +1728,23 @@
 
             // update first-tab/last-tab/beforeselected/afterselected attributes
             this.selectedTab._selected = true;
 
             // Removing the panel requires fixing up selectedPanel immediately
             // (see below), which would be hindered by the potentially expensive
             // browser removal. So we remove the browser and the panel in two
             // steps.
-            var panel = browser.parentNode.parentNode;
+
+            var panel = this.getNotificationBox(browser);
 
             // This will unload the document. An unload handler could remove
             // dependant tabs, so it's important that the tabbrowser is now in
             // a consistent state (tab removed, tab positions updated, etc.).
-            panel.removeChild(browser.parentNode);
+            panel.removeChild(this.getBrowserContainer(browser));
 
             // Release the browser in case something is erroneously holding a
             // reference to the tab after its removal.
             aTab.linkedBrowser = null;
 
             // As the browser is removed, the removal of a dependent document can
             // cause the whole window to close. So at this point, it's possible
             // that the binding is destructed.
@@ -2485,17 +2505,17 @@
               }
               break;
           }
         ]]></body>
       </method>
 
       <constructor>
         <![CDATA[
-          this.mCurrentBrowser = this.mPanelContainer.childNodes[0].firstChild.firstChild;
+          this.mCurrentBrowser = this.mPanelContainer.firstChild.firstChild.firstChild.firstChild;
           this.mCurrentTab = this.tabContainer.firstChild;
           document.addEventListener("keypress", this, false);
           window.addEventListener("sizemodechange", this, false);
 
           var uniqueId = "panel" + Date.now();
           this.mPanelContainer.childNodes[0].id = uniqueId;
           this.mCurrentTab.linkedPanel = uniqueId;
           this.mCurrentTab._tPos = 0;
--- a/browser/base/content/test/browser_bug462673.js
+++ b/browser/base/content/test/browser_bug462673.js
@@ -10,17 +10,17 @@ var runs = [
     var newBrowser = newTab.linkedBrowser;
     tabbrowser.removeTab(tab);
     ok(!win.closed, "Window stays open");
     if (!win.closed) {
       is(tabbrowser.tabContainer.childElementCount, 1, "Window has one tab");
       is(tabbrowser.browsers.length, 1, "Window has one browser");
       is(tabbrowser.selectedTab, newTab, "Remaining tab is selected");
       is(tabbrowser.selectedBrowser, newBrowser, "Browser for remaining tab is selected");
-      is(tabbrowser.mTabBox.selectedPanel, newBrowser.parentNode.parentNode, "Panel for remaining tab is selected");
+      is(tabbrowser.mTabBox.selectedPanel, newBrowser.parentNode.parentNode.parentNode, "Panel for remaining tab is selected");
     }
   }
 ];
 
 function test() {
   waitForExplicitFinish();
   runOneTest();
 }
--- a/browser/devtools/Makefile.in
+++ b/browser/devtools/Makefile.in
@@ -19,11 +19,12 @@ DIRS = \
   sourceeditor \
   styleeditor \
   styleinspector \
   tilt \
   scratchpad \
   debugger \
   layoutview \
   shared \
+  responsivedesign \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
--- a/browser/devtools/debugger/DebuggerUI.jsm
+++ b/browser/devtools/debugger/DebuggerUI.jsm
@@ -196,17 +196,18 @@ function DebuggerPane(aDebuggerUI, aTab)
 
 DebuggerPane.prototype = {
 
   /**
    * Initializes the debugger server.
    */
   _initServer: function DP__initServer() {
     if (!DebuggerServer.initialized) {
-      DebuggerServer.init();
+      // Always allow connections from nsIPipe transports.
+      DebuggerServer.init(function () { return true; });
       DebuggerServer.addBrowserActors();
     }
   },
 
   /**
    * Creates and initializes the widgets containing the debugger UI.
    */
   _create: function DP__create() {
@@ -400,24 +401,50 @@ function ChromeDebuggerProcess(aWindow, 
 
 ChromeDebuggerProcess.prototype = {
 
   /**
    * Initializes the debugger server.
    */
   _initServer: function RDP__initServer() {
     if (!DebuggerServer.initialized) {
-      DebuggerServer.init();
+      DebuggerServer.init(this._allowConnection);
       DebuggerServer.addBrowserActors();
     }
     DebuggerServer.closeListener();
     DebuggerServer.openListener(DebuggerPreferences.remotePort, false);
   },
 
   /**
+   * 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/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -100,17 +100,19 @@ let DebuggerController = {
       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) {
+      // If the connection was not established before the user canceled the
+      // prompt, close the remote debugger.
+      if (!result && !DebuggerController.activeThread) {
         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
@@ -577,17 +579,17 @@ StackFrames.prototype = {
             }.bind(this));
             break;
           case "block":
           case "function":
             // Add nodes for every argument.
             let variables = env.bindings.arguments;
             for each (let variable in variables) {
               let name = Object.getOwnPropertyNames(variable)[0];
-              let paramVar = scope.addVar(name);
+              let paramVar = scope.addVar(name, variable[name]);
               let paramVal = variable[name].value;
               paramVar.setGrip(paramVal);
               this._addExpander(paramVar, paramVal);
             }
             // Add nodes for every other variable in scope.
             this._addScopeVariables(env.bindings.variables, scope);
             break;
           default:
@@ -621,17 +623,17 @@ StackFrames.prototype = {
     // Sort all of the variables before adding them, for better UX.
     let variables = {};
     for each (let prop in Object.keys(aVariables).sort()) {
       variables[prop] = aVariables[prop];
     }
 
     // Add the sorted variables to the specified scope.
     for (let variable in variables) {
-      let paramVar = aScope.addVar(variable);
+      let paramVar = aScope.addVar(variable, variables[variable]);
       let paramVal = variables[variable].value;
       paramVar.setGrip(paramVal);
       this._addExpander(paramVar, paramVal);
     }
   },
 
   /**
    * Adds an 'onexpand' callback for a variable, lazily handling the addition of
--- a/browser/devtools/debugger/debugger-view.js
+++ b/browser/devtools/debugger/debugger-view.js
@@ -875,22 +875,24 @@ PropertiesView.prototype = {
    * Adds a variable to a specified scope.
    * If the optional id is not specified, the variable html node will have a
    * default id set as aScope.id->aName-variable.
    *
    * @param object aScope
    *        The parent scope element.
    * @param string aName
    *        The variable name.
+   * @param object aFlags
+   *        Optional, contains configurable, enumerable or writable flags.
    * @param string aId
    *        Optional, an id for the variable html node.
    * @return object
    *         The newly created html node representing the added var.
    */
-  _addVar: function DVP__addVar(aScope, aName, aId) {
+  _addVar: function DVP__addVar(aScope, aName, aFlags, aId) {
     // Make sure the scope container exists.
     if (!aScope) {
       return null;
     }
 
     // Compute the id of the element if not specified.
     aId = aId || (aScope.id + "->" + aName + "-variable");
 
@@ -922,16 +924,31 @@ PropertiesView.prototype = {
 
       // Separator between the variable name and its value.
       separatorLabel.className = "plain";
       separatorLabel.setAttribute("value", ":");
 
       // The variable information (type, class and/or value).
       valueLabel.className = "value plain";
 
+      if (aFlags) {
+        // Use attribute flags to specify the element type and tooltip text.
+        let tooltip = [];
+
+        !aFlags.configurable ? element.setAttribute("non-configurable", "")
+                             : tooltip.push("configurable");
+        !aFlags.enumerable   ? element.setAttribute("non-enumerable", "")
+                             : tooltip.push("enumerable");
+        !aFlags.writable     ? element.setAttribute("non-writable", "")
+                             : tooltip.push("writable");
+
+        element.setAttribute("tooltiptext", tooltip.join(", "));
+      }
+      if (aName === "this") { element.setAttribute("self", ""); }
+
       // Handle the click event when pressing the element value label.
       valueLabel.addEventListener("click", this._activateElementInputMode.bind({
         scope: this,
         element: element,
         valueLabel: valueLabel
       }));
 
       // Maintain the symbolic name of the variable.
@@ -1088,17 +1105,17 @@ PropertiesView.prototype = {
    *        is not specified, it will be inferred from the value).
    *        e.g. ["someProp0", 42]
    *             ["someProp1", true]
    *             ["someProp2", "nasu"]
    *             ["someProp3", { type: "undefined" }]
    *             ["someProp4", { type: "null" }]
    *             ["someProp5", { type: "object", class: "Object" }]
    * @param object aFlags
-   *        Contans configurable, enumberable or writable flags.
+   *        Contains configurable, enumerable or writable flags.
    * @param string aName
    *        Optional, the property name.
    * @paarm string aId
    *        Optional, an id for the property html node.
    * @return object
    *         The newly created html node representing the added prop.
    */
   _addProperty: function DVP__addProperty(aVar, aProperty, aFlags, aName, aId) {
@@ -1134,41 +1151,48 @@ PropertiesView.prototype = {
     element.refresh(function(pKey, pGrip) {
       let title = element.getElementsByClassName("title")[0];
       let nameLabel = title.getElementsByClassName("name")[0];
       let separatorLabel = document.createElement("label");
       let valueLabel = document.createElement("label");
 
       if ("undefined" !== typeof pKey) {
         // Use a key element to specify the property name.
-        let className = "";
-        if (aFlags) {
-          if (aFlags.configurable === false) { className += "non-configurable "; }
-          if (aFlags.enumerable === false) { className += "non-enumerable "; }
-          if (aFlags.writable === false) { className += "non-writable "; }
-        }
-        if (pKey === "__proto__ ") { className += "proto "; }
-
-        nameLabel.className = className + "key plain";
+        nameLabel.className = "key plain";
         nameLabel.setAttribute("value", pKey.trim());
         title.appendChild(nameLabel);
       }
       if ("undefined" !== typeof pGrip) {
         // Separator between the variable name and its value.
         separatorLabel.className = "plain";
         separatorLabel.setAttribute("value", ":");
 
         // Use a value element to specify the property value.
         valueLabel.className = "value plain";
         this._applyGrip(valueLabel, pGrip);
 
         title.appendChild(separatorLabel);
         title.appendChild(valueLabel);
       }
 
+      if (aFlags) {
+        // Use attribute flags to specify the element type and tooltip text.
+        let tooltip = [];
+
+        !aFlags.configurable ? element.setAttribute("non-configurable", "")
+                             : tooltip.push("configurable");
+        !aFlags.enumerable   ? element.setAttribute("non-enumerable", "")
+                             : tooltip.push("enumerable");
+        !aFlags.writable     ? element.setAttribute("non-writable", "")
+                             : tooltip.push("writable");
+
+        element.setAttribute("tooltiptext", tooltip.join(", "));
+      }
+      if (pKey === "__proto__ ") { element.setAttribute("proto", ""); }
+
       // Handle the click event when pressing the element value label.
       valueLabel.addEventListener("click", this._activateElementInputMode.bind({
         scope: this,
         element: element,
         valueLabel: valueLabel
       }));
 
       // Maintain the symbolic name of the property.
--- a/browser/devtools/debugger/test/browser_dbg_createRemote.js
+++ b/browser/devtools/debugger/test/browser_dbg_createRemote.js
@@ -50,17 +50,17 @@ function test() {
     let iframe = gTab.linkedBrowser.contentWindow.wrappedJSObject.frames[0];
 
     is(iframe.document.title, "Browser Debugger Test Tab", "Found the iframe");
 
     iframe.runDebuggerStatement();
   },
   function beforeTabAdded() {
     if (!DebuggerServer.initialized) {
-      DebuggerServer.init();
+      DebuggerServer.init(function() { return true; });
       DebuggerServer.addBrowserActors();
     }
     DebuggerServer.closeListener();
 
     gAutoConnect = Services.prefs.getBoolPref("devtools.debugger.remote-autoconnect");
     Services.prefs.setBoolPref("devtools.debugger.remote-autoconnect", true);
 
     // Open the listener at some point in the future to test automatic reconnect.
--- a/browser/devtools/debugger/test/head.js
+++ b/browser/devtools/debugger/test/head.js
@@ -14,19 +14,25 @@ let DebuggerTransport = tempScope.Debugg
 let DebuggerClient = tempScope.DebuggerClient;
 let Services = tempScope.Services;
 
 const EXAMPLE_URL = "http://example.com/browser/browser/devtools/debugger/test/";
 
 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);
+});
 
 if (!DebuggerServer.initialized) {
-  DebuggerServer.init();
+  DebuggerServer.init(function () { return true; });
   DebuggerServer.addBrowserActors();
 }
 
 waitForExplicitFinish();
 
 function addTab(aURL, aOnload)
 {
   gBrowser.selectedTab = gBrowser.addTab(aURL);
new file mode 100644
--- /dev/null
+++ b/browser/devtools/responsivedesign/Makefile.in
@@ -0,0 +1,15 @@
+DEPTH		= ../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+ifdef ENABLE_TESTS
+	DIRS += test
+endif
+
+include $(topsrcdir)/config/rules.mk
+
+libs::
+	$(NSINSTALL) $(srcdir)/*.jsm $(FINAL_TARGET)/modules/devtools
new file mode 100644
--- /dev/null
+++ b/browser/devtools/responsivedesign/responsivedesign.jsm
@@ -0,0 +1,441 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Mozilla Responsive UI Module.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2012
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Paul Rouget <paul@mozilla.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+var EXPORTED_SYMBOLS = ["ResponsiveUIManager"];
+
+const MIN_WIDTH = 50;
+const MIN_HEIGHT = 50;
+
+let ResponsiveUIManager = {
+  /**
+   * Check if the a tab is in a responsive mode.
+   * Leave the responsive mode if active,
+   * active the responsive mode if not active.
+   *
+   * @param aWindow the main window.
+   * @param aTab the tab targeted.
+   */
+  toggle: function(aWindow, aTab) {
+    if (aTab.responsiveUI) {
+      aTab.responsiveUI.close();
+    } else {
+      aTab.responsiveUI = new ResponsiveUI(aWindow, aTab);
+    }
+  },
+}
+
+let presets =  [
+  // Phones
+  {width: 320, height: 480},    // iPhone, B2G, with <meta viewport>
+  {width: 360, height: 640},    // Android 4, phones, with <meta viewport>
+
+  // Tablets
+  {width: 768, height: 1024},   // iPad, with <meta viewport>
+  {width: 800, height: 1280},   // Android 4, Tablet, with <meta viewport>
+
+  // Default width for mobile browsers, no <meta viewport>
+  {width: 980, height: 1280},
+
+  // Computer
+  {width: 1280, height: 600},
+  {width: 1920, height: 900},
+];
+
+function ResponsiveUI(aWindow, aTab)
+{
+  this.mainWindow = aWindow;
+  this.tab = aTab;
+  this.browser = aTab.linkedBrowser;
+  this.chromeDoc = aWindow.document;
+  this.container = aWindow.gBrowser.getBrowserContainer(this.browser);
+  this.stack = this.container.querySelector("[anonid=browserStack]");
+
+  // Try to load presets from prefs
+  if (Services.prefs.prefHasUserValue("devtools.responsiveUI.presets")) {
+    try {
+      presets = JSON.parse(Services.prefs.getCharPref("devtools.responsiveUI.presets"));
+    } catch(e) {
+      // User pref is malformated.
+      Cu.reportError("Could not parse pref `devtools.responsiveUI.presets`: " + e);
+    }
+  }
+
+  if (Array.isArray(presets)) {
+    this.presets = [{custom: true}].concat(presets)
+  } else {
+    Cu.reportError("Presets value (devtools.responsiveUI.presets) is malformated.");
+    this.presets = [{custom: true}];
+  }
+
+  // Default size. The first preset (custom) is the one that will be used.
+  let bbox = this.stack.getBoundingClientRect();
+  this.presets[0].width = bbox.width - 40; // horizontal padding of the container
+  this.presets[0].height = bbox.height - 80; // vertical padding + toolbar height
+  this.currentPreset = 0; // Custom
+
+  this.container.setAttribute("responsivemode", "true");
+  this.stack.setAttribute("responsivemode", "true");
+
+  // Let's bind some callbacks.
+  this.bound_presetSelected = this.presetSelected.bind(this);
+  this.bound_rotate = this.rotate.bind(this);
+  this.bound_startResizing = this.startResizing.bind(this);
+  this.bound_stopResizing = this.stopResizing.bind(this);
+  this.bound_onDrag = this.onDrag.bind(this);
+  this.bound_onKeypress = this.onKeypress.bind(this);
+
+  // Events
+  this.tab.addEventListener("TabClose", this);
+  this.tab.addEventListener("TabAttrModified", this);
+  this.mainWindow.addEventListener("keypress", this.bound_onKeypress, true);
+
+  this.buildUI();
+  this.checkMenus();
+}
+
+ResponsiveUI.prototype = {
+  /**
+   * Destroy the nodes. Remove listeners. Reset the style.
+   */
+  close: function RUI_unload() {
+    this.unCheckMenus();
+    // Reset style of the stack.
+    let style = "max-width: none;" +
+                "min-width: 0;" +
+                "max-height: none;" +
+                "min-height: 0;";
+    this.stack.setAttribute("style", style);
+
+    this.stopResizing();
+
+    // Remove listeners.
+    this.mainWindow.removeEventListener("keypress", this.bound_onKeypress, true);
+    this.menulist.removeEventListener("select", this.bound_presetSelected, true);
+    this.tab.removeEventListener("TabClose", this);
+    this.tab.removeEventListener("TabAttrModified", this);
+    this.rotatebutton.removeEventListener("command", this.bound_rotate, true);
+
+    // Removed elements.
+    this.container.removeChild(this.toolbar);
+    this.stack.removeChild(this.resizer);
+    this.stack.removeChild(this.resizeBar);
+
+    // Unset the responsive mode.
+    this.container.removeAttribute("responsivemode");
+    this.stack.removeAttribute("responsivemode");
+
+    delete this.tab.responsiveUI;
+  },
+
+  /**
+   * Handle keypressed.
+   *
+   * @param aEvent
+   */
+  onKeypress: function RUI_onKeypress(aEvent) {
+    if (aEvent.keyCode == this.mainWindow.KeyEvent.DOM_VK_ESCAPE &&
+        this.mainWindow.gBrowser.selectedBrowser == this.browser) {
+      aEvent.preventDefault();
+      aEvent.stopPropagation();
+      this.close();
+    }
+  },
+
+  /**
+   * Handle events
+   */
+  handleEvent: function (aEvent) {
+    switch (aEvent.type) {
+      case "TabClose":
+        this.close();
+        break;
+      case "TabAttrModified":
+        if (this.mainWindow.gBrowser.selectedBrowser == this.browser) {
+          this.checkMenus();
+        } else {
+          this.unCheckMenus();
+        }
+        break;
+    }
+  },
+
+  /**
+   * Check the menu items.
+   */
+   checkMenus: function RUI_checkMenus() {
+     this.chromeDoc.getElementById("Tools:ResponsiveUI").setAttribute("checked", "true");
+   },
+
+  /**
+   * Uncheck the menu items.
+   */
+   unCheckMenus: function RUI_unCheckMenus() {
+     this.chromeDoc.getElementById("Tools:ResponsiveUI").setAttribute("checked", "false");
+   },
+
+  /**
+   * Build the toolbar and the resizers.
+   *
+   * <vbox anonid="browserContainer"> From tabbrowser.xml
+   *  <toolbar class="devtools-toolbar devtools-responsiveui-toolbar">
+   *    <menulist class="devtools-menulist"/> // presets
+   *    <toolbarbutton tabindex="0" class="devtools-toolbarbutton" label="rotate"/> // rotate
+   *  </toolbar>
+   *  <stack anonid="browserStack"> From tabbrowser.xml
+   *    <browser/>
+   *    <box class="devtools-responsiveui-resizehandle" bottom="0" right="0"/>
+   *    <box class="devtools-responsiveui-resizebar" top="0" right="0"/>
+   *  </stack>
+   * </vbox>
+   */
+  buildUI: function RUI_buildUI() {
+    // Toolbar
+    this.toolbar = this.chromeDoc.createElement("toolbar");
+    this.toolbar.className = "devtools-toolbar devtools-responsiveui-toolbar";
+
+    this.menulist = this.chromeDoc.createElement("menulist");
+    this.menulist.className = "devtools-menulist";
+
+    this.menulist.addEventListener("select", this.bound_presetSelected, true);
+
+    let menupopup = this.chromeDoc.createElement("menupopup");
+    this.registerPresets(menupopup);
+    this.menulist.appendChild(menupopup);
+
+    this.rotatebutton = this.chromeDoc.createElement("toolbarbutton");
+    this.rotatebutton.setAttribute("tabindex", "0");
+    this.rotatebutton.setAttribute("label", this.strings.GetStringFromName("responsiveUI.rotate"));
+    this.rotatebutton.className = "devtools-toolbarbutton";
+    this.rotatebutton.addEventListener("command", this.bound_rotate, true);
+
+    this.toolbar.appendChild(this.menulist);
+    this.toolbar.appendChild(this.rotatebutton);
+
+    // Resizers
+    this.resizer = this.chromeDoc.createElement("box");
+    this.resizer.className = "devtools-responsiveui-resizehandle";
+    this.resizer.setAttribute("right", "0");
+    this.resizer.setAttribute("bottom", "0");
+    this.resizer.onmousedown = this.bound_startResizing;
+
+    this.resizeBar =  this.chromeDoc.createElement("box");
+    this.resizeBar.className = "devtools-responsiveui-resizebar";
+    this.resizeBar.setAttribute("top", "0");
+    this.resizeBar.setAttribute("right", "0");
+    this.resizeBar.onmousedown = this.bound_startResizing;
+
+    this.container.insertBefore(this.toolbar, this.stack);
+    this.stack.appendChild(this.resizer);
+    this.stack.appendChild(this.resizeBar);
+  },
+
+  /**
+   * Build the presets list and append it to the menupopup.
+   *
+   * @param aParent menupopup.
+   */
+  registerPresets: function RUI_registerPresets(aParent) {
+    let fragment = this.chromeDoc.createDocumentFragment();
+    let doc = this.chromeDoc;
+    let self = this;
+    this.presets.forEach(function(preset) {
+        let menuitem = doc.createElement("menuitem");
+        self.setMenuLabel(menuitem, preset);
+        fragment.appendChild(menuitem);
+    });
+    aParent.appendChild(fragment);
+  },
+
+  /**
+   * Set the menuitem label of a preset.
+   *
+   * @param aMenuitem menuitem to edit.
+   * @param aPreset associated preset.
+   */
+  setMenuLabel: function RUI_setMenuLabel(aMenuitem, aPreset) {
+    let size = Math.round(aPreset.width) + "x" + Math.round(aPreset.height);
+    if (aPreset.custom) {
+      let str = this.strings.formatStringFromName("responsiveUI.customResolution", [size], 1);
+      aMenuitem.setAttribute("label", str);
+    } else {
+      aMenuitem.setAttribute("label", size);
+    }
+  },
+
+  /**
+   * When a preset is selected, apply it.
+   */
+  presetSelected: function RUI_presetSelected() {
+    this.currentPreset = this.menulist.selectedIndex;
+    let preset = this.presets[this.currentPreset];
+    this.loadPreset(preset);
+  },
+
+  /**
+   * Apply a preset.
+   *
+   * @param aPreset preset to apply.
+   */
+  loadPreset: function RUI_loadPreset(aPreset) {
+    this.setSize(aPreset.width, aPreset.height);
+  },
+
+  /**
+   * Swap width and height.
+   */
+  rotate: function RUI_rotate() {
+    this.setSize(this.currentHeight, this.currentWidth);
+  },
+
+  /**
+   * Change the size of the browser.
+   *
+   * @param aWidth width of the browser.
+   * @param aHeight height of the browser.
+   */
+  setSize: function RUI_setSize(aWidth, aHeight) {
+    this.currentWidth = aWidth;
+    this.currentHeight = aHeight;
+
+    // We resize the containing stack.
+    let style = "max-width: %width;" +
+                "min-width: %width;" +
+                "max-height: %height;" +
+                "min-height: %height;";
+
+    style = style.replace(/%width/g, this.currentWidth + "px");
+    style = style.replace(/%height/g, this.currentHeight + "px");
+
+    this.stack.setAttribute("style", style);
+
+    if (!this.ignoreY)
+      this.resizeBar.setAttribute("top", Math.round(this.currentHeight / 2));
+
+    // We uptate the Custom menuitem if we are not using a preset.
+    if (this.presets[this.currentPreset].custom) {
+      let preset = this.presets[this.currentPreset];
+      preset.width = this.currentWidth;
+      preset.height = this.currentHeight;
+
+      let menuitem = this.menulist.firstChild.childNodes[this.currentPreset];
+      this.setMenuLabel(menuitem, preset);
+    }
+  },
+
+  /**
+   * Start the process of resizing the browser.
+   *
+   * @param aEvent
+   */
+  startResizing: function RUI_startResizing(aEvent) {
+    let preset = this.presets[this.currentPreset];
+    if (!preset.custom) {
+      this.currentPreset = 0;
+      preset = this.presets[0];
+      preset.width = this.currentWidth;
+      preset.height = this.currentHeight;
+      let menuitem = this.menulist.firstChild.childNodes[0];
+      this.setMenuLabel(menuitem, preset);
+      this.menulist.selectedIndex = 0;
+    }
+    this.mainWindow.addEventListener("mouseup", this.bound_stopResizing, true);
+    this.mainWindow.addEventListener("mousemove", this.bound_onDrag, true);
+    this.container.style.pointerEvents = "none";
+
+    this.stack.setAttribute("notransition", "true");
+
+    this.lastClientX = aEvent.clientX;
+    this.lastClientY = aEvent.clientY;
+
+    this.ignoreY = (aEvent.target === this.resizeBar);
+  },
+
+  /**
+   * Resizing on mouse move.
+   *
+   * @param aEvent
+   */
+  onDrag: function RUI_onDrag(aEvent) {
+    let deltaX = aEvent.clientX - this.lastClientX;
+    let deltaY = aEvent.clientY - this.lastClientY;
+
+    if (this.ignoreY)
+      deltaY = 0;
+
+    let width = this.currentWidth + deltaX;
+    let height = this.currentHeight + deltaY;
+
+    if (width < MIN_WIDTH) {
+        width = MIN_WIDTH;
+    } else {
+        this.lastClientX = aEvent.clientX;
+    }
+
+    if (height < MIN_HEIGHT) {
+        height = MIN_HEIGHT;
+    } else {
+        this.lastClientY = aEvent.clientY;
+    }
+
+    this.setSize(width, height);
+  },
+
+  /**
+   * Stop End resizing
+   */
+  stopResizing: function RUI_stopResizing() {
+    this.container.style.pointerEvents = "auto";
+
+    this.mainWindow.removeEventListener("mouseup", this.bound_stopResizing, true);
+    this.mainWindow.removeEventListener("mousemove", this.bound_onDrag, true);
+
+    this.stack.removeAttribute("notransition");
+    this.ignoreY = false;
+  },
+}
+
+XPCOMUtils.defineLazyGetter(ResponsiveUI.prototype, "strings", function () {
+  return Services.strings.createBundle("chrome://browser/locale/devtools/responsiveUI.properties");
+});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/responsivedesign/test/Makefile.in
@@ -0,0 +1,53 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+# Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2012
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Paul Rouget <paul@mozilla.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH     = ../../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+relativesrcdir  = browser/devtools/responsivedesign/test
+
+include $(DEPTH)/config/autoconf.mk
+include $(topsrcdir)/config/rules.mk
+
+_BROWSER_FILES = \
+		browser_responsiveui.js \
+		$(NULL)
+
+
+libs::	$(_BROWSER_FILES)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/browser/devtools/responsivedesign/test/browser_responsiveui.js
@@ -0,0 +1,143 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function test() {
+  let instance;
+
+  waitForExplicitFinish();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function onload() {
+    gBrowser.selectedBrowser.removeEventListener("load", onload, true);
+    waitForFocus(startTest, content);
+  }, true);
+
+  content.location = "data:text/html,mop";
+
+  function startTest() {
+    document.getElementById("Tools:ResponsiveUI").removeAttribute("disabled");
+    synthesizeKeyFromKeyTag("key_responsiveUI");
+    executeSoon(onUIOpen);
+  }
+
+  function onUIOpen() {
+    // Is it open?
+    let container = gBrowser.getBrowserContainer();
+    is(container.getAttribute("responsivemode"), "true", "In responsive mode.");
+
+    // Menus are correctly updated?
+    is(document.getElementById("Tools:ResponsiveUI").getAttribute("checked"), "true", "menus checked");
+
+    instance = gBrowser.selectedTab.responsiveUI;
+    ok(instance, "instance of the module is attached to the tab.");
+
+    testPresets();
+  }
+
+  function testPresets() {
+    function testOnePreset(c) {
+      if (c == 0) {
+        executeSoon(testCustom);
+        return;
+      }
+      instance.menulist.selectedIndex = c;
+      window.setTimeout(function() {
+        let item = instance.menulist.firstChild.childNodes[c];
+        let [width, height] = extractSizeFromString(item.getAttribute("label"));
+        is(content.innerWidth, width, "preset " + c + ": dimension valid (width)");
+        is(content.innerHeight, height, "preset " + c + ": dimension valid (height)");
+        testOnePreset(c - 1);
+      }, 500);
+    }
+    testOnePreset(instance.menulist.firstChild.childNodes.length - 1);
+  }
+
+  function extractSizeFromString(str) {
+    let numbers = str.match(/(\d+)[^\d]*(\d+)/);
+    if (numbers) {
+      return [numbers[1], numbers[2]];
+    } else {
+      return [null, null];
+    }
+  }
+
+  function testCustom() {
+    let initialWidth = content.innerWidth;
+    let initialHeight = content.innerHeight;
+
+    let x = 2, y = 2;
+    EventUtils.synthesizeMouse(instance.resizer, x, y, {type: "mousedown"}, window);
+    x += 20; y += 10;
+    EventUtils.synthesizeMouse(instance.resizer, x, y, {type: "mousemove"}, window);
+    EventUtils.synthesizeMouse(instance.resizer, x, y, {type: "mouseup"}, window);
+
+    window.setTimeout(function() {
+      let expectedWidth = initialWidth + 20;
+      let expectedHeight = initialHeight + 10;
+      info("initial width: " + initialWidth);
+      info("initial height: " + initialHeight);
+      is(content.innerWidth, expectedWidth, "Size correcty updated (width).");
+      is(content.innerHeight, expectedHeight, "Size correcty updated (height).");
+      is(instance.menulist.selectedIndex, 0, "Custom menuitem selected");
+      let [width, height] = extractSizeFromString(instance.menulist.firstChild.firstChild.getAttribute("label"));
+      is(width, expectedWidth, "Label updated (width).");
+      is(height, expectedHeight, "Label updated (height).");
+      rotate();
+    }, 500);
+  }
+
+  function rotate() {
+    let initialWidth = content.innerWidth;
+    let initialHeight = content.innerHeight;
+
+    info("rotate");
+    instance.rotate();
+
+    window.setTimeout(function() {
+      is(content.innerWidth, initialHeight, "The width is now the height.");
+      is(content.innerHeight, initialWidth, "The height is now the width.");
+      let [width, height] = extractSizeFromString(instance.menulist.firstChild.firstChild.getAttribute("label"));
+      is(width, initialHeight, "Label updated (width).");
+      is(height, initialWidth, "Label updated (height).");
+
+       EventUtils.synthesizeKey("VK_ESCAPE", {});
+      executeSoon(finishUp);
+    }, 500);
+  }
+
+  function finishUp() {
+
+    // Menus are correctly updated?
+    is(document.getElementById("Tools:ResponsiveUI").getAttribute("checked"), "false", "menu unchecked");
+
+    delete instance;
+    gBrowser.removeCurrentTab();
+    finish();
+  }
+
+  function synthesizeKeyFromKeyTag(aKeyId) {
+    let key = document.getElementById(aKeyId);
+    isnot(key, null, "Successfully retrieved the <key> node");
+
+    let modifiersAttr = key.getAttribute("modifiers");
+
+    let name = null;
+
+    if (key.getAttribute("keycode"))
+      name = key.getAttribute("keycode");
+    else if (key.getAttribute("key"))
+      name = key.getAttribute("key");
+
+    isnot(name, null, "Successfully retrieved keycode/key");
+
+    let modifiers = {
+      shiftKey: modifiersAttr.match("shift"),
+      ctrlKey: modifiersAttr.match("ctrl"),
+      altKey: modifiersAttr.match("alt"),
+      metaKey: modifiersAttr.match("meta"),
+      accelKey: modifiersAttr.match("accel")
+    }
+
+    EventUtils.synthesizeKey(name, modifiers);
+  }
+}
--- a/browser/devtools/styleinspector/CssRuleView.jsm
+++ b/browser/devtools/styleinspector/CssRuleView.jsm
@@ -863,25 +863,16 @@ TextProperty.prototype = {
 function CssRuleView(aDoc, aStore)
 {
   this.doc = aDoc;
   this.store = aStore;
   this.element = this.doc.createElementNS(XUL_NS, "vbox");
   this.element.setAttribute("tabindex", "0");
   this.element.classList.add("ruleview");
   this.element.flex = 1;
-  this._selectionMode = false;
-
-  this._boundMouseDown = this._onMouseDown.bind(this);
-  this.element.addEventListener("mousedown",
-                                this._boundMouseDown);
-  this._boundMouseUp = this._onMouseUp.bind(this);
-  this.element.addEventListener("mouseup",
-                                this._boundMouseUp);
-  this._boundMouseMove = this._onMouseMove.bind(this);
 
   this._boundCopy = this._onCopy.bind(this);
   this.element.addEventListener("copy", this._boundCopy);
 
   this._createContextMenu();
   this._showEmpty();
 }
 
@@ -943,34 +934,16 @@ CssRuleView.prototype = {
     }
 
     this._elementStyle = new ElementStyle(aElement, this.store);
     this._elementStyle.onChanged = function() {
       this._changed();
     }.bind(this);
 
     this._createEditors();
-
-    // When creating a new property, we fake the normal property
-    // editor behavior (focusing a property's value after entering its
-    // name) by responding to the name's blur event, creating the
-    // value editor, and grabbing focus to the value editor.  But if
-    // focus has already moved to another document, we won't be able
-    // to move focus to the new editor.
-    // Create a focusable item at the end of the editors to catch these
-    // cases.
-    this._focusBackstop = createChild(this.element, "div", {
-      tabindex: 0,
-    });
-    this._backstopHandler = function() {
-      // If this item is actually focused long enough to get the focus
-      // event, allow focus to move on out of this document.
-      moveFocus(this.doc.defaultView, FOCUS_FORWARD);
-    }.bind(this);
-    this._focusBackstop.addEventListener("focus", this._backstopHandler, false);
   },
 
   /**
    * Update the rules for the currently highlighted element.
    */
   nodeChanged: function CssRuleView_nodeChanged()
   {
     // Repopulate the element style.
@@ -1008,22 +981,16 @@ CssRuleView.prototype = {
   /**
    * Clear the rule view.
    */
   clear: function CssRuleView_clear()
   {
     this._clearRules();
     this._viewedElement = null;
     this._elementStyle = null;
-
-    if (this._focusBackstop) {
-      this._focusBackstop.removeEventListener("focus", this._backstopHandler, false);
-      this._backstopHandler = null;
-      this._focusBackstop = null;
-    }
   },
 
   /**
    * Called when the user has made changes to the ElementStyle.
    * Emits an event that clients can listen to.
    */
   _changed: function CssRuleView_changed()
   {
@@ -1147,32 +1114,16 @@ CssRuleView.prototype = {
       !node.classList.contains("ruleview-property") &&
       !node.classList.contains("ruleview-computed"));
 
     this._declarationItem.disabled = disablePropertyItems;
     this._propertyItem.disabled = disablePropertyItems;
     this._propertyValueItem.disabled = disablePropertyItems;
   },
 
-  _onMouseDown: function CssRuleView_onMouseDown()
-  {
-    this.element.addEventListener("mousemove", this._boundMouseMove);
-  },
-
-  _onMouseUp: function CssRuleView_onMouseUp()
-  {
-    this.element.removeEventListener("mousemove", this._boundMouseMove);
-    this._selectionMode = false;
-  },
-
-  _onMouseMove: function CssRuleView_onMouseMove()
-  {
-    this._selectionMode = true;
-  },
-
   /**
    * Copy selected text from the rule view.
    *
    * @param aEvent The event object
    */
   _onCopy: function CssRuleView_onCopy(aEvent)
   {
     let win = this.doc.defaultView;
@@ -1337,30 +1288,30 @@ CssRuleView.prototype = {
     }
   }
 };
 
 /**
  * Create a RuleEditor.
  *
  * @param CssRuleView aRuleView
- *        The CssRuleView containg the document holding this rule editor and the
- *        _selectionMode flag.
+ *        The CssRuleView containg the document holding this rule editor.
  * @param Rule aRule
  *        The Rule object we're editing.
  * @constructor
  */
 function RuleEditor(aRuleView, aRule)
 {
   this.ruleView = aRuleView;
   this.doc = this.ruleView.doc;
   this.rule = aRule;
   this.rule.editor = this;
 
   this._onNewProperty = this._onNewProperty.bind(this);
+  this._newPropertyDestroy = this._newPropertyDestroy.bind(this);
 
   this._create();
 }
 
 RuleEditor.prototype = {
   _create: function RuleEditor_create()
   {
     this.element = this.doc.createElementNS(HTML_NS, "div");
@@ -1395,45 +1346,39 @@ RuleEditor.prototype = {
       class: "ruleview-selector"
     });
 
     this.openBrace = createChild(header, "span", {
       class: "ruleview-ruleopen",
       textContent: " {"
     });
 
-    this.openBrace.addEventListener("click", function() {
-      this.newProperty();
-    }.bind(this), true);
+    code.addEventListener("click", function() {
+      let selection = this.doc.defaultView.getSelection();
+      if (selection.isCollapsed) {
+        this.newProperty();
+      }
+    }.bind(this), false);
 
     this.propertyList = createChild(code, "ul", {
       class: "ruleview-propertylist"
     });
 
     this.populate();
 
     this.closeBrace = createChild(code, "div", {
       class: "ruleview-ruleclose",
       tabindex: "0",
       textContent: "}"
     });
 
-    // We made the close brace focusable, tabbing to it
-    // or clicking on it should start the new property editor.
-    this.closeBrace.addEventListener("focus", function(aEvent) {
-      if (!this.ruleView._selectionMode) {
-        this.newProperty();
-      }
-    }.bind(this), true);
-    this.closeBrace.addEventListener("mousedown", function(aEvent) {
-      aEvent.preventDefault();
-    }.bind(this), true);
-    this.closeBrace.addEventListener("click", function(aEvent) {
-      this.closeBrace.focus();
-    }.bind(this), true);
+    // Create a property editor when the close brace is clicked.
+    editableItem(this.closeBrace, function(aElement) {
+      this.newProperty();
+    }.bind(this));
   },
 
   /**
    * Update the rule editor with the contents of the rule.
    */
   populate: function RuleEditor_populate()
   {
     this.selectorText.textContent = this.rule.selectorText;
@@ -1465,55 +1410,77 @@ RuleEditor.prototype = {
 
   /**
    * Create a text input for a property name.  If a non-empty property
    * name is given, we'll create a real TextProperty and add it to the
    * rule.
    */
   newProperty: function RuleEditor_newProperty()
   {
+    // If we're already creating a new property, ignore this.
+    if (!this.closeBrace.hasAttribute("tabindex")) {
+      return;
+    }
+
     // While we're editing a new property, it doesn't make sense to
     // start a second new property editor, so disable focusing the
     // close brace for now.
     this.closeBrace.removeAttribute("tabindex");
 
     this.newPropItem = createChild(this.propertyList, "li", {
       class: "ruleview-property ruleview-newproperty",
     });
 
     this.newPropSpan = createChild(this.newPropItem, "span", {
-      class: "ruleview-propertyname"
+      class: "ruleview-propertyname",
+      tabindex: "0"
     });
 
     new InplaceEditor({
       element: this.newPropSpan,
       done: this._onNewProperty,
+      destroy: this._newPropertyDestroy,
       advanceChars: ":"
     });
   },
 
-  _onNewProperty: function RuleEditor_onNewProperty(aValue, aCommit)
+  /**
+   * Called when the new property input has been dismissed.
+   * Will create a new TextProperty if necessary.
+   *
+   * @param string aValue
+   *        The value in the editor.
+   * @param bool aCommit
+   *        True if the value should be committed.
+   */
+  _onNewProperty: function RuleEditor__onNewProperty(aValue, aCommit)
   {
-    // We're done, make the close brace focusable again.
-    this.closeBrace.setAttribute("tabindex", "0");
-
-    this.propertyList.removeChild(this.newPropItem);
-    delete this.newPropItem;
-    delete this.newPropSpan;
-
     if (!aValue || !aCommit) {
       return;
     }
 
     // Create an empty-valued property and start editing it.
     let prop = this.rule.createProperty(aValue, "", "");
     let editor = new TextPropertyEditor(this, prop);
     this.propertyList.appendChild(editor.element);
-    editor.valueSpan.focus();
+    editor.valueSpan.click();
   },
+
+  /**
+   * Called when the new property editor is destroyed.
+   */
+  _newPropertyDestroy: function RuleEditor__newPropertyDestroy()
+  {
+    // We're done, make the close brace focusable again.
+    this.closeBrace.setAttribute("tabindex", "0");
+
+    this.propertyList.removeChild(this.newPropItem);
+    delete this.newPropItem;
+    delete this.newPropSpan;
+  }
 };
 
 /**
  * Create a TextPropertyEditor.
  *
  * @param {RuleEditor} aRuleEditor
  *        The rule editor that owns this TextPropertyEditor.
  * @param {TextProperty} aProperty
@@ -1558,35 +1525,55 @@ TextPropertyEditor.prototype = {
     this.enable.addEventListener("click", this._onEnableClicked, true);
 
     // Click to expand the computed properties of the text property.
     this.expander = createChild(this.element, "span", {
       class: "ruleview-expander"
     });
     this.expander.addEventListener("click", this._onExpandClicked, true);
 
+    this.nameContainer = createChild(this.element, "span", {
+      class: "ruleview-namecontainer"
+    });
+    this.nameContainer.addEventListener("click", function(aEvent) {
+      this.nameSpan.click();
+      aEvent.stopPropagation();
+    }.bind(this), false);
+
     // Property name, editable when focused.  Property name
     // is committed when the editor is unfocused.
-    this.nameSpan = createChild(this.element, "span", {
+    this.nameSpan = createChild(this.nameContainer, "span", {
       class: "ruleview-propertyname",
       tabindex: "0",
     });
+
     editableField({
       start: this._onStartEditing,
       element: this.nameSpan,
       done: this._onNameDone,
       advanceChars: ':'
     });
 
-    appendText(this.element, ": ");
+    appendText(this.nameContainer, ": ");
+
+    // Create a span that will hold the property and semicolon.
+    // Use this span to create a slightly larger click target
+    // for the value.
+    let propertyContainer = createChild(this.element, "span", {
+      class: "ruleview-propertycontainer"
+    });
+    propertyContainer.addEventListener("click", function(aEvent) {
+      this.valueSpan.click();
+      aEvent.stopPropagation();
+    }.bind(this), false);
 
     // Property value, editable when focused.  Changes to the
     // property value are applied as they are typed, and reverted
     // if the user presses escape.
-    this.valueSpan = createChild(this.element, "span", {
+    this.valueSpan = createChild(propertyContainer, "span", {
       class: "ruleview-propertyvalue",
       tabindex: "0",
     });
 
     editableField({
       start: this._onStartEditing,
       element: this.valueSpan,
       done: this._onValueDone,
@@ -1594,17 +1581,17 @@ TextPropertyEditor.prototype = {
     });
 
     // Save the initial value as the last committed value,
     // for restoring after pressing escape.
     this.committed = { name: this.prop.name,
                        value: this.prop.value,
                        priority: this.prop.priority };
 
-    appendText(this.element, ";");
+    appendText(propertyContainer, ";");
 
     this.warning = createChild(this.element, "div", {
       hidden: "",
       class: "ruleview-warning",
       title: CssLogic.l10n("rule.warning.title"),
     });
 
     // Holds the viewers for the computed properties.
@@ -1708,28 +1695,30 @@ TextPropertyEditor.prototype = {
     } else {
       this.expander.style.visibility = "hidden";
     }
   },
 
   /**
    * Handles clicks on the disabled property.
    */
-  _onEnableClicked: function TextPropertyEditor_onEnableClicked()
+  _onEnableClicked: function TextPropertyEditor_onEnableClicked(aEvent)
   {
     this.prop.setEnabled(this.enable.checked);
+    aEvent.stopPropagation();
   },
 
   /**
    * Handles clicks on the computed property expander.
    */
-  _onExpandClicked: function TextPropertyEditor_onExpandClicked()
+  _onExpandClicked: function TextPropertyEditor_onExpandClicked(aEvent)
   {
     this.expander.classList.toggle("styleinspector-open");
     this.computed.classList.toggle("styleinspector-open");
+    aEvent.stopPropagation();
   },
 
   /**
    * Called when the property name's inplace editor is closed.
    * Ignores the change if the user pressed escape, otherwise
    * commits it.
    *
    * @param {string} aValue
@@ -1819,54 +1808,95 @@ TextPropertyEditor.prototype = {
  *    {function} start:
  *       Will be called when the inplace editor is initialized.
  *    {function} change:
  *       Will be called when the text input changes.  Will be called
  *       with the current value of the text input.
  *    {function} done:
  *       Called when input is committed or blurred.  Called with
  *       current value and a boolean telling the caller whether to
- *       commit the change.  This function is called after the editor
+ *       commit the change.  This function is called before the editor
  *       has been torn down.
+ *    {function} destroy:
+ *       Called when the editor is destroyed and has been torn down.
  *    {string} advanceChars:
  *       If any characters in advanceChars are typed, focus will advance
  *       to the next element.
  */
 function editableField(aOptions)
 {
-  aOptions.element.addEventListener("focus", function() {
+  editableItem(aOptions.element, function(aElement) {
     new InplaceEditor(aOptions);
-  }, false);
+  });
+}
 
-  // In order to allow selection on the element, prevent focus on
-  // mousedown.  Focus on click instead.
-  aOptions.element.addEventListener("mousedown", function(evt) {
-    evt.preventDefault();
-  }, false);
-  aOptions.element.addEventListener("click", function(evt) {
+/**
+ * Handle events for an element that should respond to
+ * clicks and sit in the editing tab order, and call
+ * a callback when it is activated.
+ *
+ * @param DOMElement aElement
+ *        The DOM element.
+ * @param function aCallback
+ *        Called when the editor is activated.
+ */
+
+function editableItem(aElement, aCallback)
+{
+  aElement.addEventListener("click", function(evt) {
     let win = this.ownerDocument.defaultView;
     let selection = win.getSelection();
     if (selection.isCollapsed) {
-      aOptions.element.focus();
-    } else {
-      selection.removeAllRanges();
+      aCallback(aElement);
+    }
+    evt.stopPropagation();
+  }, false);
+
+  // If focused by means other than a click, start editing by
+  // pressing enter or space.
+  aElement.addEventListener("keypress", function(evt) {
+    if (evt.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_RETURN ||
+        evt.charCode === Ci.nsIDOMKeyEvent.DOM_VK_SPACE) {
+      aCallback(aElement);
     }
+  }, true);
+
+  // Ugly workaround - the element is focused on mousedown but
+  // the editor is activated on click/mouseup.  This leads
+  // to an ugly flash of the focus ring before showing the editor.
+  // So hide the focus ring while the mouse is down.
+  aElement.addEventListener("mousedown", function(evt) {
+    let cleanup = function() {
+      aElement.style.removeProperty("outline-style");
+      aElement.removeEventListener("mouseup", cleanup, false);
+      aElement.removeEventListener("mouseout", cleanup, false);
+    };
+    aElement.style.setProperty("outline-style", "none");
+    aElement.addEventListener("mouseup", cleanup, false);
+    aElement.addEventListener("mouseout", cleanup, false);
   }, false);
+
+  // Mark the element editable field for tab
+  // navigation while editing.
+  aElement._editable = true;
 }
+
 var _editableField = editableField;
 
 function InplaceEditor(aOptions)
 {
   this.elt = aOptions.element;
+  let doc = this.elt.ownerDocument;
+  this.doc = doc;
   this.elt.inplaceEditor = this;
 
   this.change = aOptions.change;
   this.done = aOptions.done;
+  this.destroy = aOptions.destroy;
   this.initial = aOptions.initial ? aOptions.initial : this.elt.textContent;
-  this.doc = this.elt.ownerDocument;
 
   this._onBlur = this._onBlur.bind(this);
   this._onKeyPress = this._onKeyPress.bind(this);
   this._onInput = this._onInput.bind(this);
 
   this._createInput();
   this._autosize();
 
@@ -1906,23 +1936,34 @@ InplaceEditor.prototype = {
     copyTextStyles(this.elt, this.input);
   },
 
   /**
    * Get rid of the editor.
    */
   _clear: function InplaceEditor_clear()
   {
+    if (!this.input) {
+      // Already cleared.
+      return;
+    }
+
     this.input.removeEventListener("blur", this._onBlur, false);
     this.input.removeEventListener("keypress", this._onKeyPress, false);
     this.input.removeEventListener("oninput", this._onInput, false);
     this._stopAutosize();
 
+    this.elt.style.display = this.originalDisplay;
+    this.elt.focus();
+
+    if (this.destroy) {
+      this.destroy();
+    }
+
     this.elt.parentNode.removeChild(this.input);
-    this.elt.style.display = this.originalDisplay;
     this.input = null;
 
     delete this.elt.inplaceEditor;
     delete this.elt;
   },
 
   /**
    * Keeps the editor close to the size of its input string.  This is pretty
@@ -1975,44 +2016,81 @@ InplaceEditor.prototype = {
     // any letter that could be typed, otherwise we'll scroll before
     // we get a chance to resize.  Yuck.
     let width = this._measurement.offsetWidth + 10;
 
     this.input.style.width = width + "px";
   },
 
   /**
-   * Handle loss of focus by calling the client's done handler and
-   * clearing out.
+   * Call the client's done handler and clear out.
+   */
+  _apply: function InplaceEditor_apply(aEvent)
+  {
+    if (this._applied) {
+      return;
+    }
+
+    this._applied = true;
+
+    if (this.done) {
+      let val = this.input.value.trim();
+      return this.done(this.cancelled ? this.initial : val, !this.cancelled);
+    }
+    return null;
+  },
+
+  /**
+   * Handle loss of focus by calling done if it hasn't been called yet.
    */
   _onBlur: function InplaceEditor_onBlur(aEvent)
   {
-    let val = this.input.value.trim();
+    this._apply();
     this._clear();
-    if (this.done) {
-      this.done(this.cancelled ? this.initial : val, !this.cancelled);
-    }
   },
 
   _onKeyPress: function InplaceEditor_onKeyPress(aEvent)
   {
     let prevent = false;
     if (aEvent.charCode in this._advanceCharCodes
-       || aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_RETURN) {
-      // Focus the next element, triggering a blur which
-      // will eventually shut us down (making return roughly equal
-      // tab).
+       || aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_RETURN
+       || aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_TAB) {
       prevent = true;
-      moveFocus(this.input.ownerDocument.defaultView, FOCUS_FORWARD);
+
+      let direction = FOCUS_FORWARD;
+      if (aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_TAB &&
+          aEvent.shiftKey) {
+        this.cancelled = true;
+        direction = FOCUS_BACKWARD;
+      }
+
+      let input = this.input;
+
+      this._apply();
+
+      let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
+      if (fm.focusedElement === input) {
+        // If the focused element wasn't changed by the done callback,
+        // move the focus as requested.
+        let next = moveFocus(this.doc.defaultView, direction);
+
+        // If the next node to be focused has been tagged as an editable
+        // node, send it a click event to trigger
+        if (next && next.ownerDocument === this.doc && next._editable) {
+          next.click();
+        }
+      }
+
+      this._clear();
     } else if (aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE) {
-      // Cancel and blur ourselves.  |_onBlur| will call the user's
-      // done handler for us.
+      // Cancel and blur ourselves.
       prevent = true;
       this.cancelled = true;
-      this.input.blur();
+      this._apply();
+      this._clear();
       aEvent.stopPropagation();
     } else if (aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_SPACE) {
       // No need for leading spaces here.  This is particularly
       // noticable when adding a property: it's very natural to type
       // <name>: (which advances to the next property) then spacebar.
       prevent = !this.input.value;
     }
 
@@ -2189,17 +2267,17 @@ function copyTextStyles(aFrom, aTo)
 }
 
 /**
  * Trigger a focus change similar to pressing tab/shift-tab.
  */
 function moveFocus(aWin, aDirection)
 {
   let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
-  fm.moveFocus(aWin, null, aDirection, 0);
+  return fm.moveFocus(aWin, null, aDirection, 0);
 }
 
 XPCOMUtils.defineLazyGetter(this, "clipboardHelper", function() {
   return Cc["@mozilla.org/widget/clipboardhelper;1"].
     getService(Ci.nsIClipboardHelper);
 });
 
 XPCOMUtils.defineLazyGetter(this, "_strings", function() {
--- a/browser/devtools/styleinspector/styleinspector.css
+++ b/browser/devtools/styleinspector/styleinspector.css
@@ -40,8 +40,17 @@
 
 .ruleview-code {
   direction: ltr;
 }
 
 .ruleview-property:not(:hover) > .ruleview-enableproperty {
   visibility: hidden;
 }
+
+.ruleview-namecontainer {
+  cursor: text;
+}
+
+.ruleview-propertycontainer {
+  cursor: text;
+  padding-right: 15px;
+}
--- a/browser/devtools/styleinspector/test/browser_ruleview_bug_703643_context_menu_copy.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_bug_703643_context_menu_copy.js
@@ -46,40 +46,16 @@ function openInspector()
   ok(!InspectorUI.inspecting, "Inspector is not highlighting");
   ok(InspectorUI.store.isEmpty(), "Inspector.store is empty");
 
   Services.obs.addObserver(inspectorUIOpen,
     InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
   InspectorUI.openInspectorUI();
 }
 
-function waitForEditorFocus(aParent, aCallback)
-{
-  aParent.addEventListener("focus", function onFocus(evt) {
-    if (inplaceEditor(evt.target)) {
-      aParent.removeEventListener("focus", onFocus, true);
-      let editor = inplaceEditor(evt.target);
-      executeSoon(function() {
-        aCallback(editor);
-      });
-    }
-  }, true);
-}
-
-function waitForEditorBlur(aEditor, aCallback)
-{
-  let input = aEditor.input;
-  input.addEventListener("blur", function onBlur() {
-    input.removeEventListener("blur", onBlur, false);
-    executeSoon(function() {
-      aCallback();
-    });
-  }, false);
-}
-
 function inspectorUIOpen()
 {
   Services.obs.removeObserver(inspectorUIOpen,
     InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
 
   // Make sure the inspector is open.
   ok(InspectorUI.inspecting, "Inspector is highlighting");
   ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open");
@@ -247,25 +223,26 @@ function checkCopyPropertyValue()
     failedClipboard(expectedPattern, checkCopySelection);
   });
 }
 
 function checkCopySelection()
 {
   let contentDoc = ruleViewFrame().contentDocument;
   let props = contentDoc.querySelectorAll(".ruleview-property");
+  let values = contentDoc.querySelectorAll(".ruleview-propertycontainer");
 
   let range = document.createRange();
   range.setStart(props[0], 0);
-  range.setEnd(props[4], 8);
+  range.setEnd(values[4], 2);
 
   let selection = ruleViewFrame().contentWindow.getSelection();
   selection.addRange(range);
 
-  info("Checking that _boundCopy()  returns the correct" +
+  info("Checking that _boundCopy() returns the correct " +
     "clipboard value");
   let expectedPattern = "    margin: 10em;[\\r\\n]+" +
                         "    font-size: 14pt;[\\r\\n]+" +
                         "    font-family: helvetica,sans-serif;[\\r\\n]+" +
                         "    color: rgb\\(170, 170, 170\\);[\\r\\n]+" +
                         "}[\\r\\n]+" +
                         "html {[\\r\\n]+" +
                         "    color: rgb\\(0, 0, 0\\);[\\r\\n]*";
--- a/browser/devtools/styleinspector/test/browser_ruleview_editor.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_editor.js
@@ -45,33 +45,33 @@ function testReturnCommit()
     initial: "explicit initial",
     start: function() {
       is(inplaceEditor(span).input.value, "explicit initial", "Explicit initial value should be used.");
       inplaceEditor(span).input.value = "Test Value";
       EventUtils.sendKey("return");
     },
     done: expectDone("Test Value", true, testBlurCommit)
   });
-  span.focus();
+  span.click();
 }
 
 function testBlurCommit()
 {
   clearBody();
   let span = createSpan();
   _editableField({
     element: span,
     start: function() {
       is(inplaceEditor(span).input.value, "Edit Me!", "textContent of the span used.");
       inplaceEditor(span).input.value = "Test Value";
       inplaceEditor(span).input.blur();
     },
     done: expectDone("Test Value", true, testAdvanceCharCommit)
   });
-  span.focus();
+  span.click();
 }
 
 function testAdvanceCharCommit()
 {
   clearBody();
   let span = createSpan();
   _editableField({
     element: span,
@@ -79,33 +79,33 @@ function testAdvanceCharCommit()
     start: function() {
       let input = inplaceEditor(span).input;
       for each (let ch in "Test:") {
         EventUtils.sendChar(ch);
       }
     },
     done: expectDone("Test", true, testEscapeCancel)
   });
-  span.focus();
+  span.click();
 }
 
 function testEscapeCancel()
 {
   clearBody();
   let span = createSpan();
   _editableField({
     element: span,
     initial: "initial text",
     start: function() {
       inplaceEditor(span).input.value = "Test Value";
       EventUtils.sendKey("escape");
     },
     done: expectDone("initial text", false, finishTest)
   });
-  span.focus();
+  span.click();
 }
 
 
 function finishTest()
 {
   doc = null;
   gBrowser.removeCurrentTab();
   finish();
--- a/browser/devtools/styleinspector/test/browser_ruleview_editor_changedvalues.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_editor_changedvalues.js
@@ -8,40 +8,16 @@ let CssRuleView = tempScope.CssRuleView;
 let _ElementStyle = tempScope._ElementStyle;
 let _editableField = tempScope._editableField;
 let inplaceEditor = tempScope._getInplaceEditorForSpan;
 
 let doc;
 let ruleDialog;
 let ruleView;
 
-function waitForEditorFocus(aParent, aCallback)
-{
-  aParent.addEventListener("focus", function onFocus(evt) {
-    if (inplaceEditor(evt.target)) {
-      aParent.removeEventListener("focus", onFocus, true);
-      let editor = inplaceEditor(evt.target);
-      executeSoon(function() {
-        aCallback(editor);
-      });
-    }
-  }, true);
-}
-
-function waitForEditorBlur(aEditor, aCallback)
-{
-  let input = aEditor.input;
-  input.addEventListener("blur", function onBlur() {
-    input.removeEventListener("blur", onBlur, false);
-    executeSoon(function() {
-      aCallback();
-    });
-  }, false);
-}
-
 var gRuleViewChanged = false;
 function ruleViewChanged()
 {
   gRuleViewChanged = true;
 }
 
 function expectChange()
 {
@@ -110,17 +86,16 @@ function testCreateNew()
     input.value = "background-color";
 
     waitForEditorFocus(elementRuleEditor.element, function onNewValue(aEditor) {
       expectChange();
       is(elementRuleEditor.rule.textProps.length,  1, "Should have created a new text property.");
       is(elementRuleEditor.propertyList.children.length, 1, "Should have created a property editor.");
       let textProp = elementRuleEditor.rule.textProps[0];
       is(aEditor, inplaceEditor(textProp.editor.valueSpan), "Should be editing the value span now.");
-
       aEditor.input.value = "#XYZ";
       waitForEditorBlur(aEditor, function() {
         expectChange();
         is(textProp.value, "#XYZ", "Text prop should have been changed.");
         is(textProp.editor._validate(), false, "#XYZ should not be a valid entry");
         testEditProperty();
       });
       aEditor.input.blur();
--- a/browser/devtools/styleinspector/test/browser_ruleview_focus.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_focus.js
@@ -11,29 +11,16 @@ let inplaceEditor = tempScope._getInplac
 let doc;
 let stylePanel;
 
 function waitForRuleView(aCallback)
 {
   InspectorUI.currentInspector.once("sidebaractivated-ruleview", aCallback);
 }
 
-function waitForEditorFocus(aParent, aCallback)
-{
-  aParent.addEventListener("focus", function onFocus(evt) {
-    if (inplaceEditor(evt.target)) {
-      aParent.removeEventListener("focus", onFocus, true);
-      let editor = inplaceEditor(evt.target);
-      executeSoon(function() {
-        aCallback(editor);
-      });
-    }
-  }, true);
-}
-
 function openRuleView()
 {
   Services.obs.addObserver(function onOpened() {
     Services.obs.removeObserver(onOpened,
     InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
 
     // Highlight a node.
     let node = content.document.getElementsByTagName("h1")[0];
@@ -68,17 +55,17 @@ function testFocus()
         aEditor.input.blur();
         finishUp();
       });
       EventUtils.sendKey("return");
     });
     EventUtils.sendKey("return");
   });
 
-  brace.focus();
+  brace.click();
 }
 
 function finishUp()
 {
   InspectorUI.sidebar.hide();
   InspectorUI.closeInspectorUI();
   doc = stylePanel = null;
   gBrowser.removeCurrentTab();
--- a/browser/devtools/styleinspector/test/browser_ruleview_ui.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_ui.js
@@ -8,40 +8,16 @@ let CssRuleView = tempScope.CssRuleView;
 let _ElementStyle = tempScope._ElementStyle;
 let _editableField = tempScope._editableField;
 let inplaceEditor = tempScope._getInplaceEditorForSpan;
 
 let doc;
 let ruleDialog;
 let ruleView;
 
-function waitForEditorFocus(aParent, aCallback)
-{
-  aParent.addEventListener("focus", function onFocus(evt) {
-    if (inplaceEditor(evt.target)) {
-      aParent.removeEventListener("focus", onFocus, true);
-      let editor = inplaceEditor(evt.target);
-      executeSoon(function() {
-        aCallback(editor);
-      });
-    }
-  }, true);
-}
-
-function waitForEditorBlur(aEditor, aCallback)
-{
-  let input = aEditor.input;
-  input.addEventListener("blur", function onBlur() {
-    input.removeEventListener("blur", onBlur, false);
-    executeSoon(function() {
-      aCallback();
-    });
-  }, false);
-}
-
 var gRuleViewChanged = false;
 function ruleViewChanged()
 {
   gRuleViewChanged = true;
 }
 
 function expectChange()
 {
--- a/browser/devtools/styleinspector/test/head.js
+++ b/browser/devtools/styleinspector/test/head.js
@@ -300,12 +300,36 @@ ComputedViewPanel.prototype = {
   },
 };
 
 function ruleView()
 {
   return InspectorUI.sidebar._toolContext("ruleview").view;
 }
 
+function waitForEditorFocus(aParent, aCallback)
+{
+  aParent.addEventListener("focus", function onFocus(evt) {
+    if (inplaceEditor(evt.target) && evt.target.tagName == "input") {
+      aParent.removeEventListener("focus", onFocus, true);
+      let editor = inplaceEditor(evt.target);
+      executeSoon(function() {
+        aCallback(editor);
+      });
+    }
+  }, true);
+}
+
+function waitForEditorBlur(aEditor, aCallback)
+{
+  let input = aEditor.input;
+  input.addEventListener("blur", function onBlur() {
+    input.removeEventListener("blur", onBlur, false);
+    executeSoon(function() {
+      aCallback();
+    });
+  }, false);
+}
+
 registerCleanupFunction(tearDown);
 
 waitForExplicitFinish();
 
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -209,16 +209,20 @@ These should match what Safari and other
 
 <!ENTITY inspectMenu.label            "Inspect">
 <!ENTITY inspectMenu.accesskey        "T">
 <!ENTITY inspectMenu.commandkey       "I">
 
 <!ENTITY inspectContextMenu.label     "Inspect Element">
 <!ENTITY inspectContextMenu.accesskey "Q">
 
+<!ENTITY responsiveUI.label            "Responsive Mode">
+<!ENTITY responsiveUI.accesskey        "R">
+<!ENTITY responsiveUI.commandkey       "M">
+
 <!-- LOCALIZATION NOTE (scratchpad.label): This menu item label appears
   -  in the Tools menu. See bug 653093.
   -  The Scratchpad is intended to provide a simple text editor for creating
   -  and evaluating bits of JavaScript code for the purposes of function
   -  prototyping, experimentation and convenient scripting.
   -
   -  It's quite possible that you won't have a good analogue for the word
   -  "Scratchpad" in your locale. You should feel free to find a close
--- a/browser/locales/en-US/chrome/browser/devtools/debugger.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/debugger.properties
@@ -61,16 +61,29 @@ emptyStackText=No stacks to display.
 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
new file mode 100644
--- /dev/null
+++ b/browser/locales/en-US/chrome/browser/devtools/responsiveUI.properties
@@ -0,0 +1,17 @@
+# LOCALIZATION NOTE These strings are used inside the Responsive Mode
+# which is available from the Web Developer sub-menu -> 'Responsive Mode'.
+#
+# 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  (responsiveUI.rotate): label of the rotate button.
+responsiveUI.rotate=rotate
+
+# LOCALIZATION NOTE  (responsiveUI.customResolution): label of the first item
+# in the menulist at the beginning of the toolbar. For %S is replace with the
+# current size of the page. For example: "400x600".
+responsiveUI.customResolution=%S (custom)
--- a/browser/locales/jar.mn
+++ b/browser/locales/jar.mn
@@ -31,16 +31,17 @@
     locale/browser/devtools/styleeditor.properties    (%chrome/browser/devtools/styleeditor.properties)
     locale/browser/devtools/styleeditor.dtd           (%chrome/browser/devtools/styleeditor.dtd)
     locale/browser/devtools/styleinspector.properties (%chrome/browser/devtools/styleinspector.properties)
     locale/browser/devtools/styleinspector.dtd        (%chrome/browser/devtools/styleinspector.dtd)
     locale/browser/devtools/webConsole.dtd            (%chrome/browser/devtools/webConsole.dtd)
     locale/browser/devtools/sourceeditor.properties   (%chrome/browser/devtools/sourceeditor.properties)
     locale/browser/devtools/sourceeditor.dtd          (%chrome/browser/devtools/sourceeditor.dtd)
     locale/browser/devtools/layoutview.dtd            (%chrome/browser/devtools/layoutview.dtd)
+    locale/browser/devtools/responsiveUI.properties   (%chrome/browser/devtools/responsiveUI.properties)
     locale/browser/newTab.dtd                      (%chrome/browser/newTab.dtd)
     locale/browser/newTab.properties               (%chrome/browser/newTab.properties)
     locale/browser/openLocation.dtd                (%chrome/browser/openLocation.dtd)
     locale/browser/openLocation.properties         (%chrome/browser/openLocation.properties)
 *   locale/browser/pageInfo.dtd                    (%chrome/browser/pageInfo.dtd)
     locale/browser/pageInfo.properties             (%chrome/browser/pageInfo.properties)
     locale/browser/quitDialog.properties           (%chrome/browser/quitDialog.properties)
 *   locale/browser/safeMode.dtd                    (%chrome/browser/safeMode.dtd)
--- a/browser/themes/gnomestripe/browser.css
+++ b/browser/themes/gnomestripe/browser.css
@@ -2472,8 +2472,54 @@ html|*#gcli-output-frame {
 
 .gcli-in-todo {
   color: hsl(210,50%,35%);
 }
 
 .gcli-in-closebrace {
   color: hsl(0,0%,80%);
 }
+
+/* Responsive Mode */
+
+vbox[anonid=browserContainer][responsivemode] {
+  background: #2a3643 url("chrome://browser/skin/devtools/responsive-background.png");
+  box-shadow: 0 0 7px black inset;
+  padding: 0 20px 20px 20px;
+}
+
+stack[anonid=browserStack][responsivemode] {
+  box-shadow: 0 0 7px black;
+}
+
+.devtools-responsiveui-toolbar {
+  background: transparent;
+  margin: 10px 0;
+  padding: 0;
+  box-shadow: none;
+}
+
+.devtools-responsiveui-toolbar > menulist,
+.devtools-responsiveui-toolbar > toolbarbutton {
+  min-width: 22px;
+  border-radius: 0;
+}
+
+.devtools-responsiveui-toolbar:-moz-locale-dir(ltr) > *:first-child,
+.devtools-responsiveui-toolbar:-moz-locale-dir(rtl) > *:last-child {
+  margin-left: 0;
+}
+
+.devtools-responsiveui-resizebar {
+  width: 7px;
+  height: 24px;
+  cursor: ew-resize;
+  -moz-transform: translate(12px, -12px);
+  background-image: url("chrome://browser/skin/devtools/responsive-vertical-resizer.png");
+}
+
+.devtools-responsiveui-resizehandle {
+  width: 16px;
+  height: 16px;
+  cursor: se-resize;
+  -moz-transform: translate(12px, 12px);
+  background-image: url("chrome://browser/skin/devtools/responsive-se-resizer.png");
+}
--- a/browser/themes/gnomestripe/devtools/csshtmltree.css
+++ b/browser/themes/gnomestripe/devtools/csshtmltree.css
@@ -169,17 +169,17 @@
   height: 12px;
 }
 
 .ruleview-ruleopen {
   -moz-padding-end: 5px;
 }
 
 .ruleview-ruleclose {
-  width: -moz-min-content;
+  cursor: text;
   padding-right: 20px;
 }
 
 .ruleview-propertylist {
   list-style: none;
   padding: 0;
   margin: 0;
 }
@@ -206,23 +206,23 @@
 
 .ruleview-newproperty {
   /* (enable checkbox width: 12px) + (expander width: 15px) */
   -moz-margin-start: 27px;
 }
 
 .ruleview-propertyname {
   padding: 1px 0;
-  cursor: text;
   color: #0060C0;
-  text-decoration: inherit;
 }
 
+.ruleview-namecontainer,
+.ruleview-propertycontainer,
+.ruleview-propertyname,
 .ruleview-propertyvalue {
-  cursor: text;
   text-decoration: inherit;
 }
 
 .ruleview-computedlist {
   list-style: none;
   padding: 0;
 }
 
@@ -242,8 +242,18 @@
 
 .ruleview-property {
   border-left: 2px solid transparent;
 }
 
 .ruleview-property[dirty] {
   border-left-color: #68E268;
 }
+
+.ruleview-namecontainer > .ruleview-propertyname,
+.ruleview-propertycontainer > .ruleview-propertyvalue {
+  border-bottom: 1px dotted transparent;
+}
+
+.ruleview-namecontainer:hover > .ruleview-propertyname,
+.ruleview-propertycontainer:hover > .ruleview-propertyvalue {
+  border-bottom-color: black;
+}
--- a/browser/themes/gnomestripe/devtools/debugger.css
+++ b/browser/themes/gnomestripe/devtools/debugger.css
@@ -94,17 +94,17 @@
 /**
  * Variable element
  */
 
 .variable {
   -moz-margin-start: 1px;
   -moz-margin-end: 1px;
   margin-top: 2px;
-  border-bottom: 1px dotted #ccc;
+  border-bottom: 1px dotted #ddd;
   border-radius: 8px;
   -moz-transition: background 1s ease-in-out;
   background: #fff;
 }
 
 .variable[changed] {
   -moz-transition-duration: 0.4s;
   background: rgba(255, 255, 0, 0.65);
@@ -131,19 +131,44 @@
 .property > .title > .arrow {
   margin-top: -2px;
 }
 
 .property > .title > .key {
   color: #881090;
 }
 
-.property > .title > .non-enumerable.key,
-.property > .title > .proto.key {
-  color: #c48bc8;
+/**
+ * Non enumerable, configurable and writable variables and properties.
+ */
+
+.property[proto] > .title > .key,
+.variable[non-enumerable] > .title > .name,
+.property[non-enumerable] > .title > .key {
+  opacity: 0.5;
+}
+
+.variable[non-configurable] > .title > .name,
+.property[non-configurable] > .title > .key {
+  border-bottom: 1px dashed #99f;
+}
+
+.variable[non-writable] > .title > .name,
+.property[non-writable] > .title > .key {
+  border-bottom: 1px dashed #f99;
+}
+
+.variable[non-writable] > .title:after,
+.property[non-writable] > .title:after {
+  content: " ";
+  display: inline-block;
+  width: 16px;
+  height: 16px;
+  background: url("chrome://browser/skin/identity-icons-https.png") no-repeat;
+  opacity: 0.5;
 }
 
 /**
  * Property values colors
  */
 
 .token-undefined {
   -moz-padding-start: 6px;
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..271dd5ebcfbdc858f83f0f7ea18a59bb1980d74f
GIT binary patch
literal 3127
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
RWY7Qr002ovPDHLkV1mzP?f?J)
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..cba07d61053c8a529978fc971a644379ed060ec9
GIT binary patch
literal 269
zc$@(Z0rLKdP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0002gNkl<Zc-q~O
zO$z~06o!?$P0`4Pj7%YZqMfYlG%FtsCc>oAlqee;f5eWRS<cF4yf<gExN$~y7EjHo
z`<%}EPAZC`pZLcG=i8k_0F4meEg(_kD@(AJ-^v0`PV2@IY%_)0hF_q{doTm1OAwZs
z2#V~35!hXWu*!6x$S$yeCuQWH@*R*&CwIX@%JKrfgO@x8Z7?C(OVWWL*#<2zBzcvz
z1N>*Rhld^DJTsdXU2~ug+)o{7*<!t)GnxbWm}W;WHRkb*{2uV%Ef6waoROO!_)UTk
TAbx#Y00000NkvXXu0mjfIhk<>
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..b68ec90282d332d65eac2fec5ba66b4a9d861e03
GIT binary patch
literal 174
zc%17D@N?(olHy`uVBq!ia0vp^>_9BR!3HGFE<K+Mq)I(q978H@y}h)WtHD6z*vCfS
z#JGUw=?e~??v~PRit?Ejc_ismsLr#}=99%Khc^6RkoxzA{eGviw!KD5vcGW4vtv_V
zx$sKelS_J$9kL?aY3m0T-A@q@8b2_c{r_X*$G@{967S7&yta_nKJ`LHf63$OrpD5n
Z>QVL@8_IjyzX2V<;OXk;vd$@?2>|YyNV)(3
--- a/browser/themes/gnomestripe/jar.mn
+++ b/browser/themes/gnomestripe/jar.mn
@@ -152,16 +152,19 @@ browser.jar:
   skin/classic/browser/devtools/layoutview.css        (devtools/layoutview.css)
   skin/classic/browser/devtools/layout-buttons.png    (devtools/layout-buttons.png)
   skin/classic/browser/devtools/debugger-pause.png     (devtools/debugger-pause.png)
   skin/classic/browser/devtools/debugger-play.png      (devtools/debugger-play.png)
   skin/classic/browser/devtools/debugger-step-in.png   (devtools/debugger-step-in.png)
   skin/classic/browser/devtools/debugger-step-out.png  (devtools/debugger-step-out.png)
   skin/classic/browser/devtools/debugger-step-over.png (devtools/debugger-step-over.png)
   skin/classic/browser/devtools/inspector-option-icon.png (devtools/inspector-option-icon.png)
+  skin/classic/browser/devtools/responsive-se-resizer.png (devtools/responsive-se-resizer.png)
+  skin/classic/browser/devtools/responsive-vertical-resizer.png (devtools/responsive-vertical-resizer.png)
+  skin/classic/browser/devtools/responsive-background.png (devtools/responsive-background.png)
 #ifdef MOZ_SERVICES_SYNC
   skin/classic/browser/sync-16-throbber.png
   skin/classic/browser/sync-16.png
   skin/classic/browser/sync-24-throbber.png
   skin/classic/browser/sync-32.png
   skin/classic/browser/sync-bg.png
   skin/classic/browser/sync-128.png
   skin/classic/browser/sync-desktopIcon.png
--- a/browser/themes/pinstripe/browser.css
+++ b/browser/themes/pinstripe/browser.css
@@ -3213,8 +3213,54 @@ html|*#gcli-output-frame {
 
 .gcli-in-todo {
   color: hsl(210,50%,35%);
 }
 
 .gcli-in-closebrace {
   color: hsl(0,0%,80%);
 }
+
+/* Responsive Mode */
+
+vbox[anonid=browserContainer][responsivemode] {
+  background: #2a3643 url("chrome://browser/skin/devtools/responsive-background.png");
+  box-shadow: 0 0 7px black inset;
+  padding: 0 20px 20px 20px;
+}
+
+stack[anonid=browserStack][responsivemode] {
+  box-shadow: 0 0 7px black;
+}
+
+.devtools-responsiveui-toolbar {
+  background: transparent;
+  margin: 10px 0;
+  padding: 0;
+  box-shadow: none;
+}
+
+.devtools-responsiveui-toolbar > menulist,
+.devtools-responsiveui-toolbar > toolbarbutton {
+  min-width: 22px;
+  border-radius: 0;
+}
+
+.devtools-responsiveui-toolbar:-moz-locale-dir(ltr) > *:first-child,
+.devtools-responsiveui-toolbar:-moz-locale-dir(rtl) > *:last-child {
+  margin-left: 0;
+}
+
+.devtools-responsiveui-resizebar {
+  width: 7px;
+  height: 24px;
+  cursor: ew-resize;
+  -moz-transform: translate(12px, -12px);
+  background-image: url("chrome://browser/skin/devtools/responsive-vertical-resizer.png");
+}
+
+.devtools-responsiveui-resizehandle {
+  width: 16px;
+  height: 16px;
+  cursor: se-resize;
+  -moz-transform: translate(12px, 12px);
+  background-image: url("chrome://browser/skin/devtools/responsive-se-resizer.png");
+}
--- a/browser/themes/pinstripe/devtools/csshtmltree.css
+++ b/browser/themes/pinstripe/devtools/csshtmltree.css
@@ -171,17 +171,17 @@
   height: 12px;
 }
 
 .ruleview-ruleopen {
   -moz-padding-end: 5px;
 }
 
 .ruleview-ruleclose {
-  width: -moz-min-content;
+  cursor: text;
   padding-right: 20px;
 }
 
 .ruleview-propertylist {
   list-style: none;
   padding: 0;
   margin: 0;
 }
@@ -208,23 +208,23 @@
 
 .ruleview-newproperty {
   /* (enable checkbox width: 12px) + (expander width: 15px) */
   -moz-margin-start: 27px;
 }
 
 .ruleview-propertyname {
   padding: 1px 0;
-  cursor: text;
   color: #0060C0;
-  text-decoration: inherit;
 }
 
+.ruleview-namecontainer,
+.ruleview-propertycontainer,
+.ruleview-propertyname,
 .ruleview-propertyvalue {
-  cursor: text;
   text-decoration: inherit;
 }
 
 .ruleview-computedlist {
   list-style: none;
   padding: 0;
 }
 
@@ -244,8 +244,18 @@
 
 .ruleview-property {
   border-left: 2px solid transparent;
 }
 
 .ruleview-property[dirty] {
   border-left-color: #68E268;
 }
+
+.ruleview-namecontainer > .ruleview-propertyname,
+.ruleview-propertycontainer > .ruleview-propertyvalue {
+  border-bottom: 1px dotted transparent;
+}
+
+.ruleview-namecontainer:hover > .ruleview-propertyname,
+.ruleview-propertycontainer:hover > .ruleview-propertyvalue {
+  border-bottom-color: black;
+}
--- a/browser/themes/pinstripe/devtools/debugger.css
+++ b/browser/themes/pinstripe/devtools/debugger.css
@@ -96,17 +96,17 @@
 /**
  * Variable element
  */
 
 .variable {
   -moz-margin-start: 1px;
   -moz-margin-end: 1px;
   margin-top: 2px;
-  border-bottom: 1px dotted #ccc;
+  border-bottom: 1px dotted #ddd;
   border-radius: 8px;
   -moz-transition: background 1s ease-in-out;
   background: #fff;
 }
 
 .variable[changed] {
   -moz-transition-duration: 0.4s;
   background: rgba(255, 255, 0, 0.65);
@@ -133,19 +133,44 @@
 .property > .title > .arrow {
   margin-top: -2px;
 }
 
 .property > .title > .key {
   color: #881090;
 }
 
-.property > .title > .non-enumerable.key,
-.property > .title > .proto.key {
-  color: #c48bc8;
+/**
+ * Non enumerable, configurable and writable variables and properties.
+ */
+
+.property[proto] > .title > .key,
+.variable[non-enumerable] > .title > .name,
+.property[non-enumerable] > .title > .key {
+  opacity: 0.5;
+}
+
+.variable[non-configurable] > .title > .name,
+.property[non-configurable] > .title > .key {
+  border-bottom: 1px dashed #99f;
+}
+
+.variable[non-writable] > .title > .name,
+.property[non-writable] > .title > .key {
+  border-bottom: 1px dashed #f99;
+}
+
+.variable[non-writable] > .title:after,
+.property[non-writable] > .title:after {
+  content: " ";
+  display: inline-block;
+  width: 16px;
+  height: 16px;
+  background: url("chrome://browser/skin/identity-icons-https.png") no-repeat;
+  opacity: 0.5;
 }
 
 /**
  * Property values colors
  */
 
 .token-undefined {
   -moz-padding-start: 6px;
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..271dd5ebcfbdc858f83f0f7ea18a59bb1980d74f
GIT binary patch
literal 3127
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
RWY7Qr002ovPDHLkV1mzP?f?J)
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..cba07d61053c8a529978fc971a644379ed060ec9
GIT binary patch
literal 269
zc$@(Z0rLKdP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0002gNkl<Zc-q~O
zO$z~06o!?$P0`4Pj7%YZqMfYlG%FtsCc>oAlqee;f5eWRS<cF4yf<gExN$~y7EjHo
z`<%}EPAZC`pZLcG=i8k_0F4meEg(_kD@(AJ-^v0`PV2@IY%_)0hF_q{doTm1OAwZs
z2#V~35!hXWu*!6x$S$yeCuQWH@*R*&CwIX@%JKrfgO@x8Z7?C(OVWWL*#<2zBzcvz
z1N>*Rhld^DJTsdXU2~ug+)o{7*<!t)GnxbWm}W;WHRkb*{2uV%Ef6waoROO!_)UTk
TAbx#Y00000NkvXXu0mjfIhk<>
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..b68ec90282d332d65eac2fec5ba66b4a9d861e03
GIT binary patch
literal 174
zc%17D@N?(olHy`uVBq!ia0vp^>_9BR!3HGFE<K+Mq)I(q978H@y}h)WtHD6z*vCfS
z#JGUw=?e~??v~PRit?Ejc_ismsLr#}=99%Khc^6RkoxzA{eGviw!KD5vcGW4vtv_V
zx$sKelS_J$9kL?aY3m0T-A@q@8b2_c{r_X*$G@{967S7&yta_nKJ`LHf63$OrpD5n
Z>QVL@8_IjyzX2V<;OXk;vd$@?2>|YyNV)(3
--- a/browser/themes/pinstripe/jar.mn
+++ b/browser/themes/pinstripe/jar.mn
@@ -192,17 +192,20 @@ browser.jar:
   skin/classic/browser/devtools/layout-background-grid.png  (devtools/layout-background-grid.png)
   skin/classic/browser/devtools/layoutview.css              (devtools/layoutview.css)
   skin/classic/browser/devtools/layout-buttons.png          (devtools/layout-buttons.png)
   skin/classic/browser/devtools/debugger-pause.png          (devtools/debugger-pause.png)
   skin/classic/browser/devtools/debugger-play.png           (devtools/debugger-play.png)
   skin/classic/browser/devtools/debugger-step-in.png        (devtools/debugger-step-in.png)
   skin/classic/browser/devtools/debugger-step-out.png       (devtools/debugger-step-out.png)
   skin/classic/browser/devtools/debugger-step-over.png      (devtools/debugger-step-over.png)
-  skin/classic/browser/devtools/inspector-option-icon.png     (devtools/inspector-option-icon.png)
+  skin/classic/browser/devtools/inspector-option-icon.png   (devtools/inspector-option-icon.png)
+  skin/classic/browser/devtools/responsive-se-resizer.png   (devtools/responsive-se-resizer.png)
+  skin/classic/browser/devtools/responsive-vertical-resizer.png (devtools/responsive-vertical-resizer.png)
+  skin/classic/browser/devtools/responsive-background.png   (devtools/responsive-background.png)
 #ifdef MOZ_SERVICES_SYNC
   skin/classic/browser/sync-throbber.png
   skin/classic/browser/sync-16.png
   skin/classic/browser/sync-32.png
   skin/classic/browser/sync-bg.png
   skin/classic/browser/sync-128.png
   skin/classic/browser/sync-desktopIcon.png
   skin/classic/browser/sync-mobileIcon.png
--- a/browser/themes/winstripe/browser.css
+++ b/browser/themes/winstripe/browser.css
@@ -3145,8 +3145,55 @@ html|*#gcli-output-frame {
 
 .gcli-in-todo {
   color: hsl(210,50%,35%);
 }
 
 .gcli-in-closebrace {
   color: hsl(0,0%,80%);
 }
+
+/* Responsive Mode */
+
+vbox[anonid=browserContainer][responsivemode] {
+  background: #2a3643 url("chrome://browser/skin/devtools/responsive-background.png");
+  box-shadow: 0 0 7px black inset;
+  padding: 0 20px 20px 20px;
+}
+
+stack[anonid=browserStack][responsivemode] {
+  box-shadow: 0 0 7px black;
+}
+
+.devtools-responsiveui-toolbar {
+  background: transparent;
+  margin: 10px 0;
+  padding: 0;
+  box-shadow: none;
+  border-bottom-width: 0;
+}
+
+.devtools-responsiveui-toolbar > menulist,
+.devtools-responsiveui-toolbar > toolbarbutton {
+  min-width: 22px;
+  border-radius: 0;
+}
+
+.devtools-responsiveui-toolbar:-moz-locale-dir(ltr) > *:first-child,
+.devtools-responsiveui-toolbar:-moz-locale-dir(rtl) > *:last-child {
+  margin-left: 0;
+}
+
+.devtools-responsiveui-resizebar {
+  width: 7px;
+  height: 24px;
+  cursor: ew-resize;
+  -moz-transform: translate(12px, -12px);
+  background-image: url("chrome://browser/skin/devtools/responsive-vertical-resizer.png");
+}
+
+.devtools-responsiveui-resizehandle {
+  width: 16px;
+  height: 16px;
+  cursor: se-resize;
+  -moz-transform: translate(12px, 12px);
+  background-image: url("chrome://browser/skin/devtools/responsive-se-resizer.png");
+}
--- a/browser/themes/winstripe/devtools/csshtmltree.css
+++ b/browser/themes/winstripe/devtools/csshtmltree.css
@@ -170,17 +170,17 @@
   height: 12px;
 }
 
 .ruleview-ruleopen {
   -moz-padding-end: 5px;
 }
 
 .ruleview-ruleclose {
-  width: -moz-min-content;
+  cursor: text;
   padding-right: 20px;
 }
 
 .ruleview-propertylist {
   list-style: none;
   padding: 0;
   margin: 0;
 }
@@ -207,23 +207,23 @@
 
 .ruleview-newproperty {
   /* (enable checkbox width: 12px) + (expander width: 15px) */
   -moz-margin-start: 27px;
 }
 
 .ruleview-propertyname {
   padding: 1px 0;
-  cursor: text;
   color: #0060C0;
-  text-decoration: inherit;
 }
 
+.ruleview-namecontainer,
+.ruleview-propertycontainer,
+.ruleview-propertyname,
 .ruleview-propertyvalue {
-  cursor: text;
   text-decoration: inherit;
 }
 
 .ruleview-computedlist {
   list-style: none;
   padding: 0;
 }
 
@@ -243,8 +243,18 @@
 
 .ruleview-property {
   border-left: 2px solid transparent;
 }
 
 .ruleview-property[dirty] {
   border-left-color: #68E268;
 }
+
+.ruleview-namecontainer > .ruleview-propertyname,
+.ruleview-propertycontainer > .ruleview-propertyvalue {
+  border-bottom: 1px dotted transparent;
+}
+
+.ruleview-namecontainer:hover > .ruleview-propertyname,
+.ruleview-propertycontainer:hover > .ruleview-propertyvalue {
+  border-bottom-color: black;
+}
--- a/browser/themes/winstripe/devtools/debugger.css
+++ b/browser/themes/winstripe/devtools/debugger.css
@@ -94,17 +94,17 @@
 /**
  * Variable element
  */
 
 .variable {
   -moz-margin-start: 1px;
   -moz-margin-end: 1px;
   margin-top: 2px;
-  border-bottom: 1px dotted #ccc;
+  border-bottom: 1px dotted #ddd;
   border-radius: 8px;
   -moz-transition: background 1s ease-in-out;
   background: #fff;
 }
 
 .variable[changed] {
   -moz-transition-duration: 0.4s;
   background: rgba(255, 255, 0, 0.65);
@@ -131,19 +131,44 @@
 .property > .title > .arrow {
   margin-top: -2px;
 }
 
 .property > .title > .key {
   color: #881090;
 }
 
-.property > .title > .non-enumerable.key,
-.property > .title > .proto.key {
-  color: #c48bc8;
+/**
+ * Non enumerable, configurable and writable variables and properties.
+ */
+
+.property[proto] > .title > .key,
+.variable[non-enumerable] > .title > .name,
+.property[non-enumerable] > .title > .key {
+  opacity: 0.5;
+}
+
+.variable[non-configurable] > .title > .name,
+.property[non-configurable] > .title > .key {
+  border-bottom: 1px dashed #99f;
+}
+
+.variable[non-writable] > .title > .name,
+.property[non-writable] > .title > .key {
+  border-bottom: 1px dashed #f99;
+}
+
+.variable[non-writable] > .title:after,
+.property[non-writable] > .title:after {
+  content: " ";
+  display: inline-block;
+  width: 16px;
+  height: 16px;
+  background: url("chrome://browser/skin/identity-icons-https.png") no-repeat;
+  opacity: 0.5;
 }
 
 /**
  * Property values colors
  */
 
 .token-undefined {
   -moz-padding-start: 6px;
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..271dd5ebcfbdc858f83f0f7ea18a59bb1980d74f
GIT binary patch
literal 3127
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
RWY7Qr002ovPDHLkV1mzP?f?J)
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..cba07d61053c8a529978fc971a644379ed060ec9
GIT binary patch
literal 269
zc$@(Z0rLKdP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0002gNkl<Zc-q~O
zO$z~06o!?$P0`4Pj7%YZqMfYlG%FtsCc>oAlqee;f5eWRS<cF4yf<gExN$~y7EjHo
z`<%}EPAZC`pZLcG=i8k_0F4meEg(_kD@(AJ-^v0`PV2@IY%_)0hF_q{doTm1OAwZs
z2#V~35!hXWu*!6x$S$yeCuQWH@*R*&CwIX@%JKrfgO@x8Z7?C(OVWWL*#<2zBzcvz
z1N>*Rhld^DJTsdXU2~ug+)o{7*<!t)GnxbWm}W;WHRkb*{2uV%Ef6waoROO!_)UTk
TAbx#Y00000NkvXXu0mjfIhk<>
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..b68ec90282d332d65eac2fec5ba66b4a9d861e03
GIT binary patch
literal 174
zc%17D@N?(olHy`uVBq!ia0vp^>_9BR!3HGFE<K+Mq)I(q978H@y}h)WtHD6z*vCfS
z#JGUw=?e~??v~PRit?Ejc_ismsLr#}=99%Khc^6RkoxzA{eGviw!KD5vcGW4vtv_V
zx$sKelS_J$9kL?aY3m0T-A@q@8b2_c{r_X*$G@{967S7&yta_nKJ`LHf63$OrpD5n
Z>QVL@8_IjyzX2V<;OXk;vd$@?2>|YyNV)(3
--- a/browser/themes/winstripe/jar.mn
+++ b/browser/themes/winstripe/jar.mn
@@ -179,16 +179,19 @@ browser.jar:
         skin/classic/browser/devtools/layoutview.css                (devtools/layoutview.css)
         skin/classic/browser/devtools/layout-buttons.png            (devtools/layout-buttons.png)
         skin/classic/browser/devtools/debugger-pause.png            (devtools/debugger-pause.png)
         skin/classic/browser/devtools/debugger-play.png             (devtools/debugger-play.png)
         skin/classic/browser/devtools/debugger-step-in.png          (devtools/debugger-step-in.png)
         skin/classic/browser/devtools/debugger-step-out.png         (devtools/debugger-step-out.png)
         skin/classic/browser/devtools/debugger-step-over.png        (devtools/debugger-step-over.png)
         skin/classic/browser/devtools/inspector-option-icon.png     (devtools/inspector-option-icon.png)
+        skin/classic/browser/devtools/responsive-se-resizer.png     (devtools/responsive-se-resizer.png)
+        skin/classic/browser/devtools/responsive-vertical-resizer.png (devtools/responsive-vertical-resizer.png)
+        skin/classic/browser/devtools/responsive-background.png     (devtools/responsive-background.png)
 #ifdef MOZ_SERVICES_SYNC
         skin/classic/browser/sync-throbber.png
         skin/classic/browser/sync-16.png
         skin/classic/browser/sync-32.png
         skin/classic/browser/sync-128.png
         skin/classic/browser/sync-bg.png
         skin/classic/browser/sync-desktopIcon.png
         skin/classic/browser/sync-mobileIcon.png
@@ -373,17 +376,20 @@ browser.jar:
         skin/classic/aero/browser/devtools/layout-background-grid.png (devtools/layout-background-grid.png)
         skin/classic/aero/browser/devtools/layoutview.css            (devtools/layoutview.css)
         skin/classic/aero/browser/devtools/layout-buttons.png        (devtools/layout-buttons.png)
         skin/classic/aero/browser/devtools/debugger-pause.png        (devtools/debugger-pause.png)
         skin/classic/aero/browser/devtools/debugger-play.png         (devtools/debugger-play.png)
         skin/classic/aero/browser/devtools/debugger-step-in.png      (devtools/debugger-step-in.png)
         skin/classic/aero/browser/devtools/debugger-step-out.png     (devtools/debugger-step-out.png)
         skin/classic/aero/browser/devtools/debugger-step-over.png    (devtools/debugger-step-over.png)
-        skin/classic/aero/browser/devtools/inspector-option-icon.png   (devtools/inspector-option-icon.png)
+        skin/classic/aero/browser/devtools/inspector-option-icon.png (devtools/inspector-option-icon.png)
+        skin/classic/aero/browser/devtools/responsive-se-resizer.png (devtools/responsive-se-resizer.png)
+        skin/classic/aero/browser/devtools/responsive-vertical-resizer.png (devtools/responsive-vertical-resizer.png)
+        skin/classic/aero/browser/devtools/responsive-background.png (devtools/responsive-background.png)
 #ifdef MOZ_SERVICES_SYNC
         skin/classic/aero/browser/sync-throbber.png
         skin/classic/aero/browser/sync-16.png
         skin/classic/aero/browser/sync-32.png
         skin/classic/aero/browser/sync-128.png
         skin/classic/aero/browser/sync-bg.png
         skin/classic/aero/browser/sync-desktopIcon.png
         skin/classic/aero/browser/sync-mobileIcon.png
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -5244,25 +5244,49 @@ var RemoteDebugger = {
   _getPort: function _rd_getPort() {
     return Services.prefs.getIntPref("remote-debugger.port");
   },
 
   _isEnabled: function rd_isEnabled() {
     return Services.prefs.getBoolPref("remote-debugger.enabled");
   },
 
+  /**
+   * Prompt the user to accept or decline the incoming connection.
+   *
+   * @return true if the connection should be permitted, false otherwise
+   */
+  _allowConnection: function rd_allowConnection() {
+    let title = Strings.browser.GetStringFromName("remoteIncomingPromptTitle");
+    let msg = Strings.browser.GetStringFromName("remoteIncomingPromptMessage");
+    let btn = Strings.browser.GetStringFromName("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, btn, null, { value: false });
+    if (result == 0)
+      return true;
+    if (result == 2) {
+      this._stop();
+      Services.prefs.setBoolPref("remote-debugger.enabled", false);
+    }
+    return false;
+  },
+
   _restart: function rd_restart() {
     this._stop();
     this._start();
   },
 
   _start: function rd_start() {
     try {
       if (!DebuggerServer.initialized) {
-        DebuggerServer.init();
+        DebuggerServer.init(this._allowConnection);
         DebuggerServer.addActors("chrome://browser/content/dbg-browser-actors.js");
       }
 
       let port = this._getPort();
       DebuggerServer.openListener(port, false);
       dump("Remote debugger listening on port " + port);
     } catch(e) {
       dump("Remote debugger didn't start: " + e);
--- a/mobile/android/locales/en-US/chrome/browser.properties
+++ b/mobile/android/locales/en-US/chrome/browser.properties
@@ -250,8 +250,17 @@ clickToPlayPlugins.playPlugins=Play Plug
 
 # Site settings dialog
 # LOCALIZATION NOTE (siteSettings.labelToValue): This string will be used to
 # dislay a list of current permissions settings for a site.
 # Example: "Store Offline Data: Allow"
 siteSettings.labelToValue=%S: %S
 
 masterPassword.incorrect=Incorrect password
+
+# Debugger
+# 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?
+remoteIncomingPromptDisable=Disable
--- a/testing/marionette/components/marionettecomponent.js
+++ b/testing/marionette/components/marionettecomponent.js
@@ -65,17 +65,18 @@ MarionetteComponent.prototype = {
         port = Services.prefs.getIntPref('marionette.defaultPrefs.port');
       }
       catch(e) {
         port = 2828;
       }
       try {
         Cu.import('resource:///modules/devtools/dbg-server.jsm');
         DebuggerServer.addActors('chrome://marionette/content/marionette-actors.js');
-        DebuggerServer.initTransport();
+        // Always allow remote connections.
+        DebuggerServer.initTransport(function () { return true; });
         DebuggerServer.openListener(port, true);
       }
       catch(e) {
         this.logger.error('exception: ' + e.name + ', ' + e.message);
       }
     }
   },
 
--- a/toolkit/devtools/debugger/server/dbg-server.js
+++ b/toolkit/devtools/debugger/server/dbg-server.js
@@ -54,42 +54,52 @@ const ServerSocket = CC("@mozilla.org/ne
 
 /***
  * Public API
  */
 var DebuggerServer = {
   _listener: null,
   _transportInitialized: false,
   xpcInspector: null,
+  _allowConnection: null,
 
   /**
    * 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() {
+  init: function DH_init(aAllowConnectionCallback) {
     if (this.initialized) {
       return;
     }
 
     this.xpcInspector = Cc["@mozilla.org/jsinspector;1"].getService(Ci.nsIJSInspector);
-    this.initTransport();
+    this.initTransport(aAllowConnectionCallback);
     this.addActors("chrome://global/content/devtools/dbg-script-actors.js");
   },
 
   /**
    * 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
+   *        remote protocol conection should be allowed or refused.
    */
-  initTransport: function DH_initTransport() {
+  initTransport: function DH_initTransport(aAllowConnectionCallback) {
     if (this._transportInitialized) {
       return;
     }
 
     this._connections = {};
     this._nextConnID = 0;
     this._transportInitialized = true;
+    this._allowConnection = aAllowConnectionCallback;
   },
 
   get initialized() { return !!this.xpcInspector; },
 
   /**
    * Load a subscript into the debugging global.
    *
    * @param aURL string A url that will be loaded as a subscript into the
@@ -112,16 +122,19 @@ var DebuggerServer = {
    * Listens on the given port for remote debugger connections.
    *
    * @param aPort int
    *        The port to listen on.
    * @param aLocalOnly bool
    *        If true, server will listen on the loopback device.
    */
   openListener: function DH_openListener(aPort, aLocalOnly) {
+    if (!Services.prefs.getBoolPref("devtools.debugger.remote-enabled")) {
+      return false;
+    }
     this._checkInit();
 
     if (this._listener) {
       throw "Debugging listener already open.";
     }
 
     try {
       let socket = new ServerSocket(aPort, aLocalOnly, 4);
@@ -204,16 +217,19 @@ 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());
--- a/toolkit/devtools/debugger/tests/unit/head_dbg.js
+++ b/toolkit/devtools/debugger/tests/unit/head_dbg.js
@@ -7,16 +7,18 @@ const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 Cu.import("resource://gre/modules/Services.jsm");
 
 // Always log packets when running tests. runxpcshelltests.py will throw
 // the output away anyway, unless you give it the --verbose flag.
 Services.prefs.setBoolPref("devtools.debugger.log", true);
+// Enable remote debugging for the relevant tests.
+Services.prefs.setBoolPref("devtools.debugger.remote-enabled", true);
 
 Cu.import("resource:///modules/devtools/dbg-server.jsm");
 Cu.import("resource:///modules/devtools/dbg-client.jsm");
 
 // Convert an nsIScriptError 'aFlags' value into an appropriate string.
 function scriptErrorFlagsToKind(aFlags) {
   var kind;
   if (aFlags & Ci.nsIScriptError.warningFlag)
@@ -118,17 +120,18 @@ function attachTestGlobalClientAndResume
 }
 
 /**
  * Initialize the testing debugger server.
  */
 function initTestDebuggerServer()
 {
   DebuggerServer.addActors("resource://test/testactors.js");
-  DebuggerServer.init();
+  // Allow incoming connections.
+  DebuggerServer.init(function () { return true; });
 }
 
 function finishClient(aClient)
 {
   aClient.close(function() {
     do_test_finished();
   });
 }
--- a/toolkit/devtools/debugger/tests/unit/test_attach.js
+++ b/toolkit/devtools/debugger/tests/unit/test_attach.js
@@ -3,17 +3,18 @@
 
 var gClient;
 var gDebuggee;
 
 function run_test()
 {
   DebuggerServer.addActors("resource://test/testactors.js");
 
-  DebuggerServer.init();
+  // Allow incoming connections.
+  DebuggerServer.init(function () { return true; });
   gDebuggee = testGlobal("test-1");
   DebuggerServer.addTestGlobal(gDebuggee);
 
   let transport = DebuggerServer.connectPipe();
   gClient = new DebuggerClient(transport);
   gClient.connect(function(aType, aTraits) {
     getTestGlobalContext(gClient, "test-1", function(aContext) {
       test_attach(aContext);
--- a/toolkit/devtools/debugger/tests/unit/test_dbgactor.js
+++ b/toolkit/devtools/debugger/tests/unit/test_dbgactor.js
@@ -6,17 +6,18 @@ Cu.import("resource:///modules/devtools/
 
 var gClient;
 var gDebuggee;
 
 function run_test()
 {
   DebuggerServer.addActors("resource://test/testactors.js");
 
-  DebuggerServer.init();
+  // Allow incoming connections.
+  DebuggerServer.init(function () { return true; });
   gDebuggee = testGlobal("test-1");
   DebuggerServer.addTestGlobal(gDebuggee);
 
   let transport = DebuggerServer.connectPipe();
   gClient = new DebuggerClient(transport);
   gClient.addListener("connected", function(aEvent, aType, aTraits) {
     gClient.request({ to: "root", type: "listContexts" }, function(aResponse) {
       do_check_true('contexts' in aResponse);
--- a/toolkit/devtools/debugger/tests/unit/test_dbgclient_debuggerstatement.js
+++ b/toolkit/devtools/debugger/tests/unit/test_dbgclient_debuggerstatement.js
@@ -6,17 +6,18 @@ Cu.import("resource:///modules/devtools/
 
 var gClient;
 var gDebuggee;
 
 function run_test()
 {
   DebuggerServer.addActors("resource://test/testactors.js");
 
-  DebuggerServer.init();
+  // Allow incoming connections.
+  DebuggerServer.init(function () { return true; });
   gDebuggee = testGlobal("test-1");
   DebuggerServer.addTestGlobal(gDebuggee);
 
   let transport = DebuggerServer.connectPipe();
   gClient = new DebuggerClient(transport);
   gClient.connect(function(aType, aTraits) {
     getTestGlobalContext(gClient, "test-1", function(aContext) {
       test_attach(aContext);
--- a/toolkit/devtools/debugger/tests/unit/test_dbgglobal.js
+++ b/toolkit/devtools/debugger/tests/unit/test_dbgglobal.js
@@ -9,17 +9,18 @@ function run_test()
   // Should get an exception if we try to interact with DebuggerServer
   // before we initialize it...
   check_except(function() {
     DebuggerServer.openListener(2929, true);
   });
   check_except(DebuggerServer.closeListener);
   check_except(DebuggerServer.connectPipe);
 
-  DebuggerServer.init();
+  // Allow incoming connections.
+  DebuggerServer.init(function () { return true; });
 
   // These should still fail because we haven't added a createRootActor
   // implementation yet.
   check_except(function() {
     DebuggerServer.openListener(2929, true);
   });
   check_except(DebuggerServer.closeListener);
   check_except(DebuggerServer.connectPipe);
--- a/toolkit/devtools/debugger/tests/unit/test_dbgsocket.js
+++ b/toolkit/devtools/debugger/tests/unit/test_dbgsocket.js
@@ -1,17 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource:///modules/devtools/dbg-server.jsm");
 Cu.import("resource:///modules/devtools/dbg-client.jsm");
 
 function run_test()
 {
-  DebuggerServer.init();
+  // Allow incoming connections.
+  DebuggerServer.init(function () { return true; });
   DebuggerServer.addActors("resource://test/testactors.js");
 
   add_test(test_socket_conn);
   add_test(test_socket_shutdown);
   add_test(test_pipe_conn);
 
   run_next_test();
 }
--- a/toolkit/devtools/debugger/tests/unit/test_interrupt.js
+++ b/toolkit/devtools/debugger/tests/unit/test_interrupt.js
@@ -3,17 +3,18 @@
 
 var gClient;
 var gDebuggee;
 
 function run_test()
 {
   DebuggerServer.addActors("resource://test/testactors.js");
 
-  DebuggerServer.init();
+  // Allow incoming connections.
+  DebuggerServer.init(function () { return true; });
   gDebuggee = testGlobal("test-1");
   DebuggerServer.addTestGlobal(gDebuggee);
 
   let transport = DebuggerServer.connectPipe();
   gClient = new DebuggerClient(transport);
   gClient.connect(function(aType, aTraits) {
     getTestGlobalContext(gClient, "test-1", function(aContext) {
       test_attach(aContext);