merge fx-team to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 14 Jul 2014 15:14:40 +0200
changeset 215708 340b19c14d3d388b7ae1a15bff9695cb0141ac0a
parent 215688 415c2a4dc7784ca4c129fd1004fc364a18ce2b75 (current diff)
parent 215707 b257f50981e7f19bb218ead06f5aaadf26c6bb59 (diff)
child 215714 827f88a7eb65f8557c7f05748139bb69691f51fa
child 215732 ad5f8677a2bf516ceb130a7d116e955a5b16a8a8
child 215781 9ac17092d1046b1d56f7b9df3c89be7932aac768
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone33.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge fx-team to mozilla-central a=merge
browser/devtools/inspector/test/browser_inspector_infobar.js
mobile/android/chrome/content/browser.js
mobile/android/search/java/org/mozilla/search/DetailActivity.java
mobile/android/search/java/org/mozilla/search/autocomplete/AutoCompleteFragment.java
mobile/android/search/java/org/mozilla/search/stream/CardStreamFragment.java
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -925,17 +925,17 @@ let RemoteDebugger = {
 
     if (!DebuggerServer.initialized) {
       // Can this really happen if we are running?
       this._running = false;
       return;
     }
 
     try {
-      DebuggerServer.closeListener();
+      DebuggerServer.closeAllListeners();
     } catch (e) {
       dump('Unable to stop debugger server: ' + e + '\n');
     }
     this._running = false;
   }
 }
 
 let KeyboardHelper = {
--- a/browser/components/nsBrowserContentHandler.js
+++ b/browser/components/nsBrowserContentHandler.js
@@ -534,16 +534,17 @@ nsBrowserContentHandler.prototype = {
       }
     }
 #endif
   },
 
   helpInfo : "  -browser           Open a browser window.\n" +
              "  -new-window  <url> Open <url> in a new window.\n" +
              "  -new-tab     <url> Open <url> in a new tab.\n" +
+             "  -private-window <url> Open <url> in a new private window.\n" +
 #ifdef XP_WIN
              "  -preferences       Open Options dialog.\n" +
 #else
              "  -preferences       Open Preferences dialog.\n" +
 #endif
              "  -search     <term> Search <term> with your default search engine.\n",
 
   /* nsIBrowserHandler */
--- a/browser/devtools/commandline/test/browser_cmd_csscoverage_oneshot.js
+++ b/browser/devtools/commandline/test/browser_cmd_csscoverage_oneshot.js
@@ -18,17 +18,22 @@ let test = asyncTest(function*() {
   let options = yield helpers.openTab(PAGE_3);
   yield helpers.openToolbar(options);
 
   let usage = yield csscoverage.getUsage(options.target);
 
   yield navigate(usage, options);
   yield checkPages(usage);
   yield checkEditorReport(usage);
-  yield checkPageReport(usage);
+  // usage.createPageReport is not supported for usage.oneshot data as of
+  // bug 1035300 because the page report assumed we have preload data which
+  // oneshot can't gather. The ideal solution is to have a special no-preload
+  // mode for the page report, but since oneshot isn't needed for the UI to
+  // function, we're currently not supporting page report for oneshot data
+  // yield checkPageReport(usage);
 
   yield helpers.closeToolbar(options);
   yield helpers.closeTab(options);
 });
 
 /**
  * Just check current page
  */
--- a/browser/devtools/inspector/test/browser.ini
+++ b/browser/devtools/inspector/test/browser.ini
@@ -3,39 +3,41 @@ skip-if = e10s # Bug ?????? - devtools t
 subsuite = devtools
 support-files =
   doc_inspector_breadcrumbs.html
   doc_inspector_delete-selected-node-01.html
   doc_inspector_delete-selected-node-02.html
   doc_inspector_gcli-inspect-command.html
   doc_inspector_highlighter-comments.html
   doc_inspector_highlighter.html
-  doc_inspector_infobar.html
+  browser_inspector_infobar_01.html
+  browser_inspector_infobar_02.html
   doc_inspector_menu.html
   doc_inspector_remove-iframe-during-load.html
   doc_inspector_search.html
   doc_inspector_search-suggestions.html
   doc_inspector_select-last-selected-01.html
   doc_inspector_select-last-selected-02.html
+  browser_inspector_highlight_after_transition.html
   head.js
 
 [browser_inspector_breadcrumbs.js]
 [browser_inspector_delete-selected-node-01.js]
 [browser_inspector_delete-selected-node-02.js]
 [browser_inspector_delete-selected-node-03.js]
 [browser_inspector_destroy-after-navigation.js]
 [browser_inspector_gcli-inspect-command.js]
 [browser_inspector_highlighter-01.js]
 [browser_inspector_highlighter-02.js]
 [browser_inspector_highlighter-03.js]
 [browser_inspector_highlighter-comments.js]
 [browser_inspector_highlighter-iframes.js]
 [browser_inspector_iframe-navigation.js]
-[browser_inspector_infobar.js]
-skip-if = true # Bug 1028609
+[browser_inspector_infobar_01.js]
+[browser_inspector_infobar_02.js]
 [browser_inspector_initialization.js]
 [browser_inspector_inspect-object-element.js]
 [browser_inspector_invalidate.js]
 [browser_inspector_keyboard-shortcuts.js]
 [browser_inspector_menu.js]
 [browser_inspector_navigation.js]
 [browser_inspector_picker-stop-on-destroy.js]
 [browser_inspector_picker-stop-on-tool-change.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/inspector/test/browser_inspector_highlight_after_transition.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+
+  <style>
+    div {
+      opacity: 0;
+      height: 0;
+      background: red;
+      border-top: 1px solid #888;
+      transition-property: height, opacity;
+      transition-duration: 3000ms;
+      transition-timing-function: ease-in-out, ease-in-out, linear;
+    }
+
+    div[visible] {
+      opacity: 1;
+      height: 200px;
+    }
+  </style>
+</head>
+<body>
+  <div></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/inspector/test/browser_inspector_highlight_after_transition.js
@@ -0,0 +1,35 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/browser/devtools/inspector/" +
+                 "test/browser_inspector_highlight_after_transition.html";
+
+// Test that the nodeinfobar is never displayed above the top or below the
+// bottom of the content area.
+let test = asyncTest(function*() {
+  info("Loading the test document and opening the inspector");
+
+  yield addTab(TEST_URI);
+
+  let {inspector} = yield openInspector();
+
+  yield checkDivHeight(inspector);
+
+  gBrowser.removeCurrentTab();
+});
+
+function* checkDivHeight(inspector) {
+  let div = getNode("div");
+
+  div.setAttribute("visible", "true");
+
+  yield once(div, "transitionend");
+  yield selectAndHighlightNode(div, inspector);
+
+  let height = div.getBoundingClientRect().height;
+
+  is (height, 201, "div is the correct height");
+}
--- a/browser/devtools/inspector/test/browser_inspector_highlighter-02.js
+++ b/browser/devtools/inspector/test/browser_inspector_highlighter-02.js
@@ -10,35 +10,35 @@
 
 const TEST_URI = TEST_URL_ROOT + "doc_inspector_highlighter.html";
 
 let test = asyncTest(function*() {
   let { inspector } = yield openInspectorForURL(TEST_URI);
 
   info("Selecting the simple, non-transformed DIV");
   let div = getNode("#simple-div");
-  yield selectNode(div, inspector, "highlight");
+  yield selectAndHighlightNode(div, inspector);
 
   testSimpleDivHighlighted(div);
   yield zoomTo(2);
   testZoomedSimpleDivHighlighted(div);
   yield zoomTo(1);
 
   info("Selecting the rotated DIV");
   let rotated = getNode("#rotated-div");
   let onBoxModelUpdate = waitForBoxModelUpdate();
-  yield selectNode(rotated, inspector, "highlight");
+  yield selectAndHighlightNode(rotated, inspector);
   yield onBoxModelUpdate;
 
   testMouseOverRotatedHighlights(rotated);
 
   info("Selecting the zero width height DIV");
   let zeroWidthHeight = getNode("#widthHeightZero-div");
   let onBoxModelUpdate = waitForBoxModelUpdate();
-  yield selectNode(zeroWidthHeight, inspector, "highlight");
+  yield selectAndHighlightNode(zeroWidthHeight, inspector);
   yield onBoxModelUpdate;
 
   testMouseOverWidthHeightZeroDiv(zeroWidthHeight);
 
 });
 
 function testSimpleDivHighlighted(div) {
   ok(isHighlighting(), "The highlighter is shown");
new file mode 100644
--- /dev/null
+++ b/browser/devtools/inspector/test/browser_inspector_infobar_01.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+
+  <style>
+    body {
+      width: 100%;
+      height: 100%;
+    }
+    div {
+      position: absolute;
+      height: 100px;
+      width: 500px;
+    }
+
+    #bottom {
+      bottom: 0px;
+      background: blue;
+    }
+
+    #vertical {
+      height: 100%;
+      background: green;
+    }
+   </style>
+ </head>
+ <body>
+  <div id="vertical">Vertical</div>
+  <div id="top" class="class1 class2">Top</div>
+  <div id="bottom">Bottom</div>
+ </body>
+ </html>
rename from browser/devtools/inspector/test/browser_inspector_infobar.js
rename to browser/devtools/inspector/test/browser_inspector_infobar_01.js
--- a/browser/devtools/inspector/test/browser_inspector_infobar.js
+++ b/browser/devtools/inspector/test/browser_inspector_infobar_01.js
@@ -1,17 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const TEST_URI = "http://example.com/browser/browser/devtools/inspector/" +
-                 "test/doc_inspector_infobar.html";
-const DOORHANGER_ARROW_HEIGHT = 5;
+                 "test/browser_inspector_infobar_01.html";
 
 // Test that hovering over nodes in the markup-view shows the highlighter over
 // those nodes
 let test = asyncTest(function*() {
   info("Loading the test document and opening the inspector");
 
   yield addTab(TEST_URI);
 
@@ -46,43 +45,32 @@ let test = asyncTest(function*() {
     {
       node: doc.querySelector("body"),
       position: "bottom",
       tag: "BODY",
       id: "",
       classes: ""
       // No dims as they will vary between computers
     },
-    {
-      node: doc.querySelector("#farbottom"),
-      position: "top",
-      tag: "DIV",
-      id: "#farbottom",
-      classes: "",
-      dims: "500 x 100"
-    },
   ];
 
   for (let currTest of testData) {
     yield testPosition(currTest, inspector);
   }
 
-  yield checkInfoBarAboveTop(inspector);
-  yield checkInfoBarBelowFindbar(inspector);
-
   gBrowser.removeCurrentTab();
 });
 
 function* testPosition(currTest, inspector) {
   let browser = gBrowser.selectedBrowser;
   let stack = browser.parentNode;
 
   info("Testing " + currTest.id);
 
-  yield selectNode(currTest.node, inspector, "highlight");
+  yield selectAndHighlightNode(currTest.node, inspector);
 
   let container = stack.querySelector(".highlighter-nodeinfobar-positioner");
   is(container.getAttribute("position"),
     currTest.position, "node " + currTest.id + ": position matches.");
 
   let tagNameLabel = stack.querySelector(".highlighter-nodeinfobar-tagname");
   is(tagNameLabel.textContent, currTest.tag,
     "node " + currTest.id + ": tagName matches.");
@@ -96,55 +84,8 @@ function* testPosition(currTest, inspect
   is(classesBox.textContent, currTest.classes,
     "node " + currTest.id  + ": classes match.");
 
   if (currTest.dims) {
     let dimBox = stack.querySelector(".highlighter-nodeinfobar-dimensions");
     is(dimBox.textContent, currTest.dims, "node " + currTest.id  + ": dims match.");
   }
 }
-
-function* checkInfoBarAboveTop(inspector) {
-  yield selectNode("#abovetop", inspector);
-
-  let positioner = getPositioner();
-  let insideContent = parseInt(positioner.style.top, 10) >= -DOORHANGER_ARROW_HEIGHT;
-
-  ok(insideContent, "Infobar is inside the content window (top = " +
-                    parseInt(positioner.style.top, 10) + ", content = '" +
-                    positioner.textContent +"')");
-}
-
-function* checkInfoBarBelowFindbar(inspector) {
-  gFindBar.open();
-
-  let body = content.document.body;
-  let farBottom = body.querySelector("#farbottom");
-  farBottom.scrollIntoView();
-
-  // Wait for scrollIntoView
-  yield waitForTick();
-
-  body.scrollTop -= 130;
-  yield selectNode(farBottom, inspector);
-
-  let positioner = getPositioner();
-  let insideContent = parseInt(positioner.style.top, 10) >= -DOORHANGER_ARROW_HEIGHT;
-
-  ok(insideContent, "Infobar does not overlap the findbar (top = " +
-                    parseInt(positioner.style.top, 10) + ", content = '" +
-                    positioner.textContent +"')");
-
-  gFindBar.close();
-}
-
-function getPositioner() {
-  let browser = gBrowser.selectedBrowser;
-  let stack = browser.parentNode;
-
-  return stack.querySelector(".highlighter-nodeinfobar-positioner");
-}
-
-function waitForTick() {
-  let deferred = promise.defer();
-  executeSoon(deferred.resolve);
-  return deferred.promise;
-}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/inspector/test/browser_inspector_infobar_02.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+
+  <style>
+    body {
+      width: 100%;
+      height: 100%;
+    }
+
+    div {
+      position: absolute;
+      height: 100px;
+      width: 500px;
+    }
+
+    #below-bottom {
+      bottom: -200px;
+      background: red;
+    }
+
+    #above-top {
+      top: -200px;
+      background: black;
+      color: white;
+    }";
+  </style>
+</head>
+<body>
+  <div id="above-top">Above top</div>
+  <div id="below-bottom">Far bottom</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/inspector/test/browser_inspector_infobar_02.js
@@ -0,0 +1,65 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/browser/devtools/inspector/" +
+                 "test/browser_inspector_infobar_02.html";
+const DOORHANGER_ARROW_HEIGHT = 5;
+
+// Test that the nodeinfobar is never displayed above the top or below the
+// bottom of the content area.
+let test = asyncTest(function*() {
+  info("Loading the test document and opening the inspector");
+
+  yield addTab(TEST_URI);
+
+  let {inspector} = yield openInspector();
+
+  yield checkInfoBarAboveTop(inspector);
+  yield checkInfoBarBelowFindbar(inspector);
+
+  gBrowser.removeCurrentTab();
+});
+
+function* checkInfoBarAboveTop(inspector) {
+  yield selectAndHighlightNode("#above-top", inspector);
+
+  let positioner = getPositioner();
+  let positionerTop = parseInt(positioner.style.top, 10);
+  let insideContent = positionerTop >= -DOORHANGER_ARROW_HEIGHT;
+
+  ok(insideContent, "Infobar is inside the content window (top = " +
+                    positionerTop + ", content = '" +
+                    positioner.textContent +"')");
+}
+
+function* checkInfoBarBelowFindbar(inspector) {
+  gFindBar.open();
+
+  // Ensure that the findbar is fully open.
+  yield once(gFindBar, "transitionend");
+  yield selectAndHighlightNode("#below-bottom", inspector);
+
+  let positioner = getPositioner();
+  let positionerBottom =
+    positioner.getBoundingClientRect().bottom - DOORHANGER_ARROW_HEIGHT;
+  let findBarTop = gFindBar.getBoundingClientRect().top;
+
+  let insideContent = positionerBottom <= findBarTop;
+
+  ok(insideContent, "Infobar does not overlap the findbar (findBarTop = " +
+                    findBarTop + ", positionerBottom = " + positionerBottom +
+                    ", content = '" + positioner.textContent +"')");
+
+  gFindBar.close();
+  yield once(gFindBar, "transitionend");
+}
+
+function getPositioner() {
+  let browser = gBrowser.selectedBrowser;
+  let stack = browser.parentNode;
+
+  return stack.querySelector(".highlighter-nodeinfobar-positioner");
+}
--- a/browser/devtools/inspector/test/head.js
+++ b/browser/devtools/inspector/test/head.js
@@ -128,33 +128,51 @@ function getNode(nodeOrSelector, options
     return node;
   }
 
   info("Looking for a node but selector was not a string.");
   return nodeOrSelector;
 }
 
 /**
+ * Highlight a node and set the inspector's current selection to the node or
+ * the first match of the given css selector.
+ * @param {String|DOMNode} nodeOrSelector
+ * @param {InspectorPanel} inspector
+ *        The instance of InspectorPanel currently loaded in the toolbox
+ * @return a promise that resolves when the inspector is updated with the new
+ * node
+ */
+function selectAndHighlightNode(nodeOrSelector, inspector) {
+  info("Highlighting and selecting the node " + nodeOrSelector);
+
+  let node = getNode(nodeOrSelector);
+  let updated = inspector.toolbox.once("highlighter-ready");
+  inspector.selection.setNode(node, "test-highlight");
+  return updated;
+
+}
+
+/**
  * Set the inspector's current selection to a node or to the first match of the
- * given css selector
- * @param {InspectorPanel} inspector The instance of InspectorPanel currently
- * loaded in the toolbox
- * @param {String} reason Defaults to "test" which instructs the inspector not
- * to highlight the node upon selection
- * @param {String} reason Defaults to "test" which instructs the inspector not
- * to highlight the node upon selection
+ * given css selector.
+ * @param {String|DOMNode} nodeOrSelector
+ * @param {InspectorPanel} inspector
+ *        The instance of InspectorPanel currently loaded in the toolbox
+ * @param {String} reason
+ *        Defaults to "test" which instructs the inspector not to highlight the
+ *        node upon selection
  * @return a promise that resolves when the inspector is updated with the new
  * node
  */
 function selectNode(nodeOrSelector, inspector, reason="test") {
   info("Selecting the node " + nodeOrSelector);
+
   let node = getNode(nodeOrSelector);
-  let updated = inspector.once("inspector-updated", () => {
-    is(inspector.selection.node, node, "Correct node was selected");
-  });
+  let updated = inspector.once("inspector-updated");
   inspector.selection.setNode(node, reason);
   return updated;
 }
 
 /**
  * Open the inspector in a tab with given URL.
  * @param {string} url  The URL to open.
  * @return A promise that is resolved once the tab and inspector have loaded
--- a/browser/devtools/layoutview/test/head.js
+++ b/browser/devtools/layoutview/test/head.js
@@ -97,29 +97,49 @@ let destroyToolbox = Task.async(function
  */
 function getNode(nodeOrSelector) {
   return typeof nodeOrSelector === "string" ?
     content.document.querySelector(nodeOrSelector) :
     nodeOrSelector;
 }
 
 /**
+ * Highlight a node and set the inspector's current selection to the node or
+ * the first match of the given css selector.
+ * @param {String|DOMNode} nodeOrSelector
+ * @param {InspectorPanel} inspector
+ *        The instance of InspectorPanel currently loaded in the toolbox
+ * @return a promise that resolves when the inspector is updated with the new
+ * node
+ */
+function selectAndHighlightNode(nodeOrSelector, inspector) {
+  info("Highlighting and selecting the node " + nodeOrSelector);
+
+  let node = getNode(nodeOrSelector);
+  let updated = inspector.toolbox.once("highlighter-ready");
+  inspector.selection.setNode(node, "test-highlight");
+  return updated;
+
+}
+
+/**
  * Set the inspector's current selection to a node or to the first match of the
- * given css selector
- * @param {InspectorPanel} inspector The instance of InspectorPanel currently
- * loaded in the toolbox
- * @param {String} reason Defaults to "test" which instructs the inspector not
- * to highlight the node upon selection
- * @param {String} reason Defaults to "test" which instructs the inspector not
- * to highlight the node upon selection
+ * given css selector.
+ * @param {String|DOMNode} nodeOrSelector
+ * @param {InspectorPanel} inspector
+ *        The instance of InspectorPanel currently loaded in the toolbox
+ * @param {String} reason
+ *        Defaults to "test" which instructs the inspector not to highlight the
+ *        node upon selection
  * @return a promise that resolves when the inspector is updated with the new
  * node
  */
 function selectNode(nodeOrSelector, inspector, reason="test") {
   info("Selecting the node " + nodeOrSelector);
+
   let node = getNode(nodeOrSelector);
   let updated = inspector.once("inspector-updated");
   inspector.selection.setNode(node, reason);
   return updated;
 }
 
 /**
  * Open the toolbox, with the inspector tool visible.
--- a/browser/devtools/markupview/test/head.js
+++ b/browser/devtools/markupview/test/head.js
@@ -129,26 +129,49 @@ function openInspector() {
 function getNode(nodeOrSelector) {
   info("Getting the node for '" + nodeOrSelector + "'");
   return typeof nodeOrSelector === "string" ?
     content.document.querySelector(nodeOrSelector) :
     nodeOrSelector;
 }
 
 /**
- * Set the inspector's current selection to a node or to the first match of the
- * given css selector
+ * Highlight a node and set the inspector's current selection to the node or
+ * the first match of the given css selector.
  * @param {String|DOMNode} nodeOrSelector
- * @param {InspectorPanel} inspector The instance of InspectorPanel currently loaded in the toolbox
- * @param {String} reason Defaults to "test" which instructs the inspector not to highlight the node upon selection
+ * @param {InspectorPanel} inspector
+ *        The instance of InspectorPanel currently loaded in the toolbox
+ * @return a promise that resolves when the inspector is updated with the new
+ * node
+ */
+function selectAndHighlightNode(nodeOrSelector, inspector) {
+  info("Highlighting and selecting the node " + nodeOrSelector);
+
+  let node = getNode(nodeOrSelector);
+  let updated = inspector.toolbox.once("highlighter-ready");
+  inspector.selection.setNode(node, "test-highlight");
+  return updated;
+
+}
+
+/**
+ * Set the inspector's current selection to a node or to the first match of the
+ * given css selector.
+ * @param {String|DOMNode} nodeOrSelector
+ * @param {InspectorPanel} inspector
+ *        The instance of InspectorPanel currently loaded in the toolbox
+ * @param {String} reason
+ *        Defaults to "test" which instructs the inspector not to highlight the
+ *        node upon selection
  * @return a promise that resolves when the inspector is updated with the new
  * node
  */
 function selectNode(nodeOrSelector, inspector, reason="test") {
-  info("Selecting the node for '" + nodeOrSelector + "'");
+  info("Selecting the node " + nodeOrSelector);
+
   let node = getNode(nodeOrSelector);
   let updated = inspector.once("inspector-updated");
   inspector.selection.setNode(node, reason);
   return updated;
 }
 
 /**
  * Get the MarkupContainer object instance that corresponds to the given
--- a/browser/devtools/profiler/test/browser_profiler_remote.js
+++ b/browser/devtools/profiler/test/browser_profiler_remote.js
@@ -17,20 +17,20 @@ let ProfilerController = temp.ProfilerCo
 
 function test() {
   waitForExplicitFinish();
   Services.prefs.setBoolPref(REMOTE_ENABLED, true);
 
   loadTab(URL, function onTabLoad(tab, browser) {
     DebuggerServer.init(function () true);
     DebuggerServer.addBrowserActors();
-    is(DebuggerServer._socketConnections, 0);
+    is(DebuggerServer.listeningSockets, 0);
 
     DebuggerServer.openListener(2929);
-    is(DebuggerServer._socketConnections, 1);
+    is(DebuggerServer.listeningSockets, 1);
 
     let transport = debuggerSocketConnect("127.0.0.1", 2929);
     let client = new DebuggerClient(transport);
     client.connect(function onClientConnect() {
       let target = { isRemote: true, client: client };
       let controller = new ProfilerController(target);
 
       controller.connect(function onControllerConnect() {
@@ -47,9 +47,9 @@ function test() {
         window.addEventListener("Debugger:Shutdown", onShutdown, true);
 
         client.close(function () {
           gBrowser.removeTab(tab);
         });
       });
     });
   });
-}
\ No newline at end of file
+}
--- a/browser/devtools/styleinspector/test/head.js
+++ b/browser/devtools/styleinspector/test/head.js
@@ -124,28 +124,49 @@ function addTab(url) {
  */
 function getNode(nodeOrSelector) {
   return typeof nodeOrSelector === "string" ?
     content.document.querySelector(nodeOrSelector) :
     nodeOrSelector;
 }
 
 /**
+ * Highlight a node and set the inspector's current selection to the node or
+ * the first match of the given css selector.
+ * @param {String|DOMNode} nodeOrSelector
+ * @param {InspectorPanel} inspector
+ *        The instance of InspectorPanel currently loaded in the toolbox
+ * @return a promise that resolves when the inspector is updated with the new
+ * node
+ */
+function selectAndHighlightNode(nodeOrSelector, inspector) {
+  info("Highlighting and selecting the node " + nodeOrSelector);
+
+  let node = getNode(nodeOrSelector);
+  let updated = inspector.toolbox.once("highlighter-ready");
+  inspector.selection.setNode(node, "test-highlight");
+  return updated;
+
+}
+
+/**
  * Set the inspector's current selection to a node or to the first match of the
- * given css selector
- * @param {InspectorPanel} inspector The instance of InspectorPanel currently
- * loaded in the toolbox
- * @param {String} reason Defaults to "test" which instructs the inspector not
- * to highlight the node upon selection
- * @param {String} reason Defaults to "test" which instructs the inspector not to highlight the node upon selection
+ * given css selector.
+ * @param {String|DOMNode} nodeOrSelector
+ * @param {InspectorPanel} inspector
+ *        The instance of InspectorPanel currently loaded in the toolbox
+ * @param {String} reason
+ *        Defaults to "test" which instructs the inspector not to highlight the
+ *        node upon selection
  * @return a promise that resolves when the inspector is updated with the new
  * node
  */
 function selectNode(nodeOrSelector, inspector, reason="test") {
   info("Selecting the node " + nodeOrSelector);
+
   let node = getNode(nodeOrSelector);
   let updated = inspector.once("inspector-updated");
   inspector.selection.setNode(node, reason);
   return updated;
 }
 
 /**
  * Set the inspector's current selection to null so that no node is selected
--- a/mobile/android/base/tabspanel/TabsPanel.java
+++ b/mobile/android/base/tabspanel/TabsPanel.java
@@ -174,16 +174,20 @@ public class TabsPanel extends LinearLay
             @Override
             public void onClick(View view) {
                 showMenu();
             }
         });
     }
 
     public void showMenu() {
+        if (mCurrentPanel == Panel.REMOTE_TABS) {
+            return;
+        }
+
         final Menu menu = mPopupMenu.getMenu();
 
         // Each panel has a "+" shortcut button, so don't show it for that panel.
         menu.findItem(R.id.new_tab).setVisible(mCurrentPanel != Panel.NORMAL_TABS);
         menu.findItem(R.id.new_private_tab).setVisible(mCurrentPanel != Panel.PRIVATE_TABS);
 
         // Only show "Clear * tabs" for current panel.
         menu.findItem(R.id.close_all_tabs).setVisible(mCurrentPanel == Panel.NORMAL_TABS);
--- a/mobile/android/base/tests/testDebuggerServer.js
+++ b/mobile/android/base/tests/testDebuggerServer.js
@@ -17,14 +17,14 @@ add_test(function() {
   // Enable the debugger via the pref it listens for
   do_register_cleanup(function() {
     Services.prefs.clearUserPref(DEBUGGER_REMOTE_ENABLED);
   });
   Services.prefs.setBoolPref(DEBUGGER_REMOTE_ENABLED, true);
 
   let DebuggerServer = window.DebuggerServer;
   do_check_true(DebuggerServer.initialized);
-  do_check_true(!!DebuggerServer._listener);
+  do_check_eq(DebuggerServer.listeningSockets, 1);
 
   run_next_test();
 });
 
 run_next_test();
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -1518,18 +1518,19 @@ var BrowserApp = {
         break;
       }
 
       case "Tab:Load": {
         let data = JSON.parse(aData);
         let url = data.url;
         let flags;
 
-        if (/^[0-9]+$/.test(url)) {
-          // If the query is a number, force a search (see bug 993705; workaround for bug 693808).
+        if (!data.engine && /^[0-9]+$/.test(url)) {
+          // If the query is a number and we're not using a search engine,
+          // force a search (see bug 993705; workaround for bug 693808).
           url = URIFixup.keywordToURI(url).spec;
         } else {
           flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP |
                    Ci.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
         }
 
         // Pass LOAD_FLAGS_DISALLOW_INHERIT_OWNER to prevent any loads from
         // inheriting the currently loaded document's principal.
@@ -3158,16 +3159,17 @@ Tab.prototype = {
                 Ci.nsIWebProgress.NOTIFY_LOCATION |
                 Ci.nsIWebProgress.NOTIFY_SECURITY;
     this.browser.addProgressListener(this, flags);
     this.browser.sessionHistory.addSHistoryListener(this);
 
     this.browser.addEventListener("DOMContentLoaded", this, true);
     this.browser.addEventListener("DOMFormHasPassword", this, true);
     this.browser.addEventListener("DOMLinkAdded", this, true);
+    this.browser.addEventListener("DOMLinkChanged", this, true);
     this.browser.addEventListener("DOMTitleChanged", this, true);
     this.browser.addEventListener("DOMWindowClose", this, true);
     this.browser.addEventListener("DOMWillOpenModalDialog", this, true);
     this.browser.addEventListener("DOMAutoComplete", this, true);
     this.browser.addEventListener("blur", this, true);
     this.browser.addEventListener("scroll", this, true);
     this.browser.addEventListener("MozScrolledAreaChanged", this, true);
     this.browser.addEventListener("pageshow", this, true);
@@ -3330,16 +3332,17 @@ Tab.prototype = {
     this.browser.contentWindow.controllers.removeController(this.overscrollController);
 
     this.browser.removeProgressListener(this);
     this.browser.sessionHistory.removeSHistoryListener(this);
 
     this.browser.removeEventListener("DOMContentLoaded", this, true);
     this.browser.removeEventListener("DOMFormHasPassword", this, true);
     this.browser.removeEventListener("DOMLinkAdded", this, true);
+    this.browser.removeEventListener("DOMLinkChanged", this, true);
     this.browser.removeEventListener("DOMTitleChanged", this, true);
     this.browser.removeEventListener("DOMWindowClose", this, true);
     this.browser.removeEventListener("DOMWillOpenModalDialog", this, true);
     this.browser.removeEventListener("DOMAutoComplete", this, true);
     this.browser.removeEventListener("blur", this, true);
     this.browser.removeEventListener("scroll", this, true);
     this.browser.removeEventListener("MozScrolledAreaChanged", this, true);
     this.browser.removeEventListener("pageshow", this, true);
@@ -3743,17 +3746,18 @@ Tab.prototype = {
         break;
       }
 
       case "DOMFormHasPassword": {
         LoginManagerContent.onFormPassword(aEvent);
         break;
       }
 
-      case "DOMLinkAdded": {
+      case "DOMLinkAdded":
+      case "DOMLinkChanged": {
         let target = aEvent.originalTarget;
         if (!target.href || target.disabled)
           return;
 
         // Ignore on frames and other documents
         if (target.ownerDocument != this.browser.contentDocument)
           return;
 
@@ -3792,17 +3796,17 @@ Tab.prototype = {
 
           let json = {
             type: "Link:Favicon",
             tabID: this.id,
             href: resolveGeckoURI(target.href),
             size: maxSize
           };
           sendMessageToJava(json);
-        } else if (list.indexOf("[alternate]") != -1) {
+        } else if (list.indexOf("[alternate]") != -1 && aEvent.type == "DOMLinkAdded") {
           let type = target.type.toLowerCase().replace(/^\s+|\s*(?:;.*)?$/g, "");
           let isFeed = (type == "application/rss+xml" || type == "application/atom+xml");
 
           if (!isFeed)
             return;
 
           try {
             // urlSecurityCeck will throw if things are not OK
@@ -3813,17 +3817,17 @@ Tab.prototype = {
             this.browser.feeds.push({ href: target.href, title: target.title, type: type });
 
             let json = {
               type: "Link:Feed",
               tabID: this.id
             };
             sendMessageToJava(json);
           } catch (e) {}
-        } else if (list.indexOf("[search]" != -1)) {
+        } else if (list.indexOf("[search]" != -1) && aEvent.type == "DOMLinkAdded") {
           let type = target.type && target.type.toLowerCase();
 
           // Replace all starting or trailing spaces or spaces before "*;" globally w/ "".
           type = type.replace(/^\s+|\s*(?:;.*)?$/g, "");
 
           // Check that type matches opensearch.
           let isOpenSearch = (type == "application/opensearchdescription+xml");
           if (isOpenSearch && target.title && /^(?:https?|ftp):/i.test(target.href)) {
@@ -7210,17 +7214,17 @@ var RemoteDebugger = {
       DebuggerServer.openListener(port);
       dump("Remote debugger listening on port " + port);
     } catch(e) {
       dump("Remote debugger didn't start: " + e);
     }
   },
 
   _stop: function rd_start() {
-    DebuggerServer.closeListener();
+    DebuggerServer.closeAllListeners();
     dump("Remote debugger stopped");
   }
 };
 
 var Telemetry = {
   addData: function addData(aHistogramId, aValue) {
     let histogram = Services.telemetry.getHistogramById(aHistogramId);
     histogram.add(aValue);
--- a/mobile/android/search/java/org/mozilla/search/Constants.java
+++ b/mobile/android/search/java/org/mozilla/search/Constants.java
@@ -10,14 +10,15 @@
 
 package org.mozilla.search;
 
 /**
  * Key should not be stored here. For more info on storing keys, see
  * https://github.com/ericedens/FirefoxSearch/issues/3
  */
 public class Constants {
-    public static final String AUTO_COMPLETE_FRAGMENT = "org.mozilla.search.AUTO_COMPLETE_FRAGMENT";
-    public static final String CARD_STREAM_FRAGMENT = "org.mozilla.search.CARD_STREAM_FRAGMENT";
-    public static final String GECKO_VIEW_FRAGMENT = "org.mozilla.search.GECKO_VIEW_FRAGMENT";
+
+    public static final String POSTSEARCH_FRAGMENT = "org.mozilla.search.POSTSEARCH_FRAGMENT";
+    public static final String PRESEARCH_FRAGMENT = "org.mozilla.search.PRESEARCH_FRAGMENT";
+    public static final String SEARCH_FRAGMENT = "org.mozilla.search.SEARCH_FRAGMENT";
 
     public static final String AUTOCOMPLETE_ROW_LIMIT = "5";
 }
--- a/mobile/android/search/java/org/mozilla/search/MainActivity.java
+++ b/mobile/android/search/java/org/mozilla/search/MainActivity.java
@@ -2,102 +2,69 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.search;
 
 import android.net.Uri;
 import android.os.Bundle;
 import android.support.v4.app.FragmentActivity;
-import android.support.v4.app.FragmentManager;
-import android.support.v4.app.FragmentTransaction;
+import android.view.View;
 
 import org.mozilla.search.autocomplete.AcceptsSearchQuery;
-import org.mozilla.search.autocomplete.AutoCompleteFragment;
-import org.mozilla.search.stream.CardStreamFragment;
-
 
 /**
  * The main entrance for the Android search intent.
  * <p/>
  * State management is delegated to child fragments. Fragments communicate
  * with each other by passing messages through this activity. The only message passing right
  * now, the only message passing occurs when a user wants to submit a search query. That
  * passes through the onSearch method here.
  */
-public class MainActivity extends FragmentActivity implements AcceptsSearchQuery,
-        FragmentManager.OnBackStackChangedListener {
+public class MainActivity extends FragmentActivity implements AcceptsSearchQuery {
 
-    private DetailActivity detailActivity;
+    enum State {
+        START,
+        PRESEARCH,
+        POSTSEARCH
+    }
+
+    private State state = State.START;
 
     @Override
     protected void onCreate(Bundle stateBundle) {
         super.onCreate(stateBundle);
-
-        // Sets the content view for the Activity
         setContentView(R.layout.search_activity_main);
+        startPresearch();
+    }
 
-        // Gets an instance of the support library FragmentManager
-        FragmentManager localFragmentManager = getSupportFragmentManager();
-
-        // If the incoming state of the Activity is null, sets the initial view to be thumbnails
-        if (null == stateBundle) {
+    @Override
+    public void onSearch(String s) {
+        startPostsearch();
+        ((PostSearchFragment) getSupportFragmentManager().findFragmentById(R.id.gecko))
+                .setUrl("https://search.yahoo.com/search?p=" + Uri.encode(s));
+    }
 
-            // Starts a Fragment transaction to track the stack
-            FragmentTransaction localFragmentTransaction = localFragmentManager.beginTransaction();
-
-            localFragmentTransaction.add(R.id.header_fragments, new AutoCompleteFragment(),
-                    Constants.AUTO_COMPLETE_FRAGMENT);
+    private void startPresearch() {
+        if (state != State.PRESEARCH) {
+            state = State.PRESEARCH;
+            findViewById(R.id.gecko).setVisibility(View.INVISIBLE);
+            findViewById(R.id.presearch).setVisibility(View.VISIBLE);
+        }
+    }
 
-            localFragmentTransaction.add(R.id.presearch_fragments, new CardStreamFragment(),
-                    Constants.CARD_STREAM_FRAGMENT);
-
-            // Commits this transaction to display the Fragment
-            localFragmentTransaction.commit();
-
-            // The incoming state of the Activity isn't null.
+    private void startPostsearch() {
+        if (state != State.POSTSEARCH) {
+            state = State.POSTSEARCH;
+            findViewById(R.id.presearch).setVisibility(View.INVISIBLE);
+            findViewById(R.id.gecko).setVisibility(View.VISIBLE);
         }
     }
 
     @Override
-    protected void onStart() {
-        super.onStart();
-
-
-        if (null == detailActivity) {
-            detailActivity = new DetailActivity();
-        }
-
-        if (null == getSupportFragmentManager().findFragmentByTag(Constants.GECKO_VIEW_FRAGMENT)) {
-            FragmentTransaction txn = getSupportFragmentManager().beginTransaction();
-            txn.add(R.id.gecko_fragments, detailActivity, Constants.GECKO_VIEW_FRAGMENT);
-            txn.hide(detailActivity);
-
-            txn.commit();
+    public void onBackPressed() {
+        if (state == State.POSTSEARCH) {
+            startPresearch();
+        } else {
+            super.onBackPressed();
         }
     }
-
-    @Override
-    public void onSearch(String s) {
-        FragmentManager localFragmentManager = getSupportFragmentManager();
-        FragmentTransaction localFragmentTransaction = localFragmentManager.beginTransaction();
-
-        localFragmentTransaction
-                .hide(localFragmentManager.findFragmentByTag(Constants.CARD_STREAM_FRAGMENT))
-                .addToBackStack(null);
-
-        localFragmentTransaction
-                .show(localFragmentManager.findFragmentByTag(Constants.GECKO_VIEW_FRAGMENT))
-                .addToBackStack(null);
-
-        localFragmentTransaction.commit();
-
-
-        ((DetailActivity) getSupportFragmentManager()
-                .findFragmentByTag(Constants.GECKO_VIEW_FRAGMENT))
-                .setUrl("https://search.yahoo.com/search?p=" + Uri.encode(s));
-    }
-
-    @Override
-    public void onBackStackChanged() {
-
-    }
 }
rename from mobile/android/search/java/org/mozilla/search/DetailActivity.java
rename to mobile/android/search/java/org/mozilla/search/PostSearchFragment.java
--- a/mobile/android/search/java/org/mozilla/search/DetailActivity.java
+++ b/mobile/android/search/java/org/mozilla/search/PostSearchFragment.java
@@ -13,25 +13,25 @@ import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 
 import org.mozilla.gecko.GeckoView;
 import org.mozilla.gecko.GeckoViewChrome;
 import org.mozilla.gecko.GeckoViewContent;
 import org.mozilla.gecko.PrefsHelper;
 
-public class DetailActivity extends Fragment {
+public class PostSearchFragment extends Fragment {
 
-    private static final String LOGTAG = "DetailActivity";
+    private static final String LOGTAG = "PostSearchFragment";
     private GeckoView geckoView;
 
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
+                             Bundle savedInstanceState) {
         View mainView = inflater.inflate(R.layout.search_activity_detail, container, false);
 
 
         geckoView = (GeckoView) mainView.findViewById(R.id.gecko_view);
 
         geckoView.setChromeDelegate(new MyGeckoViewChrome());
         geckoView.setContentDelegate(new SearchGeckoView());
 
rename from mobile/android/search/java/org/mozilla/search/stream/CardStreamFragment.java
rename to mobile/android/search/java/org/mozilla/search/PreSearchFragment.java
--- a/mobile/android/search/java/org/mozilla/search/stream/CardStreamFragment.java
+++ b/mobile/android/search/java/org/mozilla/search/PreSearchFragment.java
@@ -1,36 +1,36 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-package org.mozilla.search.stream;
+package org.mozilla.search;
 
 import android.os.Bundle;
 import android.support.v4.app.ListFragment;
 import android.view.View;
 import android.widget.ArrayAdapter;
 
-import org.mozilla.search.R;
+import org.mozilla.search.stream.PreloadAgent;
 
 
 /**
  * This fragment is responsible for managing the card stream. Right now
  * we only use this during pre-search, but we could also use it
  * during post-search at some point.
  */
-public class CardStreamFragment extends ListFragment {
+public class PreSearchFragment extends ListFragment {
 
     private ArrayAdapter<PreloadAgent.TmpItem> adapter;
 
     /**
      * Mandatory empty constructor for the fragment manager to instantiate the
      * fragment (e.g. upon screen orientation changes).
      */
-    public CardStreamFragment() {
+    public PreSearchFragment() {
     }
 
     @Override
     public void onViewCreated(View view, Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
         getListView().setDivider(null);
     }
 
rename from mobile/android/search/java/org/mozilla/search/autocomplete/AutoCompleteFragment.java
rename to mobile/android/search/java/org/mozilla/search/autocomplete/SearchFragment.java
--- a/mobile/android/search/java/org/mozilla/search/autocomplete/AutoCompleteFragment.java
+++ b/mobile/android/search/java/org/mozilla/search/autocomplete/SearchFragment.java
@@ -28,40 +28,40 @@ import org.mozilla.search.R;
 
 /**
  * A fragment to handle autocomplete. Its interface with the outside
  * world should be very very limited.
  * <p/>
  * TODO: Add clear button to search input
  * TODO: Add more search providers (other than the dictionary)
  */
-public class AutoCompleteFragment extends Fragment implements AdapterView.OnItemClickListener,
+public class SearchFragment extends Fragment implements AdapterView.OnItemClickListener,
         TextView.OnEditorActionListener, AcceptsJumpTaps {
 
     private View mainView;
     private FrameLayout backdropFrame;
     private EditText searchBar;
     private ListView suggestionDropdown;
     private InputMethodManager inputMethodManager;
     private AutoCompleteAdapter autoCompleteAdapter;
     private AutoCompleteAgentManager autoCompleteAgentManager;
     private State state;
 
     private enum State {
         WAITING,  // The user is doing something else in the app.
         RUNNING   // The user is in search mode.
     }
 
-    public AutoCompleteFragment() {
+    public SearchFragment() {
         // Required empty public constructor
     }
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
+                             Bundle savedInstanceState) {
 
 
         mainView = inflater.inflate(R.layout.search_auto_complete, container, false);
         backdropFrame = (FrameLayout) mainView.findViewById(R.id.auto_complete_backdrop);
         searchBar = (EditText) mainView.findViewById(R.id.auto_complete_search_bar);
         suggestionDropdown = (ListView) mainView.findViewById(R.id.auto_complete_dropdown);
 
         inputMethodManager =
--- a/mobile/android/search/java/org/mozilla/search/stream/PreloadAgent.java
+++ b/mobile/android/search/java/org/mozilla/search/stream/PreloadAgent.java
@@ -7,20 +7,20 @@ package org.mozilla.search.stream;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
 /**
  * A temporary agent for loading cards into the pre-load card stream.
  * <p/>
- * When we have more agents, we'll want to put an agent manager between the CardStreamFragment
+ * When we have more agents, we'll want to put an agent manager between the PreSearchFragment
  * and the set of all agents. See autocomplete.AutoCompleteFragmentManager.
  */
-class PreloadAgent {
+public class PreloadAgent {
 
     public static final List<TmpItem> ITEMS = new ArrayList<TmpItem>();
 
     private static final Map<String, TmpItem> ITEM_MAP = new HashMap<String, TmpItem>();
 
     static {
         addItem(new TmpItem("1", "Pre-load item1"));
         addItem(new TmpItem("2", "Pre-load item2"));
--- a/mobile/android/search/res/layout/search_activity_detail.xml
+++ b/mobile/android/search/res/layout/search_activity_detail.xml
@@ -5,16 +5,16 @@
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                 xmlns:tools="http://schemas.android.com/tools"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
                 android:paddingBottom="@dimen/activity_vertical_margin"
                 android:paddingLeft="@dimen/activity_horizontal_margin"
                 android:paddingRight="@dimen/activity_horizontal_margin"
                 android:paddingTop="@dimen/activity_vertical_margin"
-                tools:context="org.mozilla.search.DetailActivity">
+                tools:context="org.mozilla.search.PostSearchFragment">
 
     <org.mozilla.gecko.GeckoView
         android:id="@+id/gecko_view"
         android:layout_width="fill_parent"
         android:layout_height="fill_parent"/>
 
 </RelativeLayout>
--- a/mobile/android/search/res/layout/search_activity_main.xml
+++ b/mobile/android/search/res/layout/search_activity_main.xml
@@ -5,31 +5,30 @@
 <merge
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical"
     tools:context=".MainActivity">
 
-
-    <FrameLayout
-        android:id="@+id/gecko_fragments"
+    <fragment
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:layout_marginTop="45dp"
+        android:name="org.mozilla.search.PostSearchFragment"
+        android:id="@+id/gecko"
         />
 
-
-    <FrameLayout
-        android:id="@+id/presearch_fragments"
+    <fragment
         android:layout_width="match_parent"
         android:layout_height="match_parent"
+        android:name="org.mozilla.search.PreSearchFragment"
+        android:id="@+id/presearch"
         />
 
-    <FrameLayout
-
-        android:id="@+id/header_fragments"
+    <fragment
         android:layout_width="match_parent"
         android:layout_height="match_parent"
+        android:name="org.mozilla.search.autocomplete.SearchFragment"
+        android:id="@+id/header_fragments"
         />
 
-</merge>
\ No newline at end of file
+</merge>
--- a/mobile/android/search/search_activity_sources.mozbuild
+++ b/mobile/android/search/search_activity_sources.mozbuild
@@ -4,18 +4,18 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 search_activity_sources = [
     'java/org/mozilla/search/autocomplete/AcceptsJumpTaps.java',
     'java/org/mozilla/search/autocomplete/AcceptsSearchQuery.java',
     'java/org/mozilla/search/autocomplete/AutoCompleteAdapter.java',
     'java/org/mozilla/search/autocomplete/AutoCompleteAgentManager.java',
-    'java/org/mozilla/search/autocomplete/AutoCompleteFragment.java',
     'java/org/mozilla/search/autocomplete/AutoCompleteModel.java',
     'java/org/mozilla/search/autocomplete/AutoCompleteRowView.java',
     'java/org/mozilla/search/autocomplete/AutoCompleteWordListAgent.java',
+    'java/org/mozilla/search/autocomplete/SearchFragment.java',
     'java/org/mozilla/search/Constants.java',
-    'java/org/mozilla/search/DetailActivity.java',
     'java/org/mozilla/search/MainActivity.java',
-    'java/org/mozilla/search/stream/CardStreamFragment.java',
+    'java/org/mozilla/search/PostSearchFragment.java',
+    'java/org/mozilla/search/PreSearchFragment.java',
     'java/org/mozilla/search/stream/PreloadAgent.java',
 ]
--- a/netwerk/dns/nsIDNSListener.idl
+++ b/netwerk/dns/nsIDNSListener.idl
@@ -5,17 +5,17 @@
 #include "nsISupports.idl"
 
 interface nsICancelable;
 interface nsIDNSRecord;
 
 /**
  * nsIDNSListener
  */
-[scriptable, uuid(41466a9f-f027-487d-a96c-af39e629b8d2)]
+[scriptable, function, uuid(27d49bfe-280c-49e0-bbaa-f6200c232c3d)]
 interface nsIDNSListener : nsISupports
 {
     /**
      * called when an asynchronous host lookup completes.
      *
      * @param aRequest
      *        the value returned from asyncResolve.
      * @param aRecord
--- a/toolkit/devtools/gcli/commands/csscoverage.js
+++ b/toolkit/devtools/gcli/commands/csscoverage.js
@@ -25,23 +25,31 @@ exports.items = [
     name: "csscoverage",
     hidden: true,
     description: l10n.lookup("csscoverageDesc"),
   },
   {
     name: "csscoverage start",
     hidden: true,
     description: l10n.lookup("csscoverageStartDesc2"),
+    params: [
+      {
+        name: "noreload",
+        type: "boolean",
+        description: l10n.lookup("csscoverageStartNoReloadDesc"),
+        manual: l10n.lookup("csscoverageStartNoReloadManual")
+      }
+    ],
     exec: function*(args, context) {
       let usage = yield csscoverage.getUsage(context.environment.target);
       if (usage == null) {
         throw new Error(l10n.lookup("csscoverageNoRemoteError"));
       }
       yield usage.start(context.environment.chromeWindow,
-                        context.environment.target);
+                        context.environment.target, args.noreload);
     }
   },
   {
     name: "csscoverage stop",
     hidden: true,
     description: l10n.lookup("csscoverageStopDesc2"),
     exec: function*(args, context) {
       let target = context.environment.target;
--- a/toolkit/devtools/server/actors/csscoverage.js
+++ b/toolkit/devtools/server/actors/csscoverage.js
@@ -103,22 +103,27 @@ let CSSUsageActor = protocol.ActorClass(
     delete this._onTabLoad;
     delete this._onChange;
 
     protocol.Actor.prototype.destroy.call(this);
   },
 
   /**
    * Begin recording usage data
+   * @param noreload It's best if we start by reloading the current page
+   * because that starts the test at a known point, but there could be reasons
+   * why we don't want to do that (e.g. the page contains state that will be
+   * lost across a reload)
    */
-  start: method(function() {
+  start: method(function(noreload) {
     if (this._running) {
       throw new Error(l10n.lookup("csscoverageRunningError"));
     }
 
+    this._isOneShot = false;
     this._visitedPages = new Set();
     this._knownRules = new Map();
     this._running = true;
     this._tooManyUnused = false;
 
     this._progressListener = {
       QueryInterface: XPCOMUtils.generateQI([ Ci.nsIWebProgressListener,
                                               Ci.nsISupportsWeakReference ]),
@@ -138,20 +143,28 @@ let CSSUsageActor = protocol.ActorClass(
       onStatusChange: () => {},
       destroy: () => {}
     };
 
     this._progress = this._tabActor.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                                             .getInterface(Ci.nsIWebProgress);
     this._progress.addProgressListener(this._progressListener, this._notifyOn);
 
-    this._populateKnownRules(this._tabActor.window.document);
-    this._updateUsage(this._tabActor.window.document, false);
+    if (noreload) {
+      // If we're not starting by reloading the page, then pretend that onload
+      // has just happened.
+      this._onTabLoad(this._tabActor.window.document);
+    }
+    else {
+      this._tabActor.window.location.reload();
+    }
 
     events.emit(this, "state-change", { isRunning: true });
+  }, {
+    request: { url: Arg(0, "boolean") }
   }),
 
   /**
    * Cease recording usage data
    */
   stop: method(function() {
     if (!this._running) {
       throw new Error(l10n.lookup("csscoverageNotRunningError"));
@@ -175,16 +188,17 @@ let CSSUsageActor = protocol.ActorClass(
    * Running start() quickly followed by stop() does a bunch of unnecessary
    * work, so this cuts all that out
    */
   oneshot: method(function() {
     if (this._running) {
       throw new Error(l10n.lookup("csscoverageRunningError"));
     }
 
+    this._isOneShot = true;
     this._visitedPages = new Set();
     this._knownRules = new Map();
 
     this._populateKnownRules(this._tabActor.window.document);
     this._updateUsage(this._tabActor.window.document, false);
   }),
 
   /**
@@ -387,16 +401,20 @@ let CSSUsageActor = protocol.ActorClass(
     if (this._running) {
       throw new Error(l10n.lookup("csscoverageRunningError"));
     }
 
     if (this._visitedPages == null) {
       throw new Error(l10n.lookup("csscoverageNotRunError"));
     }
 
+    if (this._isOneShot) {
+      throw new Error(l10n.lookup("csscoverageOneShotReportError"));
+    }
+
     // Helper function to create a JSONable data structure representing a rule
     const ruleToRuleReport = function(rule, ruleData) {
       return {
         url: rule.url,
         shortUrl: rule.url.split("/").slice(-1)[0],
         start: { line: rule.line, column: rule.column },
         selectorText: ruleData.selectorText,
         formattedCssText: CssLogic.prettifyCSS(ruleData.cssText)
@@ -772,21 +790,21 @@ const CSSUsageFront = protocol.FrontClas
       gDevTools.showToolbox(target, "styleeditor");
       target = undefined;
     }
   }),
 
   /**
    * Server-side start is above. Client-side start adds a notification box
    */
-  start: custom(function(newChromeWindow, newTarget) {
+  start: custom(function(newChromeWindow, newTarget, noreload=false) {
     target = newTarget;
     chromeWindow = newChromeWindow;
 
-    return this._start();
+    return this._start(noreload);
   }, {
     impl: "_start"
   }),
 
   /**
    * Server-side start is above. Client-side start adds a notification box
    */
   toggle: custom(function(newChromeWindow, newTarget) {
--- a/toolkit/devtools/server/main.js
+++ b/toolkit/devtools/server/main.js
@@ -157,21 +157,19 @@ function ModuleAPI() {
     }
   }
 };
 
 /***
  * Public API
  */
 var DebuggerServer = {
-  _listener: null,
+  _listeners: [],
   _initialized: false,
   _transportInitialized: false,
-  // Number of currently open TCP connections.
-  _socketConnections: 0,
   // Map of global actor names to actor constructors provided by extensions.
   globalActorFactories: {},
   // Map of tab actor names to actor constructors provided by extensions.
   tabActorFactories: {},
 
   LONG_STRING_LENGTH: 10000,
   LONG_STRING_INITIAL_LENGTH: 1000,
   LONG_STRING_READ_LENGTH: 65 * 1024,
@@ -207,17 +205,17 @@ var DebuggerServer = {
                 prompt.BUTTON_POS_2 * prompt.BUTTON_TITLE_IS_STRING +
                 prompt.BUTTON_POS_1_DEFAULT;
     let result = prompt.confirmEx(null, title, msg, flags, null, null,
                                   disableButton, null, { value: false });
     if (result == 0) {
       return true;
     }
     if (result == 2) {
-      DebuggerServer.closeListener(true);
+      DebuggerServer.closeAllListeners();
       Services.prefs.setBoolPref("devtools.debugger.remote-enabled", false);
     }
     return false;
   },
 
   /**
    * Initialize the debugger server.
    *
@@ -277,17 +275,17 @@ var DebuggerServer = {
     }
 
     for (let id of Object.getOwnPropertyNames(gRegisteredModules)) {
       let mod = gRegisteredModules[id];
       mod.module.unregister(mod.api);
     }
     gRegisteredModules = {};
 
-    this.closeListener();
+    this.closeAllListeners();
     this.globalActorFactories = {};
     this.tabActorFactories = {};
     this._allowConnection = null;
     this._transportInitialized = false;
     this._initialized = false;
 
     dumpn("Debugger server is shut down.");
   },
@@ -431,81 +429,72 @@ var DebuggerServer = {
     // Pass to all connections
     for (let connID of Object.getOwnPropertyNames(this._connections)) {
       promises.push(this._connections[connID].setAddonOptions(aId, aOptions));
     }
 
     return all(promises);
   },
 
+  get listeningSockets() {
+    return this._listeners.length;
+  },
+
+  // TODO: Bug 1033079: Remove after cleaning up Gaia test:
+  // https://github.com/mozilla-b2g/gaia/blob/1ba15ce1ae7254badd25fd276556c1b4f36c0a45/tests/integration/devtools/server_test.js#L31
+  get _listener() {
+    return this.listeningSockets;
+  },
+
   /**
    * Listens on the given port or socket file for remote debugger connections.
    *
-   * @param aPortOrPath int, string
+   * @param portOrPath int, string
    *        If given an integer, the port to listen on.
    *        Otherwise, the path to the unix socket domain file to listen on.
+   * @return SocketListener
+   *         A SocketListener instance that is already opened is returned.  This
+   *         single listener can be closed at any later time by calling |close|
+   *         on the SocketListener.  If a SocketListener could not be opened, an
+   *         error is thrown.  If remote connections are disabled, undefined is
+   *         returned.
    */
-  openListener: function DS_openListener(aPortOrPath) {
+  openListener: function(portOrPath) {
     if (!Services.prefs.getBoolPref("devtools.debugger.remote-enabled")) {
-      return false;
+      return;
     }
     this._checkInit();
 
-    // Return early if the server is already listening.
-    if (this._listener) {
-      return true;
-    }
-
-    let flags = Ci.nsIServerSocket.KeepWhenOffline;
-    // A preference setting can force binding on the loopback interface.
-    if (Services.prefs.getBoolPref("devtools.debugger.force-local")) {
-      flags |= Ci.nsIServerSocket.LoopbackOnly;
-    }
-
-    try {
-      let backlog = 4;
-      let socket;
-      let port = Number(aPortOrPath);
-      if (port) {
-        socket = new ServerSocket(port, flags, backlog);
-      } else {
-        let file = nsFile(aPortOrPath);
-        if (file.exists())
-          file.remove(false);
-        socket = new UnixDomainServerSocket(file, parseInt("666", 8), backlog);
-      }
-      socket.asyncListen(this);
-      this._listener = socket;
-    } catch (e) {
-      dumpn("Could not start debugging listener on '" + aPortOrPath + "': " + e);
-      throw Cr.NS_ERROR_NOT_AVAILABLE;
-    }
-    this._socketConnections++;
-
-    return true;
+    let listener = new SocketListener(this);
+    listener.open(portOrPath);
+    this._listeners.push(listener);
+    return listener;
   },
 
   /**
-   * Close a previously-opened TCP listener.
+   * Remove a SocketListener instance from the server's set of active
+   * SocketListeners.  This is called by a SocketListener after it is closed.
+   */
+  _removeListener: function(listener) {
+    this._listeners = this._listeners.filter(l => l !== listener);
+  },
+
+  /**
+   * Closes and forgets all previously opened listeners.
    *
-   * @param aForce boolean [optional]
-   *        If set to true, then the socket will be closed, regardless of the
-   *        number of open connections.
+   * @return boolean
+   *         Whether any listeners were actually closed.
    */
-  closeListener: function DS_closeListener(aForce) {
-    if (!this._listener || this._socketConnections == 0) {
+  closeAllListeners: function() {
+    if (!this.listeningSockets) {
       return false;
     }
 
-    // Only close the listener when the last connection is closed, or if the
-    // aForce flag is passed.
-    if (--this._socketConnections == 0 || aForce) {
-      this._listener.close();
-      this._listener = null;
-      this._socketConnections = 0;
+    for (let listener of this._listeners) {
+      listener.close();
     }
 
     return true;
   },
 
   /**
    * Creates a new connection to the local debugger speaking over a fake
    * transport. This connection results in straightforward calls to the onPacket
@@ -660,35 +649,16 @@ var DebuggerServer = {
       Services.obs.removeObserver(onMessageManagerDisconnect, "message-manager-disconnect");
     });
 
     mm.sendAsyncMessage("debug:connect", { prefix: prefix });
 
     return deferred.promise;
   },
 
-  // nsIServerSocketListener implementation
-
-  onSocketAccepted:
-  DevToolsUtils.makeInfallible(function DS_onSocketAccepted(aSocket, aTransport) {
-    if (Services.prefs.getBoolPref("devtools.debugger.prompt-connection") && !this._allowConnection()) {
-      return;
-    }
-    dumpn("New debugging connection on " + aTransport.host + ":" + aTransport.port);
-
-    let input = aTransport.openInputStream(0, 0, 0);
-    let output = aTransport.openOutputStream(0, 0, 0);
-    let transport = new DebuggerTransport(input, output);
-    DebuggerServer._onConnection(transport);
-  }, "DebuggerServer.onSocketAccepted"),
-
-  onStopListening: function DS_onStopListening(aSocket, status) {
-    dumpn("onStopListening, status: " + status);
-  },
-
   /**
    * Raises an exception if the server has not been properly initialized.
    */
   _checkInit: function DS_checkInit() {
     if (!this._transportInitialized) {
       throw "DebuggerServer has not been initialized.";
     }
 
@@ -855,16 +825,104 @@ includes.forEach(name => {
 });
 
 // Export ActorPool for requirers of main.js
 if (this.exports) {
   exports.ActorPool = ActorPool;
 }
 
 /**
+ * Creates a new socket listener for remote connections to a given
+ * DebuggerServer.  This helps contain and organize the parts of the server that
+ * may differ or are particular to one given listener mechanism vs. another.
+ */
+function SocketListener(server) {
+  this._server = server;
+}
+
+SocketListener.prototype = {
+
+  /**
+   * Listens on the given port or socket file for remote debugger connections.
+   *
+   * @param portOrPath int, string
+   *        If given an integer, the port to listen on.
+   *        Otherwise, the path to the unix socket domain file to listen on.
+   */
+  open: function(portOrPath) {
+    let flags = Ci.nsIServerSocket.KeepWhenOffline;
+    // A preference setting can force binding on the loopback interface.
+    if (Services.prefs.getBoolPref("devtools.debugger.force-local")) {
+      flags |= Ci.nsIServerSocket.LoopbackOnly;
+    }
+
+    try {
+      let backlog = 4;
+      let port = Number(portOrPath);
+      if (port) {
+        this._socket = new ServerSocket(port, flags, backlog);
+      } else {
+        let file = nsFile(portOrPath);
+        if (file.exists())
+          file.remove(false);
+        this._socket = new UnixDomainServerSocket(file, parseInt("666", 8),
+                                                  backlog);
+      }
+      this._socket.asyncListen(this);
+    } catch (e) {
+      dumpn("Could not start debugging listener on '" + portOrPath + "': " + e);
+      throw Cr.NS_ERROR_NOT_AVAILABLE;
+    }
+  },
+
+  /**
+   * Closes the SocketListener.  Notifies the server to remove the listener from
+   * the set of active SocketListeners.
+   */
+  close: function() {
+    this._socket.close();
+    this._server._removeListener(this);
+    this._server = null;
+  },
+
+  /**
+   * Gets the port that a TCP socket listener is listening on, or null if this
+   * is not a TCP socket (so there is no port).
+   */
+  get port() {
+    if (!this._socket) {
+      return null;
+    }
+    return this._socket.port;
+  },
+
+  // nsIServerSocketListener implementation
+
+  onSocketAccepted:
+  DevToolsUtils.makeInfallible(function(aSocket, aTransport) {
+    if (Services.prefs.getBoolPref("devtools.debugger.prompt-connection") &&
+        !this._server._allowConnection()) {
+      return;
+    }
+    dumpn("New debugging connection on " +
+          aTransport.host + ":" + aTransport.port);
+
+    let input = aTransport.openInputStream(0, 0, 0);
+    let output = aTransport.openOutputStream(0, 0, 0);
+    let transport = new DebuggerTransport(input, output);
+    this._server._onConnection(transport);
+  }, "SocketListener.onSocketAccepted"),
+
+  onStopListening: function(aSocket, status) {
+    dumpn("onStopListening, status: " + status);
+  }
+
+};
+
+/**
  * Creates a DebuggerServerConnection.
  *
  * Represents a connection to this debugging global from a client.
  * Manages a set of actors and actor pools, allocates actor ids, and
  * handles incoming requests.
  *
  * @param aPrefix string
  *        All actor IDs created by this connection should be prefixed
--- a/toolkit/devtools/server/protocol.js
+++ b/toolkit/devtools/server/protocol.js
@@ -1102,17 +1102,19 @@ let Front = Class({
       let msg = "Unexpected packet " + this.actorID + ", " + JSON.stringify(packet);
       let err = Error(msg);
       console.error(err);
       throw err;
     }
 
     let deferred = this._requests.shift();
     if (packet.error) {
-      deferred.reject(packet.error);
+      let message = (packet.error == "unknownError" && packet.message) ?
+                    packet.message : packet.error;
+      deferred.reject(message);
     } else {
       deferred.resolve(packet);
     }
   }
 });
 exports.Front = Front;
 
 /**
--- a/toolkit/devtools/server/tests/unit/test_dbgglobal.js
+++ b/toolkit/devtools/server/tests/unit/test_dbgglobal.js
@@ -6,35 +6,35 @@ Cu.import("resource://gre/modules/devtoo
 
 function run_test()
 {
   // Should get an exception if we try to interact with DebuggerServer
   // before we initialize it...
   check_except(function() {
     DebuggerServer.openListener(-1);
   });
-  check_except(DebuggerServer.closeListener);
+  check_except(DebuggerServer.closeAllListeners);
   check_except(DebuggerServer.connectPipe);
 
   // 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(-1);
   });
-  check_except(DebuggerServer.closeListener);
+  check_except(DebuggerServer.closeAllListeners);
   check_except(DebuggerServer.connectPipe);
 
   DebuggerServer.registerModule("xpcshell-test/testactors");
 
   // Now they should work.
   DebuggerServer.openListener(-1);
-  DebuggerServer.closeListener();
+  DebuggerServer.closeAllListeners();
 
   // Make sure we got the test's root actor all set up.
   let client1 = DebuggerServer.connectPipe();
   client1.hooks = {
     onPacket: function(aPacket1) {
       do_check_eq(aPacket1.from, "root");
       do_check_eq(aPacket1.applicationType, "xpcshell-tests");
 
--- a/toolkit/devtools/transport/tests/unit/head_dbg.js
+++ b/toolkit/devtools/transport/tests/unit/head_dbg.js
@@ -245,34 +245,23 @@ function writeTestTempFile(aFileName, aC
       let numWritten = stream.write(aContent, aContent.length);
       aContent = aContent.slice(numWritten);
     } while (aContent.length > 0);
   } finally {
     stream.close();
   }
 }
 
-function try_open_listener() {
-  if (DebuggerServer._listener) {
-    return DebuggerServer._listener.port;
-  }
-  try {
-    // Pick a random one between 2000 and 65000.
-    let port = Math.floor(Math.random() * (65000 - 2000 + 1)) + 2000;
-    do_check_true(DebuggerServer.openListener(port));
-    return port;
-  } catch (e) {
-    return try_open_listener();
-  }
-}
-
 /*** Transport Factories ***/
 
 function socket_transport() {
-  let port = try_open_listener();
+  if (!DebuggerServer.listeningSockets) {
+    DebuggerServer.openListener(-1);
+  }
+  let port = DebuggerServer._listeners[0].port;
   do_print("Debugger server port is " + port);
   return debuggerSocketConnect("127.0.0.1", port);
 }
 
 function local_transport() {
   return DebuggerServer.connectPipe();
 }
 
--- a/toolkit/devtools/transport/tests/unit/test_dbgsocket.js
+++ b/toolkit/devtools/transport/tests/unit/test_dbgsocket.js
@@ -1,49 +1,43 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
 Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
 
-let port = 2929;
+let gPort;
+let gExtraListener;
 
 function run_test()
 {
   do_print("Starting test at " + new Date().toTimeString());
   initTestDebuggerServer();
 
   add_test(test_socket_conn);
   add_test(test_socket_shutdown);
   add_test(test_pipe_conn);
 
   run_next_test();
 }
 
-function really_long() {
-  let ret = "0123456789";
-  for (let i = 0; i < 18; i++) {
-    ret += ret;
-  }
-  return ret;
-}
-
 function test_socket_conn()
 {
-  do_check_eq(DebuggerServer._socketConnections, 0);
-  try_open_listener();
-  do_print("Debugger server port is " + port);
-  do_check_eq(DebuggerServer._socketConnections, 1);
-  // Make sure opening the listener twice does nothing.
-  do_check_true(DebuggerServer.openListener(port));
-  do_check_eq(DebuggerServer._socketConnections, 1);
+  do_check_eq(DebuggerServer.listeningSockets, 0);
+  do_check_true(DebuggerServer.openListener(-1));
+  do_check_eq(DebuggerServer.listeningSockets, 1);
+  gPort = DebuggerServer._listeners[0].port;
+  do_print("Debugger server port is " + gPort);
+  // Open a second, separate listener
+  gExtraListener = DebuggerServer.openListener(-1);
+  do_check_eq(DebuggerServer.listeningSockets, 2);
 
   do_print("Starting long and unicode tests at " + new Date().toTimeString());
   let unicodeString = "(╯°□°)╯︵ ┻━┻";
-  let transport = debuggerSocketConnect("127.0.0.1", port);
+  let transport = debuggerSocketConnect("127.0.0.1", gPort);
   transport.hooks = {
     onPacket: function(aPacket) {
       this.onPacket = function(aPacket) {
         do_check_eq(aPacket.unicode, unicodeString);
         transport.close();
       }
       // Verify that things work correctly when bigger than the output
       // transport buffers and when transporting unicode...
@@ -57,25 +51,27 @@ function test_socket_conn()
       run_next_test();
     },
   };
   transport.ready();
 }
 
 function test_socket_shutdown()
 {
-  do_check_eq(DebuggerServer._socketConnections, 1);
-  do_check_true(DebuggerServer.closeListener());
-  do_check_eq(DebuggerServer._socketConnections, 0);
+  do_check_eq(DebuggerServer.listeningSockets, 2);
+  gExtraListener.close();
+  do_check_eq(DebuggerServer.listeningSockets, 1);
+  do_check_true(DebuggerServer.closeAllListeners());
+  do_check_eq(DebuggerServer.listeningSockets, 0);
   // Make sure closing the listener twice does nothing.
-  do_check_false(DebuggerServer.closeListener());
-  do_check_eq(DebuggerServer._socketConnections, 0);
+  do_check_false(DebuggerServer.closeAllListeners());
+  do_check_eq(DebuggerServer.listeningSockets, 0);
 
   do_print("Connecting to a server socket at " + new Date().toTimeString());
-  let transport = debuggerSocketConnect("127.0.0.1", port);
+  let transport = debuggerSocketConnect("127.0.0.1", gPort);
   transport.hooks = {
     onPacket: function(aPacket) {
       // Shouldn't reach this, should never connect.
       do_check_true(false);
     },
 
     onClosed: function(aStatus) {
       do_print("test_socket_shutdown onClosed called at " + new Date().toTimeString());
@@ -101,19 +97,8 @@ function test_pipe_conn()
     },
     onClosed: function(aStatus) {
       run_next_test();
     }
   };
 
   transport.ready();
 }
-
-function try_open_listener()
-{
-  try {
-    do_check_true(DebuggerServer.openListener(port));
-  } catch (e) {
-    // In case the port is unavailable, pick a random one between 2000 and 65000.
-    port = Math.floor(Math.random() * (65000 - 2000 + 1)) + 2000;
-    try_open_listener();
-  }
-}
--- a/toolkit/devtools/transport/tests/unit/test_dbgsocket_connection_drop.js
+++ b/toolkit/devtools/transport/tests/unit/test_dbgsocket_connection_drop.js
@@ -8,18 +8,16 @@
  * framed packet, i.e. when the length header is invalid.
  */
 
 Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
 Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
 
 const { RawPacket } = devtools.require("devtools/toolkit/transport/packets");
 
-let port = 2929;
-
 function run_test() {
   do_print("Starting test at " + new Date().toTimeString());
   initTestDebuggerServer();
 
   add_test(test_socket_conn_drops_after_invalid_header);
   add_test(test_socket_conn_drops_after_invalid_header_2);
   add_test(test_socket_conn_drops_after_too_large_length);
   add_test(test_socket_conn_drops_after_too_long_header);
@@ -44,39 +42,29 @@ function test_socket_conn_drops_after_to
   let rawPacket = '4305724038957487634549823475894325';
   for (let i = 0; i < 8; i++) {
     rawPacket += rawPacket;
   }
   return test_helper(rawPacket + ':');
 }
 
 function test_helper(payload) {
-  try_open_listener();
+  let listener = DebuggerServer.openListener(-1);
 
-  let transport = debuggerSocketConnect("127.0.0.1", port);
+  let transport = debuggerSocketConnect("127.0.0.1", listener.port);
   transport.hooks = {
     onPacket: function(aPacket) {
       this.onPacket = function(aPacket) {
         do_throw(new Error("This connection should be dropped."));
         transport.close();
-      }
+      };
 
       // Inject the payload directly into the stream.
       transport._outgoing.push(new RawPacket(transport, payload));
       transport._flushOutgoing();
     },
     onClosed: function(aStatus) {
       do_check_true(true);
       run_next_test();
     },
   };
   transport.ready();
 }
-
-function try_open_listener() {
-  try {
-    do_check_true(DebuggerServer.openListener(port));
-  } catch (e) {
-    // In case the port is unavailable, pick a random one between 2000 and 65000.
-    port = Math.floor(Math.random() * (65000 - 2000 + 1)) + 2000;
-    try_open_listener();
-  }
-}
--- a/toolkit/locales/en-US/chrome/global/devtools/csscoverage.properties
+++ b/toolkit/locales/en-US/chrome/global/devtools/csscoverage.properties
@@ -9,21 +9,24 @@
 # csscoverageStopDesc2, csscoverageOneShotDesc2, csscoverageToggleDesc2,
 # csscoverageReportDesc2): Short descriptions of the csscoverage commands
 csscoverageDesc=Control CSS coverage analysis
 csscoverageStartDesc2=Begin collecting CSS coverage data
 csscoverageStopDesc2=Stop collecting CSS coverage data
 csscoverageOneShotDesc2=Collect instantaneous CSS coverage data
 csscoverageToggleDesc2=Toggle collecting CSS coverage data
 csscoverageReportDesc2=Show CSS coverage report
+csscoverageStartNoReloadDesc=Don't start with a page reload
+csscoverageStartNoReloadManual=It's best if we start by reloading the current page because that starts the test at a known point, but there could be reasons why we don't want to do that (e.g. the page contains state that will be lost across a reload)
 
 # LOCALIZATION NOTE (csscoverageRunningReply, csscoverageDoneReply): Text that
 # describes the current state of the css coverage system
 csscoverageRunningReply=Running CSS coverage analysis
 csscoverageDoneReply=CSS Coverage analysis completed
 
 # LOCALIZATION NOTE (csscoverageRunningError, csscoverageNotRunningError,
 # csscoverageNotRunError): Error message that describe things that can go wrong
 # with the css coverage system
 csscoverageRunningError=CSS coverage analysis already running
 csscoverageNotRunningError=CSS coverage analysis not running
 csscoverageNotRunError=CSS coverage analysis has not been run
 csscoverageNoRemoteError=Target does not support CSS Coverage
+csscoverageOneShotReportError=CSS coverage report is not available for 'oneshot' data. Please use start/stop.
--- a/tools/docs/conf.py
+++ b/tools/docs/conf.py
@@ -14,16 +14,17 @@ mozilla_dir = os.environ['MOZILLA_DIR']
 
 import mdn_theme
 
 extensions = [
     'sphinx.ext.autodoc',
     'sphinx.ext.graphviz',
     'sphinx.ext.todo',
     'mozbuild.sphinx',
+    'javasphinx',
 ]
 
 templates_path = ['_templates']
 source_suffix = '.rst'
 master_doc = 'index'
 project = u'Mozilla Source Tree Docs'
 year = datetime.now().year