merge mozilla-central to mozilla-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 11 Sep 2013 13:50:28 +0200
changeset 146550 ab26dc7677f9437cec4802f5291a3cf3159d03c1
parent 146549 bf6bfb9a52aa13423390bcf5295929e266e752f6 (current diff)
parent 146519 5417e5da2cebc0d34ccc05b16be513b5d8a01e55 (diff)
child 146551 e6a358389a6064b454e373324fe933f9fe16de3b
push id25266
push userryanvm@gmail.com
push dateThu, 12 Sep 2013 00:24:40 +0000
treeherdermozilla-central@162f05aec17a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone26.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 mozilla-central to mozilla-inbound
browser/components/preferences/in-content/tabs.js
browser/components/preferences/in-content/tabs.xul
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "3014a433e3b78fa04aacb919c853fa220d309d70", 
+    "revision": "e3d925b497f5b996c9c397c200805fbcc62bc823", 
     "repo_path": "/integration/gaia-central"
 }
--- a/browser/components/preferences/in-content/jar.mn
+++ b/browser/components/preferences/in-content/jar.mn
@@ -1,20 +1,18 @@
 # 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/.
 
 browser.jar:
-*  content/browser/preferences/in-content/preferences.js
+   content/browser/preferences/in-content/preferences.js
    content/browser/preferences/in-content/landing.xul
 *  content/browser/preferences/in-content/preferences.xul
 *  content/browser/preferences/in-content/main.xul
-   content/browser/preferences/in-content/main.js
-*  content/browser/preferences/in-content/tabs.xul
-*  content/browser/preferences/in-content/tabs.js
+*  content/browser/preferences/in-content/main.js
    content/browser/preferences/in-content/privacy.xul
 *  content/browser/preferences/in-content/privacy.js
 *  content/browser/preferences/in-content/advanced.xul
 *  content/browser/preferences/in-content/advanced.js
    content/browser/preferences/in-content/applications.xul
 *  content/browser/preferences/in-content/applications.js
    content/browser/preferences/in-content/content.xul
    content/browser/preferences/in-content/content.js
--- a/browser/components/preferences/in-content/landing.xul
+++ b/browser/components/preferences/in-content/landing.xul
@@ -8,21 +8,17 @@
   <hbox id="preferences-home" flex="1">
 
     <button label="&paneGeneral.title;" class="landingButton"
             oncommand="gotoPref('paneGeneral');">
       <image class="landingButton-icon" type="general"/>
       <label class="landingButton-label">&paneGeneral.title;</label>
     </button>
 
-    <button label="&paneTabs.title;" class="landingButton"
-            oncommand="gotoPref('paneTabs');">
-      <image class="landingButton-icon" type="tabs"/>
-      <label class="landingButton-label">&paneTabs.title;</label>
-    </button>
+
 
     <button label="&paneContent.title;" class="landingButton"
             oncommand="gotoPref('paneContent');">
       <image class="landingButton-icon" type="content"/>
       <label class="landingButton-label">&paneContent.title;</label>
     </button>
 
     <button label="&paneApplications.title;" class="landingButton"
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -1,37 +1,47 @@
 /* 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/. */
 
 XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
                                   "resource:///modules/DownloadsCommon.jsm");
 
 var gMainPane = {
-  _pane: null,
-
   /**
    * Initialization of this.
    */
   init: function ()
   {
-    this._pane = document.getElementById("paneMain");
-
     // set up the "use current page" label-changing listener
     this._updateUseCurrentButton();
     window.addEventListener("focus", this._updateUseCurrentButton.bind(this), false);
 
     this.updateBrowserStartupLastSession();
 
     this.setupDownloadsWindowOptions();
 
     // Notify observers that the UI is now ready
     Components.classes["@mozilla.org/observer-service;1"]
               .getService(Components.interfaces.nsIObserverService)
               .notifyObservers(window, "main-pane-loaded", null);
+
+    //Functionality for "Show tabs in taskbar" on Win XP
+
+#ifdef XP_WIN
+    try {
+      let sysInfo = Cc["@mozilla.org/system-info;1"].
+                    getService(Ci.nsIPropertyBag2);
+      let ver = parseFloat(sysInfo.getProperty("version"));
+      let showTabsInTaskbar = document.getElementById("showTabsInTaskbar");
+      showTabsInTaskbar.hidden = ver < 6.1 || (ver >= 6.1 && history.state != "tabs");
+    } catch (ex) {}
+
+#endif
+
   },
 
   setupDownloadsWindowOptions: function ()
   {
     let showWhenDownloading = document.getElementById("showWhenDownloading");
     let closeWhenDone = document.getElementById("closeWhenDone");
 
     // These radio buttons should be hidden when the Downloads Panel is enabled.
@@ -470,10 +480,33 @@ var gMainPane = {
       option.setAttribute("disabled", "true");
       if (option.selected) {
         menu.selectedItem = document.getElementById("browserStartupHomePage");
       }
     } else {
       option.removeAttribute("disabled");
       startupPref.updateElements(); // select the correct index in the startup menulist
     }
+  },
+  // TABS
+
+
+  /**
+   * Determines where a link which opens a new window will open.
+   *
+   * @returns |true| if such links should be opened in new tabs
+   */
+  readLinkTarget: function() {
+    var openNewWindow = document.getElementById("browser.link.open_newwindow");
+    return openNewWindow.value != 2;
+  },
+
+  /**
+   * Determines where a link which opens a new window will open.
+   *
+   * @returns 2 if such links should be opened in new windows,
+   *          3 if such links should be opened in new tabs
+   */
+  writeLinkTarget: function() {
+    var linkTargeting = document.getElementById("linkTargeting");
+    return linkTargeting.checked ? 3 : 2;
   }
 };
--- a/browser/components/preferences/in-content/main.xul
+++ b/browser/components/preferences/in-content/main.xul
@@ -44,16 +44,57 @@
     <preference id="browser.download.dir"
                 name="browser.download.dir"
                 type="file"
                 onchange="gMainPane.displayDownloadDirPref();"/>
     <preference id="browser.download.folderList"
                 name="browser.download.folderList"
                 type="int"/>
 
+    <!-- Tab preferences 
+    Preferences:
+
+    browser.link.open_newwindow
+        1 opens such links in the most recent window or tab,
+        2 opens such links in a new window,
+        3 opens such links in a new tab
+    browser.tabs.loadInBackground
+    - true if display should switch to a new tab which has been opened from a
+      link, false if display shouldn't switch
+    browser.tabs.warnOnClose
+    - true if when closing a window with multiple tabs the user is warned and
+      allowed to cancel the action, false to just close the window
+    browser.tabs.warnOnOpen
+    - true if the user should be warned if he attempts to open a lot of tabs at
+      once (e.g. a large folder of bookmarks), false otherwise
+    browser.taskbar.previews.enable
+    - true if tabs are to be shown in the Windows 7 taskbar    
+    -->
+
+    <preference id="browser.link.open_newwindow"
+                name="browser.link.open_newwindow"
+                type="int"/>
+    <preference id="browser.tabs.loadInBackground"
+                name="browser.tabs.loadInBackground"
+                type="bool"
+                inverted="true"/>
+    <preference id="browser.tabs.warnOnClose"
+                name="browser.tabs.warnOnClose"
+                type="bool"/>
+    <preference id="browser.tabs.warnOnOpen"
+                name="browser.tabs.warnOnOpen"
+                type="bool"/>
+    <preference id="browser.sessionstore.restore_on_demand"
+                name="browser.sessionstore.restore_on_demand"
+                type="bool"/>
+#ifdef XP_WIN
+    <preference id="browser.taskbar.previews.enable"
+                name="browser.taskbar.previews.enable"
+                type="bool"/>
+#endif
 </preferences>
 
 <hbox class="heading" data-category="paneGeneral" hidden="true">
   <image class="preference-icon" type="general"/>
   <html:h1>&paneGeneral.title;</html:h1>
 </hbox>
 
 <!-- Startup -->
@@ -161,8 +202,47 @@
               onsynctopreference="return gMainPane.getFolderListPref();"/>
     </hbox>
     <radio id="alwaysAsk"
            value="false"
            label="&alwaysAsk.label;"
            accesskey="&alwaysAsk.accesskey;"/>
   </radiogroup>
 </groupbox>
+
+<!-- Tab preferences -->
+<groupbox data-category="paneGeneral" hidden="true">
+    <caption label="&tabsGroup.label;"/>
+    <checkbox id="linkTargeting" label="&newWindowsAsTabs.label;"
+              accesskey="&newWindowsAsTabs.accesskey;"
+              preference="browser.link.open_newwindow"
+              onsyncfrompreference="return gMainPane.readLinkTarget();"
+              onsynctopreference="return gMainPane.writeLinkTarget();"
+              class="indent"/>
+
+    <checkbox id="warnCloseMultiple" label="&warnCloseMultipleTabs.label;"
+              accesskey="&warnCloseMultipleTabs.accesskey;"
+              preference="browser.tabs.warnOnClose"
+              class="indent"/>
+
+    <checkbox id="warnOpenMany" label="&warnOpenManyTabs.label;"
+              accesskey="&warnOpenManyTabs.accesskey;"
+              preference="browser.tabs.warnOnOpen"
+              class="indent"/>
+
+    <checkbox id="restoreOnDemand" label="&restoreTabsOnDemand.label;"
+              accesskey="&restoreTabsOnDemand.accesskey;"
+              preference="browser.sessionstore.restore_on_demand"
+              class="indent"/>
+
+    <checkbox id="switchToNewTabs" label="&switchToNewTabs.label;"
+              accesskey="&switchToNewTabs.accesskey;"
+              preference="browser.tabs.loadInBackground"
+              class="indent"/>
+
+#ifdef XP_WIN
+    <checkbox id="showTabsInTaskbar" label="&showTabsInTaskbar.label;"
+              accesskey="&showTabsInTaskbar.accesskey;"
+              preference="browser.taskbar.previews.enable"
+              class="indent"/>
+#endif
+
+</groupbox>
--- a/browser/components/preferences/in-content/preferences.js
+++ b/browser/components/preferences/in-content/preferences.js
@@ -13,19 +13,16 @@ Cu.import("resource://gre/modules/XPCOMU
 Cu.import("resource://gre/modules/Services.jsm");
 
 function init_all() {
   document.documentElement.instantApply = true;
   window.history.replaceState("landing", document.title);
   window.addEventListener("popstate", onStatePopped, true);
   updateCommands();
   gMainPane.init();
-#ifdef XP_WIN
-  gTabsPane.init();
-#endif
   gPrivacyPane.init();
   gAdvancedPane.init();
   gApplicationsPane.init();
   gContentPane.init();
   gSyncPane.init();
   gSecurityPane.init();
   var initFinished = document.createEvent("Event");
   initFinished.initEvent("Initialized", true, true);
--- a/browser/components/preferences/in-content/preferences.xul
+++ b/browser/components/preferences/in-content/preferences.xul
@@ -86,17 +86,16 @@
                    oncommand="cmd_forward()" tooltiptext="&buttonForward.tooltip;"
                    disabled="true"/>
   </hbox>
   
   <hbox class="main-content" flex="1">
     <prefpane flex="1" id="mainPrefPane">
 #include landing.xul
 #include main.xul
-#include tabs.xul
 #include privacy.xul
 #include advanced.xul
 #include applications.xul
 #include content.xul
 #include security.xul
 #ifdef MOZ_SERVICES_SYNC
 #include sync.xul
 #endif
deleted file mode 100644
--- a/browser/components/preferences/in-content/tabs.js
+++ /dev/null
@@ -1,63 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-var gTabsPane = {
-
-  /*
-   * Preferences:
-   *
-   * browser.link.open_newwindow
-   * - determines where pages which would open in a new window are opened:
-   *     1 opens such links in the most recent window or tab,
-   *     2 opens such links in a new window,
-   *     3 opens such links in a new tab
-   * browser.tabs.loadInBackground
-   * - true if display should switch to a new tab which has been opened from a
-   *   link, false if display shouldn't switch
-   * browser.tabs.warnOnClose
-   * - true if when closing a window with multiple tabs the user is warned and
-   *   allowed to cancel the action, false to just close the window
-   * browser.tabs.warnOnOpen
-   * - true if the user should be warned if he attempts to open a lot of tabs at
-   *   once (e.g. a large folder of bookmarks), false otherwise
-   * browser.taskbar.previews.enable
-   * - true if tabs are to be shown in the Windows 7 taskbar
-   */
-
-#ifdef XP_WIN
-  /**
-   * Initialize any platform-specific UI.
-   */
-  init: function () {
-    try {
-      let sysInfo = Cc["@mozilla.org/system-info;1"].
-                    getService(Ci.nsIPropertyBag2);
-      let ver = parseFloat(sysInfo.getProperty("version"));
-      let showTabsInTaskbar = document.getElementById("showTabsInTaskbar");
-      showTabsInTaskbar.hidden = ver < 6.1 || (ver >= 6.1 && history.state != "tabs");
-    } catch (ex) {}
-  },
-#endif
-
-  /**
-   * Determines where a link which opens a new window will open.
-   *
-   * @returns |true| if such links should be opened in new tabs
-   */
-  readLinkTarget: function() {
-    var openNewWindow = document.getElementById("browser.link.open_newwindow");
-    return openNewWindow.value != 2;
-  },
-
-  /**
-   * Determines where a link which opens a new window will open.
-   *
-   * @returns 2 if such links should be opened in new windows,
-   *          3 if such links should be opened in new tabs
-   */
-  writeLinkTarget: function() {
-    var linkTargeting = document.getElementById("linkTargeting");
-    return linkTargeting.checked ? 3 : 2;
-  }
-};
deleted file mode 100644
--- a/browser/components/preferences/in-content/tabs.xul
+++ /dev/null
@@ -1,76 +0,0 @@
-<!-- This Source Code Form is subject to the terms of the Mozilla Public License,
-   - v. 2.0. If a copy of the MPL was not distributed with this file,
-   - You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-<script type="application/javascript"
-        src="chrome://browser/content/preferences/in-content/tabs.js"/>
-
-<preferences id="tabsPreferences">
-  <preference id="browser.link.open_newwindow"
-              name="browser.link.open_newwindow"
-              type="int"/>
-  <preference id="browser.tabs.loadInBackground"
-              name="browser.tabs.loadInBackground"
-              type="bool"
-              inverted="true"/>
-  <preference id="browser.tabs.warnOnClose"
-              name="browser.tabs.warnOnClose"
-              type="bool"/>
-  <preference id="browser.tabs.warnOnOpen"
-              name="browser.tabs.warnOnOpen"
-              type="bool"/>
-  <preference id="browser.sessionstore.restore_on_demand"
-              name="browser.sessionstore.restore_on_demand"
-              type="bool"/>
-#ifdef XP_WIN
-  <preference id="browser.taskbar.previews.enable"
-              name="browser.taskbar.previews.enable"
-              type="bool"/>
-#endif
-</preferences>
-
-<hbox class="heading" data-category="paneTabs" hidden="true">
-  <image class="preference-icon" type="tabs"/>
-  <html:h1>&paneTabs.title;</html:h1>
-</hbox>
-
-<checkbox id="linkTargeting" label="&newWindowsAsTabs.label;"
-          data-category="paneTabs" hidden="true"
-          accesskey="&newWindowsAsTabs.accesskey;"
-          preference="browser.link.open_newwindow"
-          onsyncfrompreference="return gTabsPane.readLinkTarget();"
-          onsynctopreference="return gTabsPane.writeLinkTarget();"
-          class="indent"/>
-
-<checkbox id="warnCloseMultiple" label="&warnCloseMultipleTabs.label;"
-          data-category="paneTabs" hidden="true"
-          accesskey="&warnCloseMultipleTabs.accesskey;"
-          preference="browser.tabs.warnOnClose"
-          class="indent"/>
-
-<checkbox id="warnOpenMany" label="&warnOpenManyTabs.label;"
-          data-category="paneTabs" hidden="true"
-          accesskey="&warnOpenManyTabs.accesskey;"
-          preference="browser.tabs.warnOnOpen"
-          class="indent"/>
-
-
-<checkbox id="restoreOnDemand" label="&restoreTabsOnDemand.label;"
-          data-category="paneTabs" hidden="true"
-          accesskey="&restoreTabsOnDemand.accesskey;"
-          preference="browser.sessionstore.restore_on_demand"
-          class="indent"/>
-
-<checkbox id="switchToNewTabs" label="&switchToNewTabs.label;"
-          data-category="paneTabs" hidden="true"
-          accesskey="&switchToNewTabs.accesskey;"
-          preference="browser.tabs.loadInBackground"
-          class="indent"/>
-
-#ifdef XP_WIN
-<checkbox id="showTabsInTaskbar" label="&showTabsInTaskbar.label;"
-          data-category="paneTabs" hidden="true"
-          accesskey="&showTabsInTaskbar.accesskey;"
-          preference="browser.taskbar.previews.enable"
-          class="indent"/>
-#endif
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -760,16 +760,17 @@ bin/libfreebl_32int64_3.so
 @BINPATH@/webapprt/components/CommandLineHandler.js
 @BINPATH@/webapprt/components/ContentPermission.js
 @BINPATH@/webapprt/components/DirectoryProvider.js
 @BINPATH@/webapprt/components/components.manifest
 @BINPATH@/webapprt/defaults/preferences/prefs.js
 @BINPATH@/webapprt/modules/Startup.jsm
 @BINPATH@/webapprt/modules/WebappRT.jsm
 @BINPATH@/webapprt/modules/WebappsHandler.jsm
+@BINPATH@/webapprt/modules/RemoteDebugger.jsm
 #endif
 
 #ifdef MOZ_METRO
 @BINPATH@/components/MetroUIUtils.js
 @BINPATH@/components/MetroUIUtils.manifest
 [metro]
 ; gre resources
 @BINPATH@/CommandExecuteHandler@BIN_SUFFIX@
--- a/browser/locales/en-US/chrome/browser/preferences/tabs.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/tabs.dtd
@@ -14,8 +14,9 @@
 <!ENTITY restoreTabsOnDemand.label        "Don’t load tabs until selected">
 <!ENTITY restoreTabsOnDemand.accesskey    "l">
 
 <!ENTITY switchToNewTabs.label        "When I open a link in a new tab, switch to it immediately">
 <!ENTITY switchToNewTabs.accesskey    "s">
 
 <!ENTITY showTabsInTaskbar.label          "Show tab previews in the Windows taskbar">
 <!ENTITY showTabsInTaskbar.accesskey      "k">
+<!ENTITY tabsGroup.label          "Tabs">
--- a/browser/metro/base/content/bindings/urlbar.xml
+++ b/browser/metro/base/content/bindings/urlbar.xml
@@ -62,19 +62,16 @@
         </body>
       </method>
 
       <!-- URI Display: Domain Highlighting -->
 
       <method name="_clearFormatting">
         <body>
           <![CDATA[
-            if (!this._mayFormat)
-              return;
-
             let controller = this.editor.selectionController;
             let selection = controller.getSelection(controller.SELECTION_URLSECONDARY);
             selection.removeAllRanges();
           ]]>
         </body>
       </method>
 
       <method name="formatValue">
@@ -412,32 +409,39 @@
         <body>
           <![CDATA[
             if (aTopic != "nsPref:changed")
               return;
 
             switch (aData) {
               case "browser.urlbar.formatting.enabled":
                 this._mayFormat = Services.prefs.getBoolPref(aData);
+                if (!this._mayFormat) this._clearFormatting();
                 break;
               case "browser.urlbar.trimURLs":
                 this._mayTrimURLs = Services.prefs.getBoolPref(aData);
                 break;
               case "browser.urlbar.doubleClickSelectsAll":
                 this._maySelectAll = Services.prefs.getBoolPref(aData);
                 break;
             }
           ]]>
         </body>
       </method>
     </implementation>
 
     <handlers>
       <!-- Entering editing mode -->
 
+      <handler event="focus" phase="capturing">
+        <![CDATA[
+          this.beginEditing();
+        ]]>
+      </handler>
+
       <handler event="input" phase="capturing">
         <![CDATA[
           // Ensures that paste-and-go actually brings the URL bar into editing mode
           // and displays the half-height autocomplete popup.
           this.beginEditing();
           this.openPopup();
         ]]>
       </handler>
--- a/browser/metro/base/tests/mochitest/Makefile.in
+++ b/browser/metro/base/tests/mochitest/Makefile.in
@@ -31,16 +31,18 @@ MOCHITEST_METRO_FILES = \
   browser_remotetabs.js \
   browser_snappedState.js \
   browser_tabs.js \
   browser_test.js \
   browser_tiles.js \
   browser_tilegrid.xul \
   browser_topsites.js \
   browser_urlbar.js \
+  browser_urlbar_highlightURLs.js \
+  browser_urlbar_trimURLs.js \
   $(NULL)
 
 ifndef MOZ_DEBUG
 MOCHITEST_METRO_FILES += \
   browser_selection_basic.js \
   browser_selection_basic.html \
   browser_selection_textarea.js \
   browser_selection_textarea.html \
new file mode 100644
--- /dev/null
+++ b/browser/metro/base/tests/mochitest/browser_urlbar_highlightURLs.js
@@ -0,0 +1,171 @@
+// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const kHighlightPref = "browser.urlbar.formatting.enabled";
+var gHighlightPrefValue;
+
+function test() {
+  runTests();
+}
+
+function setUp() {
+  gHighlightPrefValue = SpecialPowers.getBoolPref(kHighlightPref);
+  SpecialPowers.setBoolPref(kHighlightPref, true);
+  yield addTab("about:blank");
+}
+
+function tearDown() {
+  SpecialPowers.setBoolPref(kHighlightPref, gHighlightPrefValue);
+  Browser.closeTab(Browser.selectedTab, { forceClose: true });
+}
+
+function testHighlight(aExpected) {
+  let urlbar = BrowserUI._edit;
+  urlbar.value = aExpected.replace(/[<>]/g, "");
+
+  let selectionController = urlbar.editor.selectionController;
+  let selection = selectionController.getSelection(selectionController.SELECTION_URLSECONDARY);
+  let value = urlbar.editor.rootElement.textContent;
+
+  let result = "";
+  for (let i = 0; i < selection.rangeCount; i++) {
+    let range = selection.getRangeAt(i).toString();
+    let pos = value.indexOf(range);
+    result += value.substring(0, pos) + "<" + range + ">";
+    value = value.substring(pos + range.length);
+  }
+  result += value;
+  is(result, aExpected, "test highight");
+}
+
+gTests.push({
+  desc: "Domain-based URIs (not in editing mode)",
+  setUp: setUp,
+  tearDown: tearDown,
+  run: function () {
+    let testcases = [
+      "<https://>mozilla.org",
+      "<https://>mözilla.org",
+      "<https://>mozilla.imaginatory",
+
+      "<https://www.>mozilla.org",
+      "<https://sub.>mozilla.org",
+      "<https://sub1.sub2.sub3.>mozilla.org",
+      "<www.>mozilla.org",
+      "<sub.>mozilla.org",
+      "<sub1.sub2.sub3.>mozilla.org",
+
+      "<http://ftp.>mozilla.org",
+      "<ftp://ftp.>mozilla.org",
+
+      "<https://sub.>mozilla.org",
+      "<https://sub1.sub2.sub3.>mozilla.org",
+      "<https://user:pass@sub1.sub2.sub3.>mozilla.org",
+      "<https://user:pass@>mozilla.org",
+
+      "<https://>mozilla.org</file.ext>",
+      "<https://>mozilla.org</sub/file.ext>",
+      "<https://>mozilla.org</sub/file.ext?foo>",
+      "<https://>mozilla.org</sub/file.ext?foo&bar>",
+      "<https://>mozilla.org</sub/file.ext?foo&bar#top>",
+      "<https://>mozilla.org</sub/file.ext?foo&bar#top>",
+
+      "<https://sub.>mozilla.org<:666/file.ext>",
+      "<sub.>mozilla.org<:666/file.ext>",
+      "localhost<:666/file.ext>",
+
+      "mailto:admin@mozilla.org",
+      "gopher://mozilla.org/",
+      "about:config",
+      "jar:http://mozilla.org/example.jar!/",
+      "view-source:http://mozilla.org/",
+      "foo9://mozilla.org/",
+      "foo+://mozilla.org/",
+      "foo.://mozilla.org/",
+      "foo-://mozilla.org/"
+    ];
+
+    testcases.forEach(testHighlight);
+  }
+});
+
+gTests.push({
+  desc: "IP-based URIs (not in editing mode)",
+  setUp: setUp,
+  tearDown: tearDown,
+  run: function () {
+    let ips = [
+      "192.168.1.1",
+      "[::]",
+      "[::1]",
+      "[1::]",
+      "[::]",
+      "[::1]",
+      "[1::]",
+      "[1:2:3:4:5:6:7::]",
+      "[::1:2:3:4:5:6:7]",
+      "[1:2:a:B:c:D:e:F]",
+      "[1::8]",
+      "[1:2::8]",
+      "[fe80::222:19ff:fe11:8c76]",
+      "[0000:0123:4567:89AB:CDEF:abcd:ef00:0000]",
+      "[::192.168.1.1]",
+      "[1::0.0.0.0]",
+      "[1:2::255.255.255.255]",
+      "[1:2:3::255.255.255.255]",
+      "[1:2:3:4::255.255.255.255]",
+      "[1:2:3:4:5::255.255.255.255]",
+      "[1:2:3:4:5:6:255.255.255.255]"
+    ];
+
+    let formats = [
+      "{ip}</file.ext>",
+      "{ip}<:666/file.ext>",
+      "<https://>{ip}",
+      "<https://>{ip}</file.ext>",
+      "<https://user:pass@>{ip}<:666/file.ext>",
+      "<http://user:pass@>{ip}<:666/file.ext>"
+    ];
+
+    function testHighlightAllFormats(aIP) {
+      formats.forEach((aFormat) => testHighlight(aFormat.replace("{ip}", aIP)));
+    }
+
+    ips.forEach(testHighlightAllFormats);
+  }
+});
+
+gTests.push({
+  desc: "no highlighting (in editing mode)",
+  setUp: setUp,
+  tearDown: tearDown,
+  run: function () {
+    testHighlight("<https://>mozilla.org");
+
+    BrowserUI._edit.focus();
+    testHighlight("https://mozilla.org");
+
+    Browser.selectedBrowser.focus();
+    testHighlight("<https://>mozilla.org");
+  }
+});
+
+gTests.push({
+  desc: "no higlighting (pref disabled)",
+  setUp: setUp,
+  tearDown: tearDown,
+  run: function () {
+    testHighlight("<https://>mozilla.org");
+
+    SpecialPowers.setBoolPref(kHighlightPref, false);
+    testHighlight("https://mozilla.org");
+
+    SpecialPowers.setBoolPref(kHighlightPref, true);
+    testHighlight("<https://>mozilla.org");
+  }
+});
+
new file mode 100644
--- /dev/null
+++ b/browser/metro/base/tests/mochitest/browser_urlbar_trimURLs.js
@@ -0,0 +1,141 @@
+// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const kTrimPref = "browser.urlbar.trimURLs";
+var gTrimPrefValue;
+
+function test() {
+  runTests();
+}
+
+function setUp() {
+  gTrimPrefValue = SpecialPowers.getBoolPref(kTrimPref);
+  SpecialPowers.setBoolPref(kTrimPref, true);
+  yield addTab("about:blank");
+}
+
+function tearDown() {
+  SpecialPowers.setBoolPref(kTrimPref, gTrimPrefValue);
+  Browser.closeTab(Browser.selectedTab, { forceClose: true });
+}
+
+function testTrim(aOriginal, aTarget) {
+  let urlbar = BrowserUI._edit;
+  urlbar.value = aOriginal;
+  urlbar.valueIsTyped = false;
+  is(urlbar.value, aTarget || aOriginal, "url bar value set");
+}
+
+gTests.push({
+  desc: "URIs - trimming (pref enabled)",
+  setUp: setUp,
+  tearDown: tearDown,
+  run: function () {
+    let testcases = [
+      ["http://mozilla.org/", "mozilla.org"],
+      ["https://mozilla.org/", "https://mozilla.org"],
+      ["http://mözilla.org/", "mözilla.org"],
+      ["http://mozilla.imaginatory/", "mozilla.imaginatory"],
+      ["http://www.mozilla.org/", "www.mozilla.org"],
+      ["http://sub.mozilla.org/", "sub.mozilla.org"],
+      ["http://sub1.sub2.sub3.mozilla.org/", "sub1.sub2.sub3.mozilla.org"],
+      ["http://mozilla.org/file.ext", "mozilla.org/file.ext"],
+      ["http://mozilla.org/sub/", "mozilla.org/sub/"],
+
+      ["http://ftp.mozilla.org/", "http://ftp.mozilla.org"],
+      ["http://ftp1.mozilla.org/", "http://ftp1.mozilla.org"],
+      ["http://ftp42.mozilla.org/", "http://ftp42.mozilla.org"],
+      ["http://ftpx.mozilla.org/", "ftpx.mozilla.org"],
+      ["ftp://ftp.mozilla.org/", "ftp://ftp.mozilla.org"],
+      ["ftp://ftp1.mozilla.org/", "ftp://ftp1.mozilla.org"],
+      ["ftp://ftp42.mozilla.org/", "ftp://ftp42.mozilla.org"],
+      ["ftp://ftpx.mozilla.org/", "ftp://ftpx.mozilla.org"],
+
+      ["https://user:pass@mozilla.org/", "https://user:pass@mozilla.org"],
+      ["http://user:pass@mozilla.org/", "http://user:pass@mozilla.org"],
+      ["http://sub.mozilla.org:666/", "sub.mozilla.org:666"],
+
+      ["https://[fe80::222:19ff:fe11:8c76]/file.ext"],
+      ["http://[fe80::222:19ff:fe11:8c76]/", "[fe80::222:19ff:fe11:8c76]"],
+      ["https://user:pass@[fe80::222:19ff:fe11:8c76]:666/file.ext"],
+      ["http://user:pass@[fe80::222:19ff:fe11:8c76]:666/file.ext"],
+
+      ["mailto:admin@mozilla.org"],
+      ["gopher://mozilla.org/"],
+      ["about:config"],
+      ["jar:http://mozilla.org/example.jar!/"],
+      ["view-source:http://mozilla.org/"]
+    ];
+
+    for (let [original, target] of testcases)
+      testTrim(original, target);
+  }
+});
+
+gTests.push({
+  desc: "URIs - no trimming (pref disabled)",
+  setUp: setUp,
+  tearDown: tearDown,
+  run: function () {
+    SpecialPowers.setBoolPref(kTrimPref, false);
+    testTrim("http://mozilla.org/");
+
+    SpecialPowers.setBoolPref(kTrimPref, true);
+    testTrim("http://mozilla.org/", "mozilla.org");
+  }
+});
+
+gTests.push({
+  desc: "Loaded URI - copy/paste behavior",
+  setUp: setUp,
+  tearDown: tearDown,
+  run: function () {
+    let urlbar = BrowserUI._edit;
+
+    BrowserUI.goToURI("http://example.com/");
+    let pageLoaded = yield waitForCondition(
+      () => Browser.selectedBrowser.currentURI.spec == "http://example.com/");
+
+    ok(pageLoaded, "expected page should have loaded");
+    is(urlbar.value, "example.com", "trimmed value set");
+
+    yield showNavBar();
+
+    function clipboardCondition(aExpected) {
+      return () => aExpected == SpecialPowers.getClipboardData("text/unicode");
+    }
+
+    // Value set by browser -- should copy entire url (w/ scheme) on full select
+
+    urlbar.focus();
+    urlbar.select();
+    CommandUpdater.doCommand("cmd_copy");
+
+    let copy = yield waitForCondition(clipboardCondition("http://example.com/"));
+    ok(copy, "should copy entire url (w/ scheme) on full select");
+
+    // Value set by browser -- should copy selected text on partial select
+
+    urlbar.focus();
+    urlbar.select();
+    urlbar.selectionStart = 2;
+    CommandUpdater.doCommand("cmd_copy");
+
+    copy = yield waitForCondition(clipboardCondition("ample.com"));
+    ok(copy, "should copy selected text on partial select");
+
+    // Value set by user -- should not copy full string
+
+    urlbar.valueIsTyped = true;
+    urlbar.focus();
+    urlbar.select();
+    CommandUpdater.doCommand("cmd_copy");
+
+    copy = yield waitForCondition(clipboardCondition("example.com"));
+    ok(copy, "should not copy full string");
+  }
+});
\ No newline at end of file
--- a/browser/themes/linux/preferences/in-content/preferences.css
+++ b/browser/themes/linux/preferences/in-content/preferences.css
@@ -44,21 +44,16 @@
   margin: 0 20px;
 }
 
 .preference-icon[type="general"],
 .landingButton-icon[type="general"] {
   background-position: 0 0;
 }
 
-.preference-icon[type="tabs"],
-.landingButton-icon[type="tabs"] {
-  background-position: -32px 0;
-}
-
 .preference-icon[type="content"],
 .landingButton-icon[type="content"] {
   background-position: -64px 0;
 }
 
 .preference-icon[type="applications"],
 .landingButton-icon[type="applications"] {
   background-position: -96px 0;
--- a/browser/themes/osx/preferences/in-content/preferences.css
+++ b/browser/themes/osx/preferences/in-content/preferences.css
@@ -47,21 +47,16 @@
   margin: 0 20px;
 }
 
 .preference-icon[type="general"],
 .landingButton-icon[type="general"] {
   background-position: 0 0;
 }
 
-.preference-icon[type="tabs"],
-.landingButton-icon[type="tabs"] {
-  background-position: -32px 0;
-}
-
 .preference-icon[type="content"],
 .landingButton-icon[type="content"] {
   background-position: -64px 0;
 }
 
 .preference-icon[type="applications"],
 .landingButton-icon[type="applications"] {
   background-position: -96px 0;
--- a/browser/themes/windows/preferences/in-content/preferences.css
+++ b/browser/themes/windows/preferences/in-content/preferences.css
@@ -47,21 +47,16 @@
   margin: 0 20px;
 }
 
 .preference-icon[type="general"],
 .landingButton-icon[type="general"] {
   background-position: 0 0;
 }
 
-.preference-icon[type="tabs"],
-.landingButton-icon[type="tabs"] {
-  background-position: -32px 0;
-}
-
 .preference-icon[type="content"],
 .landingButton-icon[type="content"] {
   background-position: -64px 0;
 }
 
 .preference-icon[type="applications"],
 .landingButton-icon[type="applications"] {
   background-position: -96px 0;
--- a/dom/system/gonk/NetworkManager.js
+++ b/dom/system/gonk/NetworkManager.js
@@ -601,17 +601,17 @@ NetworkManager.prototype = {
       cmd: "setDefaultRouteAndDNS",
       ifname: this.active.name,
       oldIfname: (oldInterface && oldInterface != this.active) ? oldInterface.name : null,
       gateway_str: this.active.gateway,
       dns1_str: this.active.dns1,
       dns2_str: this.active.dns2
     };
     this.worker.postMessage(options);
-    this.setNetworkProxy();
+    this.setNetworkProxy(this.active);
   },
 
   removeDefaultRoute: function removeDefaultRoute(ifname) {
     debug("Remove default route for " + ifname);
     let options = {
       cmd: "removeDefaultRoute",
       ifname: ifname
     }
@@ -684,43 +684,43 @@ NetworkManager.prototype = {
       cmd: "removeHostRoute",
       ifname: network.name,
       gateway: network.gateway,
       hostnames: hosts
     };
     this.worker.postMessage(options);
   },
 
-  setNetworkProxy: function setNetworkProxy() {
+  setNetworkProxy: function setNetworkProxy(network) {
     try {
-      if (!this.active.httpProxyHost || this.active.httpProxyHost == "") {
+      if (!network.httpProxyHost || network.httpProxyHost == "") {
         // Sets direct connection to internet.
         Services.prefs.clearUserPref("network.proxy.type");
         Services.prefs.clearUserPref("network.proxy.share_proxy_settings");
         Services.prefs.clearUserPref("network.proxy.http");
         Services.prefs.clearUserPref("network.proxy.http_port");
         Services.prefs.clearUserPref("network.proxy.ssl");
         Services.prefs.clearUserPref("network.proxy.ssl_port");
-        debug("No proxy support for " + this.active.name + " network interface.");
+        debug("No proxy support for " + network.name + " network interface.");
         return;
       }
 
-      debug("Going to set proxy settings for " + this.active.name + " network interface.");
+      debug("Going to set proxy settings for " + network.name + " network interface.");
       // Sets manual proxy configuration.
       Services.prefs.setIntPref("network.proxy.type", MANUAL_PROXY_CONFIGURATION);
       // Do not use this proxy server for all protocols.
       Services.prefs.setBoolPref("network.proxy.share_proxy_settings", false);
-      Services.prefs.setCharPref("network.proxy.http", this.active.httpProxyHost);
-      Services.prefs.setCharPref("network.proxy.ssl", this.active.httpProxyHost);
-      let port = this.active.httpProxyPort == "" ? 8080 : this.active.httpProxyPort;
+      Services.prefs.setCharPref("network.proxy.http", network.httpProxyHost);
+      Services.prefs.setCharPref("network.proxy.ssl", network.httpProxyHost);
+      let port = network.httpProxyPort == 0 ? 8080 : network.httpProxyPort;
       Services.prefs.setIntPref("network.proxy.http_port", port);
       Services.prefs.setIntPref("network.proxy.ssl_port", port);
     } catch (ex) {
        debug("Exception " + ex + ". Unable to set proxy setting for "
-             + this.active.name + " network interface.");
+             + network.name + " network interface.");
        return;
     }
   },
 
   // nsISettingsServiceCallback
 
   tetheringSettings: {},
 
--- a/dom/system/gonk/nsINetworkManager.idl
+++ b/dom/system/gonk/nsINetworkManager.idl
@@ -114,17 +114,17 @@ interface nsIWifiOperationModeCallback :
    *        or `null` if it was.
    */
   void wifiOperationModeResult(in jsval error);
 };
 
 /**
  * Manage network interfaces.
  */
-[scriptable, uuid(5b22c620-f8b9-11e2-b778-0800200c9a66)]
+[scriptable, uuid(fad3fb08-664f-48e3-bba3-423186988c61)]
 interface nsINetworkManager : nsISupports
 {
   /**
    * Register the given network interface with the network manager.
    *
    * Consumers will be notified with the 'network-interface-registered'
    * observer notification.
    *
@@ -226,9 +226,17 @@ interface nsINetworkManager : nsISupport
    *        AP  - Access pointer mode.
    *        P2P - Peer to peer connection mode.
    *        STA - Station mode.
    *
    * @param callback
    *        Callback to notify Wifi firmware reload result.
    */
   void setWifiOperationMode(in DOMString interfaceName, in DOMString mode, in nsIWifiOperationModeCallback callback);
+
+  /**
+   * Set http proxy for specific network
+   *
+   * @param network
+   *        Network interface to register.
+   */
+  void setNetworkProxy(in nsINetworkInterface network);
 };
--- a/dom/wifi/DOMWifiManager.js
+++ b/dom/wifi/DOMWifiManager.js
@@ -82,16 +82,17 @@ DOMWifiManager.prototype = {
     this._lastConnectionInfo = null;
 
     const messages = ["WifiManager:getNetworks:Return:OK", "WifiManager:getNetworks:Return:NO",
                       "WifiManager:getKnownNetworks:Return:OK", "WifiManager:getKnownNetworks:Return:NO",
                       "WifiManager:associate:Return:OK", "WifiManager:associate:Return:NO",
                       "WifiManager:forget:Return:OK", "WifiManager:forget:Return:NO",
                       "WifiManager:wps:Return:OK", "WifiManager:wps:Return:NO",
                       "WifiManager:setPowerSavingMode:Return:OK", "WifiManager:setPowerSavingMode:Return:NO",
+                      "WifiManager:setHttpProxy:Return:OK", "WifiManager:setHttpProxy:Return:NO",
                       "WifiManager:setStaticIpMode:Return:OK", "WifiManager:setStaticIpMode:Return:NO",
                       "WifiManager:wifiDown", "WifiManager:wifiUp",
                       "WifiManager:onconnecting", "WifiManager:onassociate",
                       "WifiManager:onconnect", "WifiManager:ondisconnect",
                       "WifiManager:onwpstimeout", "WifiManager:onwpsfail",
                       "WifiManager:onwpsoverlap", "WifiManager:connectionInfoUpdate",
                       "WifiManager:onconnectingfailed"];
     this.initDOMRequestHelper(aWindow, messages);
@@ -189,16 +190,26 @@ DOMWifiManager.prototype = {
         Services.DOMRequest.fireSuccess(request, exposeReadOnly(msg.data));
         break;
 
       case "WifiManager:setPowerSavingMode:Return:NO":
         request = this.takeRequest(msg.rid);
         Services.DOMRequest.fireError(request, msg.data);
         break;
 
+      case "WifiManager:setHttpProxy:Return:OK":
+        request = this.takeRequest(msg.rid);
+        Services.DOMRequest.fireSuccess(request, exposeReadOnly(msg.data));
+        break;
+
+      case "WifiManager:setHttpProxy:Return:NO":
+        request = this.takeRequest(msg.rid);
+        Services.DOMRequest.fireError(request, msg.data);
+        break;
+
       case "WifiManager:setStaticIpMode:Return:OK":
         request = this.takeRequest(msg.rid);
         Services.DOMRequest.fireSuccess(request, exposeReadOnly(msg.data));
         break;
 
       case "WifiManager:setStaticIpMode:Return:NO":
         request = this.takeRequest(msg.rid);
         Services.DOMRequest.fireError(request, msg.data);
@@ -355,16 +366,24 @@ DOMWifiManager.prototype = {
   setPowerSavingMode: function nsIDOMWifiManager_setPowerSavingMode(enabled) {
     if (!this._hasPrivileges)
       throw new Components.Exception("Denied", Cr.NS_ERROR_FAILURE);
     var request = this.createRequest();
     this._sendMessageForRequest("WifiManager:setPowerSavingMode", enabled, request);
     return request;
   },
 
+  setHttpProxy: function nsIDOMWifiManager_setHttpProxy(network, info) {
+    if (!this._hasPrivileges)
+      throw new Components.Exception("Denied", Cr.NS_ERROR_FAILURE);
+    var request = this.createRequest();
+    this._sendMessageForRequest("WifiManager:setHttpProxy", {network:network, info:info}, request);
+    return request;
+  },
+
   setStaticIpMode: function nsIDOMWifiManager_setStaticIpMode(network, info) {
     if (!this._hasPrivileges)
       throw new Components.Exception("Denied", Cr.NS_ERROR_FAILURE);
     var request = this.createRequest();
     this._sendMessageForRequest("WifiManager:setStaticIpMode", {network: network,info: info}, request);
     return request;
   },
 
--- a/dom/wifi/WifiWorker.js
+++ b/dom/wifi/WifiWorker.js
@@ -642,16 +642,58 @@ var WifiManager = (function() {
   }
 
   function resetConnections(ifname, callback) {
     controlMessage({ cmd: "ifc_reset_connections", ifname: ifname }, function(data) {
       callback(!data.status);
     });
   }
 
+  var httpProxyConfig = Object.create(null);
+
+  /**
+   * Given a network, configure http proxy when using wifi.
+   * @param network A network object to update http proxy
+   * @param info Info should have following field:
+   *        - httpProxyHost ip address of http proxy.
+   *        - httpProxyPort port of http proxy, set 0 to use default port 8080.
+   * @param callback callback function.
+   */
+  function configureHttpProxy(network, info, callback) {
+    if (!network)
+      return;
+
+    let networkKey = getNetworkKey(network);
+
+    if (!info || info.httpProxyHost === "") {
+      delete httpProxyConfig[networkKey];
+    } else {
+      httpProxyConfig[networkKey] = network;
+      httpProxyConfig[networkKey].httpProxyHost = info.httpProxyHost;
+      httpProxyConfig[networkKey].httpProxyPort = info.httpProxyPort;
+    }
+
+    callback(true);
+  }
+
+  function getHttpProxyNetwork(network) {
+    if (!network)
+      return null;
+
+    let networkKey = getNetworkKey(network);
+    return ((networkKey in httpProxyConfig) ? httpProxyConfig : null);
+  }
+
+  function setHttpProxy(network) {
+    if (!network)
+      return;
+
+    gNetworkManager.setNetworkProxy(network);
+  }
+
   var staticIpConfig = Object.create(null);
   function setStaticIpMode(network, info, callback) {
     let setNetworkKey = getNetworkKey(network);
     let curNetworkKey = null;
     let currentNetwork = Object.create(null);
     currentNetwork.netId = manager.connectionInfo.id;
 
     manager.getNetworkConfiguration(currentNetwork, function (){
@@ -1543,16 +1585,19 @@ var WifiManager = (function() {
   manager.setBackgroundScan = setBackgroundScan;
   manager.scan = scanCommand;
   manager.wpsPbc = wpsPbcCommand;
   manager.wpsPin = wpsPinCommand;
   manager.wpsCancel = wpsCancelCommand;
   manager.setPowerMode = (sdkVersion >= 16)
                          ? setPowerModeCommandJB
                          : setPowerModeCommandICS;
+  manager.getHttpProxyNetwork = getHttpProxyNetwork;
+  manager.setHttpProxy = setHttpProxy;
+  manager.configureHttpProxy = configureHttpProxy;
   manager.setSuspendOptimizations = setSuspendOptimizationsCommand;
   manager.setStaticIpMode = setStaticIpMode;
   manager.getRssiApprox = getRssiApproxCommand;
   manager.getLinkSpeed = getLinkSpeedCommand;
   manager.getDhcpInfo = function() { return dhcpInfo; }
   manager.getConnectionInfo = (sdkVersion >= 15)
                               ? getConnectionInfoICS
                               : getConnectionInfoGB;
@@ -1846,16 +1891,17 @@ function WifiWorker() {
   var self = this;
 
   this._mm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
                .getService(Ci.nsIMessageListenerManager);
   const messages = ["WifiManager:getNetworks", "WifiManager:getKnownNetworks",
                     "WifiManager:associate", "WifiManager:forget",
                     "WifiManager:wps", "WifiManager:getState",
                     "WifiManager:setPowerSavingMode",
+                    "WifiManager:setHttpProxy",
                     "WifiManager:setStaticIpMode",
                     "child-process-shutdown"];
 
   messages.forEach((function(msgName) {
     this._mm.addMessageListener(msgName, this);
   }).bind(this));
 
   Services.obs.addObserver(this, kMozSettingsChangedObserverTopic, false);
@@ -2155,16 +2201,21 @@ function WifiWorker() {
           // In this case, we connected to an already-connected wpa_supplicant,
           // because of that we need to gather information about the current
           // network here.
           self.currentNetwork = { ssid: quote(WifiManager.connectionInfo.ssid),
                                   netId: WifiManager.connectionInfo.id };
           WifiManager.getNetworkConfiguration(self.currentNetwork, function(){});
         }
 
+        // Update http proxy when connected to network.
+        let netConnect = WifiManager.getHttpProxyNetwork(self.currentNetwork);
+        if (netConnect)
+          WifiManager.setHttpProxy(netConnect);
+
         // The full authentication process is completed, reset the count.
         WifiManager.authenticationFailuresCount = 0;
         WifiManager.loopDetectionCount = 0;
         self._startConnectionInfoTimer();
         self._fireEvent("onassociate", { network: netToDOM(self.currentNetwork) });
         break;
       case "CONNECTED":
         // BSSID is read after connected, update it.
@@ -2177,16 +2228,33 @@ function WifiWorker() {
           this.prevState === "DISCONNECTED" ||
           this.prevState === "INTERFACE_DISABLED" ||
           this.prevState === "INACTIVE" ||
           this.prevState === "UNINITIALIZED") {
           return;
         }
 
         self._fireEvent("ondisconnect", {});
+
+        // When disconnected, clear the http proxy setting if it exists.
+        // Temporarily set http proxy to empty and restore user setting after setHttpProxy.
+        let netDisconnect = WifiManager.getHttpProxyNetwork(self.currentNetwork);
+        if (netDisconnect) {
+          let prehttpProxyHostSetting = netDisconnect.httpProxyHost;
+          let prehttpProxyPortSetting = netDisconnect.httpProxyPort;
+
+          netDisconnect.httpProxyHost = "";
+          netDisconnect.httpProxyPort = 0;
+
+          WifiManager.setHttpProxy(netDisconnect);
+
+          netDisconnect.httpProxyHost = prehttpProxyHostSetting;
+          netDisconnect.httpProxyPort = prehttpProxyPortSetting;
+        }
+
         self.currentNetwork = null;
         self.ipAddress = "";
 
         if (self._turnOnBackgroundScan) {
           self._turnOnBackgroundScan = false;
           WifiManager.setBackgroundScan("ON", function(did_something, ok) {
             WifiManager.reassociate(function() {});
           });
@@ -2711,16 +2779,19 @@ WifiWorker.prototype = {
         this.forget(msg);
         break;
       case "WifiManager:wps":
         this.wps(msg);
         break;
       case "WifiManager:setPowerSavingMode":
         this.setPowerSavingMode(msg);
         break;
+      case "WifiManager:setHttpProxy":
+        this.setHttpProxy(msg);
+        break;
       case "WifiManager:setStaticIpMode":
         this.setStaticIpMode(msg);
         break;
       case "WifiManager:getState": {
         let i;
         if ((i = this._domManagers.indexOf(msg.manager)) === -1) {
           this._domManagers.push(msg.manager);
         }
@@ -3205,16 +3276,40 @@ WifiWorker.prototype = {
           self._sendMessage(message, true, true, msg);
         } else {
           self._sendMessage(message, false, "Set power saving mode failed", msg);
         }
       });
     });
   },
 
+  setHttpProxy: function(msg) {
+    const message = "WifiManager:setHttpProxy:Return";
+    let self = this;
+    let network = msg.data.network;
+    let info = msg.data.info;
+
+    netFromDOM(network, null);
+
+    WifiManager.configureHttpProxy(network, info, function(ok) {
+      if (ok) {
+        // If configured network is current connected network
+        // need update http proxy immediately.
+        let setNetworkKey = getNetworkKey(network);
+        let curNetworkKey = self.currentNetwork ? getNetworkKey(self.currentNetwork) : null;
+        if (setNetworkKey === curNetworkKey)
+          WifiManager.setHttpProxy(network);
+
+        self._sendMessage(message, true, true, msg);
+      } else {
+        self._sendMessage(message, false, "Set http proxy failed", msg);
+      }
+    });
+  },
+
   setStaticIpMode: function(msg) {
     const message = "WifiManager:setStaticMode:Return";
     let self = this;
     let network = msg.data.network;
     let info = msg.data.info;
 
     netFromDOM(network, null);
 
--- a/dom/wifi/nsIWifi.idl
+++ b/dom/wifi/nsIWifi.idl
@@ -54,17 +54,17 @@ interface nsIWifi : nsISupports
      * currently configured networks.
      *
      * On success a callback is notified with the list of networks.
      * On failure after 3 scan retry attempts a callback is notified of failure.
      */
     void getWifiScanResults(in nsIWifiScanResultsReady callback);
 };
 
-[scriptable, uuid(3f21012d-6e75-4632-b87c-acdd7c57fbf3)]
+[scriptable, uuid(e5a72295-1c5f-4848-9cbb-f1d3785c16c1)]
 interface nsIDOMWifiManager : nsISupports
 {
     /**
      * Returns the list of currently available networks.
      * onsuccess: We have obtained the current list of networks. request.value
      *            is an object whose property names are SSIDs and values are
      *            network objects.
      * onerror: We were unable to obtain a list of property names.
@@ -141,16 +141,29 @@ interface nsIDOMWifiManager : nsISupport
      *        - dns2 configured seconf DNS server address
      * onsuccess: We have successfully configure the static ip mode.
      * onerror: We have failed to configure the static ip mode.
      */
     nsIDOMDOMRequest setStaticIpMode(in jsval network,
                                      in jsval info);
 
     /**
+     * Given a network, configure http proxy when using wifi.
+     * @param network A network object with the SSID of the network to set http proxy.
+     * @param info info should have following field:
+     *        - httpProxyHost ip address of http proxy.
+     *        - httpProxyPort port of http proxy, set 0 to use default port 8080.
+     *        set info to null to clear http proxy.
+     * onsuccess: We have successfully configure http proxy.
+     * onerror: We have failed to configure http proxy.
+     */
+    nsIDOMDOMRequest setHttpProxy(in jsval network,
+                                  in jsval info);
+
+    /**
      * Returns whether or not wifi is currently enabled.
      */
     readonly attribute boolean enabled;
 
     /**
      * Returns the MAC address of the wifi adapter.
      */
     readonly attribute DOMString macAddress;
--- a/mobile/android/base/tests/AboutHomeTest.java.in
+++ b/mobile/android/base/tests/AboutHomeTest.java.in
@@ -1,11 +1,12 @@
 #filter substitution
 package @ANDROID_PACKAGE_NAME@.tests;
 
+import com.jayway.android.robotium.solo.Condition;
 import @ANDROID_PACKAGE_NAME@.*;
 
 import android.content.ContentResolver;
 import android.database.Cursor;
 import android.net.Uri;
 import android.support.v4.view.ViewPager;
 import android.text.TextUtils;
 import android.view.View;
@@ -74,36 +75,35 @@ abstract class AboutHomeTest extends Bas
         uri = uri.buildUpon().appendQueryParameter("profile", "default")
                              .appendQueryParameter("sync", "true").build();
         return uri;
     }
 
     /**
      * Waits for the given ListView to have a non-empty adapter.
      *
-     * This method will fail if the given ListView or its adapter are null.
+     * This method will return false if the given ListView or its adapter are null.
      */
     protected boolean waitForListToLoad(final ListView listView) {
-        boolean result = waitForTest(new BooleanTest() {
+        Condition listWaitCondition = new Condition() {
             @Override
-            public boolean test() {
+            public boolean isSatisfied() {
                 if (listView == null) {
                     return false;
                 }
 
                 final ListAdapter adapter = listView.getAdapter();
                 if (adapter == null) {
                     return false;
                 }
 
                 return (adapter.getCount() > 0);
             }
-        }, MAX_WAIT_MS);
-
-        return result;
+        };
+        return waitForCondition(listWaitCondition, MAX_WAIT_MS);
     }
 
     /**
      * Get an active ListView with the specified tag .
      *
      * This method uses the predefined tags in HomePager.
      */
     protected final ListView findListViewWithTag(String tag) {
@@ -326,19 +326,19 @@ abstract class AboutHomeTest extends Bas
             clickOnButton("Cancel");
             waitForText("about:home");
             return false;
         }
     }
 
    // A wait in order for the about:home tab to be rendered after drag/tab selection
     private void waitForAboutHomeTab(final int tabIndex) {
-        boolean correctTab = waitForTest(new BooleanTest() {
+        boolean correctTab = waitForCondition(new Condition() {
             @Override
-            public boolean test() {
+            public boolean isSatisfied() {
               ViewPager pager = (ViewPager)mSolo.getView(ViewPager.class, 0);
               return (pager.getCurrentItem() == tabIndex);
             }
         }, MAX_WAIT_MS);
         mAsserter.ok(correctTab, "Checking that the correct tab is displayed", "The " + aboutHomeTabs.get(tabIndex) + " tab is displayed");
     }
  
     /**
--- a/mobile/android/base/tests/BaseTest.java.in
+++ b/mobile/android/base/tests/BaseTest.java.in
@@ -1,11 +1,12 @@
 #filter substitution
 package @ANDROID_PACKAGE_NAME@.tests;
 
+import com.jayway.android.robotium.solo.Condition;
 import com.jayway.android.robotium.solo.Solo;
 import @ANDROID_PACKAGE_NAME@.*;
 
 import android.app.Activity;
 import android.app.Instrumentation;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.ContentUris;
@@ -191,22 +192,23 @@ abstract class BaseTest extends Activity
         mAsserter.ok(value.matches(regex), name, "Expected /" + regex +"/, got \"" + value + "\"");
     }
 
     /**
      * Click on the URL bar to focus it and enter editing mode.
      */
     protected final void focusUrlBar() {
         // Click on the browser toolbar to enter editing mode
-        mDriver.findElement(mActivity, BROWSER_TOOLBAR_ID).click();
+        final View toolbarView = mSolo.getView(BROWSER_TOOLBAR_ID);
+        mSolo.clickOnView(toolbarView);
 
         // Wait for highlighed text to gain focus
-        boolean success = waitForTest(new BooleanTest() {
+        boolean success = waitForCondition(new Condition() {
             @Override
-            public boolean test() {
+            public boolean isSatisfied() {
                 EditText urlEditText = mSolo.getEditText(0);
                 if (urlEditText.isInputMethodTarget()) {
                     return true;
                 } else {
                     mSolo.clickOnEditText(0);
                     return false;
                 }
             }
@@ -217,17 +219,17 @@ abstract class BaseTest extends Activity
 
     protected final void enterUrl(String url) {
         focusUrlBar();
 
         // Send the keys for the URL we want to enter
         mActions.sendKeys(url);
 
         // Get the URL text from the URL bar EditText view
-        String urlBarText = mDriver.findElement(mActivity, URL_EDIT_TEXT_ID).getText();
+        String urlBarText = ((EditText) mSolo.getView(URL_EDIT_TEXT_ID)).getText().toString();
         mAsserter.is(url, urlBarText, "URL typed properly");
     }
 
     protected final Fragment getBrowserSearch() {
         final FragmentManager fm = ((FragmentActivity) getActivity()).getSupportFragmentManager();
         return fm.findFragmentByTag("browser_search");
     }
 
@@ -267,68 +269,83 @@ abstract class BaseTest extends Activity
             loadUrl.invoke(tabs, new Object[] { url });
         } catch (Exception e) {
             mAsserter.dumpLog("Exception in loadUrl", e);
             throw new RuntimeException(e);
         }
     }
 
     public final void verifyUrl(String url) {
-        Element urlEditText = mDriver.findElement(mActivity, URL_EDIT_TEXT_ID);
+        final EditText urlEditText = (EditText) mSolo.getView(URL_EDIT_TEXT_ID);
         String urlBarText = null;
         if (urlEditText != null) {
             // wait for a short time for the expected text, in case there is a delay
             // in updating the view
-            waitForTest(new VerifyUrlTest(urlEditText, url), VERIFY_URL_TIMEOUT);
-            urlBarText = urlEditText.getText();
+            waitForCondition(new VerifyTextViewText(urlEditText, url), VERIFY_URL_TIMEOUT);
+            urlBarText = urlEditText.getText().toString();
+
         }
         mAsserter.is(urlBarText, url, "Browser toolbar URL stayed the same");
     }
 
-    class VerifyUrlTest implements BooleanTest {
-        private Element mUrlEditText;
-        private String mUrl;
-        public VerifyUrlTest(Element urlEditText, String url) {
-            mUrlEditText = urlEditText;
-            mUrl = url;
+    class VerifyTextViewText implements Condition {
+        private TextView mTextView;
+        private String mExpected;
+        public VerifyTextViewText(TextView textView, String expected) {
+            mTextView = textView;
+            mExpected = expected;
         }
 
         @Override
-        public boolean test() {
-            String urlbarText = mUrlEditText.getText();
-            if (urlbarText.equals(mUrl)) {
-                return true;
-            }
-            return false;
+        public boolean isSatisfied() {
+            String textValue = mTextView.getText().toString();
+            return mExpected.equals(textValue);
         }
     }
 
     protected final String getAbsoluteUrl(String url) {
         return mBaseUrl + "/" + url.replaceAll("(^/)", "");
     }
 
     protected final String getAbsoluteRawUrl(String url) {
         return mRawBaseUrl + "/" + url.replaceAll("(^/)", "");
     }
 
+    /*
+     * Wrapper method for mSolo.waitForCondition with additional logging.
+     */
+    protected final boolean waitForCondition(Condition condition, int timeout) {
+        boolean result = mSolo.waitForCondition(condition, timeout);
+        if (!result) {
+            // Log timeout failure for diagnostic purposes only; a failed wait may
+            // be normal and does not necessarily warrant a test asssertion/failure.
+            mAsserter.dumpLog("waitForCondition timeout after " + timeout + " ms.");
+        }
+        return result;
+    }
+
+    // TODO: With Robotium 4.2, we should use Condition and waitForCondition instead.
+    // Future boolean tests should not use this method.
     protected final boolean waitForTest(BooleanTest t, int timeout) {
         long end = SystemClock.uptimeMillis() + timeout;
         while (SystemClock.uptimeMillis() < end) {
             if (t.test()) {
                 return true;
             }
             mSolo.sleep(100);
         }
         // log out wait failure for diagnostic purposes only;
         // a failed wait may be normal and does not necessarily
         // warrant a test assertion/failure
         mAsserter.dumpLog("waitForTest timeout after "+timeout+" ms");
         return false;
     }
 
+    // TODO: With Robotium 4.2, we should use Condition and waitForCondition instead.
+    // Future boolean tests should not implement this interface.
     protected interface BooleanTest {
         public boolean test();
     }
 
     @SuppressWarnings({"unchecked", "non-varargs"})
     public void SqliteCompare(String dbName, String sqlCommand, ContentValues[] cvs) {
         File profile = new File(mProfile);
         String dbPath = new File(profile, dbName).getPath();
@@ -408,19 +425,19 @@ abstract class BaseTest extends Activity
         return rc;
     }
 
     /**
      * Wait for <text> to be visible and also be enabled/clickable.
      */
     public boolean waitForEnabledText(String text) {
         final String testText = text;
-        boolean rc = waitForTest(new BooleanTest() {
+        boolean rc = waitForCondition(new Condition() {
             @Override
-            public boolean test() {
+            public boolean isSatisfied() {
                 // Solo.getText() could be used here, except that it sometimes
                 // hits an assertion when the requested text is not found.
                 ArrayList<View> views = mSolo.getCurrentViews();
                 for (View view : views) {
                     if (view instanceof TextView) {
                         TextView tv = (TextView)view;
                         String viewText = tv.getText().toString();
                         if (tv.isEnabled() && viewText != null && viewText.matches(testText)) {
@@ -480,61 +497,42 @@ abstract class BaseTest extends Activity
                 mSolo.clickOnText("(^More$|^Tools$)");
             }
             waitForText(itemName);
             mSolo.clickOnText(itemName);
         }
     }
 
     public final void verifyHomePagerHidden() {
-        final Element homePagerElement = mDriver.findElement(mActivity, "home_pager");
-        final View homePagerView = mActivity.findViewById(homePagerElement.getId());
+        final View homePagerView = mSolo.getView("home_pager");
 
-        boolean rc = waitForTest(new BooleanTest() {
+        boolean rc = waitForCondition(new Condition() {
             @Override
-            public boolean test() {
+            public boolean isSatisfied() {
                 return homePagerView.getVisibility() != View.VISIBLE;
             }
         }, MAX_WAIT_HOME_PAGER_HIDDEN_MS);
 
         if (!rc) {
             mAsserter.ok(rc, "Verify HomePager is hidden", "HomePager is hidden");
         }
     }
 
     public final void verifyPageTitle(String title) {
-        Element urlBarTitle = mDriver.findElement(getActivity(), URL_BAR_TITLE_ID);
+        final TextView urlBarTitle = (TextView) mSolo.getView(URL_BAR_TITLE_ID);
         String pageTitle = null;
         if (urlBarTitle != null) {
             // Wait for the title to make sure it has been displayed in case the view
             // does not update fast enough
-            waitForTest(new VerifyTitle(urlBarTitle, title), MAX_WAIT_MS);
-            pageTitle = urlBarTitle.getText();
+            waitForCondition(new VerifyTextViewText(urlBarTitle, title), MAX_WAIT_MS);
+            pageTitle = urlBarTitle.getText().toString();
         }
         mAsserter.is(pageTitle, title, "Page title is correct");
     }
 
-    class VerifyTitle implements BooleanTest {
-        private Element mUrlBarTitle;
-        private String mTitle;
-        public VerifyTitle(Element urlBarTitle, String title) {
-            mUrlBarTitle = urlBarTitle;
-            mTitle = title;
-        }
-
-        @Override
-        public boolean test() {
-            String pageTitle = mUrlBarTitle.getText();
-            if (pageTitle.equals(mTitle)) {
-                return true;
-            }
-            return false;
-        }
-    }
-
     public final void verifyTabCount(int expectedTabCount) {
         Activity activity = getActivity();
         Element tabCount = mDriver.findElement(activity, "tabs_counter");
         String tabCountText = tabCount.getText();
         int tabCountInt = Integer.parseInt(tabCountText);
         mAsserter.is(tabCountInt, expectedTabCount, "The correct number of tabs are opened");
     }
 
@@ -555,36 +553,31 @@ abstract class BaseTest extends Activity
 
     // Used to hide/show the virtual keyboard
     public void toggleVKB() {
         InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Activity.INPUT_METHOD_SERVICE);
         imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0);
     }
 
     public void addTab(String url) {
-        Element tabs = null;
-        Element addTab = null;
-        Activity activity = getActivity();
-        tabs = mDriver.findElement(activity, "tabs");
-        addTab = mDriver.findElement(activity, "add_tab");
-        final int addTabId = addTab.getId();
-        mAsserter.ok(tabs.click(), "checking that tabs clicked", "tabs element clicked");
+        mSolo.clickOnView(mSolo.getView("tabs"));
         // wait for addTab to appear (this is usually immediate)
-        boolean success = waitForTest(new BooleanTest() {
+        boolean success = waitForCondition(new Condition() {
             @Override
-            public boolean test() {
-                View addTabView = getActivity().findViewById(addTabId);
+            public boolean isSatisfied() {
+                View addTabView = mSolo.getView("add_tab");
                 if (addTabView == null) {
                     return false;
                 }
                 return true;
             }
         }, MAX_WAIT_MS);
         mAsserter.ok(success, "waiting for add tab view", "add tab view available");
-        mAsserter.ok(addTab.click(), "checking that add_tab clicked", "add_tab element clicked");
+        final View addTabView = mSolo.getView("add_tab");
+        mSolo.clickOnView(mSolo.getView("add_tab"));
 
         // Adding a new tab opens about:home, so now we just need to load the url in it.
         inputAndLoadUrl(url);
     }
 
     public final void runOnUiThreadSync(Runnable runnable) {
         RobocopUtils.runOnUiThreadSync(mActivity, runnable);
     }
--- a/toolkit/components/social/FrameWorker.jsm
+++ b/toolkit/components/social/FrameWorker.jsm
@@ -77,27 +77,16 @@ function _Worker(browserPromise, options
     mm.loadFrameScript("resource://gre/modules/FrameWorkerContent.js", true);
     mm.sendAsyncMessage("frameworker:init", this.options);
     mm.addMessageListener("frameworker:port-message", this);
     mm.addMessageListener("frameworker:notify-worker-error", this);
   });
 }
 
 _Worker.prototype = {
-  reload: function() {
-    // In the future, it would be nice to just throw away the browser element
-    // and re-create from scratch.  The complication there would be the need
-    // to reconnect existing ports - but even that might be managable.
-    // However, bug 899908 calls for 'reload' to be dropped, so let's do that
-    // instead!
-    this.browserPromise.then(browser => {
-      browser.messageManager.sendAsyncMessage("frameworker:reload");
-    });
-  },
-
   // Message handler.
   receiveMessage: function(msg) {
     switch (msg.name) {
       case "frameworker:port-message":
         let port = this.ports.get(msg.data.portId);
         port._onmessage(msg.data.data);
         break;
       case "frameworker:notify-worker-error":
--- a/toolkit/components/social/FrameWorkerContent.js
+++ b/toolkit/components/social/FrameWorkerContent.js
@@ -40,17 +40,16 @@ function navigate(url) {
  * instead run in a sandbox that has a select set of methods cloned from the
  * URL's domain.
  */
 function FrameWorker(url, name, origin, exposeLocalStorage) {
   this.url = url;
   this.name = name || url;
   this.ports = new Map(); // all unclosed ports, including ones yet to be entangled
   this.loaded = false;
-  this.reloading = false;
   this.origin = origin;
   this._injectController = null;
   this.exposeLocalStorage = exposeLocalStorage;
 
   this.load();
 }
 
 FrameWorker.prototype = {
@@ -73,32 +72,16 @@ FrameWorker.prototype = {
 
   _maybeRemoveInjectController: function() {
     if (this._injectController) {
       Services.obs.removeObserver(this._injectController, "document-element-inserted");
       this._injectController = null;
     }
   },
 
-  reload: function FrameWorker_reloadWorker() {
-    // reset all ports as not entangled; they will then be re-entangled during
-    // the call to createSandbox after the document is reloaded
-    for (let [, port] of this.ports) {
-      port._entangled = false;
-    }
-    // Mark the provider as unloaded now, so that any new ports created after
-    // this point but before the unload has fired are properly queued up.
-    this.loaded = false;
-    // reset the content to about:blank - this will fire the unload event
-    // but not remove our browser from the DOM.  Our unload handler will
-    // see this.reloading is true and reload for us.
-    this.reloading = true;
-    navigate("about:blank");
-  },
-
   createSandbox: function createSandbox() {
     let workerWindow = content;
     let sandbox = new Cu.Sandbox(workerWindow);
 
     // copy the window apis onto the sandbox namespace only functions or
     // objects that are naturally a part of an iframe, I'm assuming they are
     // safe to import this way
     let workerAPI = ['WebSocket', 'atob', 'btoa',
@@ -241,56 +224,39 @@ FrameWorker.prototype = {
       }
     });
 
     // the 'unload' listener cleans up the worker and the sandbox.  This
     // will be triggered by the window unloading as part of shutdown or reload.
     workerWindow.addEventListener("unload", function unloadListener() {
       workerWindow.removeEventListener("unload", unloadListener);
       worker.loaded = false;
-      // If the worker is reloading, when we don't actually close any ports as
-      // they need to be re-entangled.
-      // If content is already null, we can't send a close message, so skip it.
-      if (!worker.reloading && content) {
-        for (let [, port] of worker.ports) {
-          try {
-            port.close();
-          } catch (ex) {
-            Cu.reportError("FrameWorker: failed to close port. " + ex);
-          }
-        }
-        worker.ports.clear();
-      }
-
+      // No need to close ports - the worker probably wont see a
+      // social.port-closing message and certainly isn't going to have time to
+      // do anything if it did see it.
+      worker.ports.clear();
       if (sandbox) {
         Cu.nukeSandbox(sandbox);
         sandbox = null;
       }
-      if (worker.reloading) {
-        Services.tm.mainThread.dispatch(function doReload() {
-          worker.reloading = false;
-          worker.load();
-        }, Ci.nsIThread.DISPATCH_NORMAL);
-      }
     });
   },
 };
 
 const FrameWorkerManager = {
   init: function() {
     // first, setup the docShell to disable some types of content
     docShell.allowAuth = false;
     docShell.allowPlugins = false;
     docShell.allowImages = false;
     docShell.allowMedia = false;
     docShell.allowWindowControl = false;
 
     addMessageListener("frameworker:init", this._onInit);
     addMessageListener("frameworker:connect", this._onConnect);
-    addMessageListener("frameworker:reload", this._onReload);
     addMessageListener("frameworker:port-message", this._onPortMessage);
     addMessageListener("frameworker:cookie-get", this._onCookieGet);
   },
 
   // This new frameworker is being created.  This should only be called once.
   _onInit: function(msg) {
     let {url, name, origin, exposeLocalStorage} = msg.data;
     frameworker = new FrameWorker(url, name, origin, exposeLocalStorage);
@@ -299,20 +265,16 @@ const FrameWorkerManager = {
   // A new port is being established for this frameworker.
   _onConnect: function(msg) {
     let port = new ClientPort(msg.data.portId);
     frameworker.ports.set(msg.data.portId, port);
     if (frameworker.loaded && !frameworker.reloading)
       port._createWorkerAndEntangle(frameworker);
   },
 
-  _onReload: function(msg) {
-    frameworker.reload();
-  },
-
   // A message related to a port.
   _onPortMessage: function(msg) {
     // find the "client" port for this message and have it post it into
     // the worker.
     let port = frameworker.ports.get(msg.data.portId);
     port._dopost(msg.data);
   },
 
--- a/toolkit/components/social/WorkerAPI.jsm
+++ b/toolkit/components/social/WorkerAPI.jsm
@@ -55,21 +55,17 @@ WorkerAPI.prototype = {
     },
     "social.manifest-set": function(data) {
       // the provider will get reloaded as a result of this call
       let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
       let origin = this._provider.origin;
       SocialService.updateProvider(origin, data);
     },
     "social.reload-worker": function(data) {
-      getFrameWorkerHandle(this._provider.workerURL, null)._worker.reload();
-      // the frameworker is going to be reloaded, send the initialization
-      // so it can have the same startup sequence as if it were loaded
-      // the first time.  This will be queued until the frameworker is ready.
-      this._port.postMessage({topic: "social.initialize"});
+      this._provider.reload();
     },
     "social.user-profile": function (data) {
       this._provider.updateUserProfile(data);
     },
     "social.ambient-notification": function (data) {
       this._provider.setAmbientNotification(data);
     },
     "social.cookies-get": function(data) {
--- a/toolkit/components/social/test/browser/browser_frameworker.js
+++ b/toolkit/components/social/test/browser/browser_frameworker.js
@@ -554,43 +554,16 @@ let tests = {
       cbnext();
     }, 'social:frameworker-error', false);
     worker.port.onmessage = function(e) {
       ok(false, "social:frameworker-error was handled");
       cbnext();
     }
   },
 
-  testReloadAndNewPort: function(cbnext) {
-    let run = function () {
-      onconnect = function(e) {
-        e.ports[0].postMessage({topic: "ready"});
-      }
-    }
-    let doneReload = false;
-    let worker = getFrameWorkerHandle(makeWorkerUrl(run),
-                                      undefined, "testReloadAndNewPort");
-    worker.port.onmessage = function(e) {
-      if (e.data.topic == "ready" && !doneReload) {
-        // do the "reload"
-        doneReload = true;
-        worker._worker.reload();
-        let worker2 = getFrameWorkerHandle(makeWorkerUrl(run),
-                                           undefined, "testReloadAndNewPort");
-        worker2.port.onmessage = function(e) {
-          if (e.data.topic == "ready") {
-            // "worker" and "worker2" are handles to the same worker
-            worker2.terminate();
-            cbnext();
-          }
-        }
-      }
-    }
-  },
-
   // This will create the worker, then send a message to the port, then close
   // the port - all before the worker has actually initialized.
   testCloseFirstSend: function(cbnext) {
     let run = function() {
       let numPings = 0, numCloses = 0;
       onconnect = function(e) {
         let port = e.ports[0];
         port.onmessage = function(e) {
--- a/toolkit/components/social/test/browser/browser_workerAPI.js
+++ b/toolkit/components/social/test/browser/browser_workerAPI.js
@@ -123,113 +123,51 @@ let tests = {
     port.postMessage({topic: "test-initialization"});
     port.postMessage({topic: "test.cookies-get"});
   },
 
   testWorkerReload: function(next) {
     let fw = {};
     Cu.import("resource://gre/modules/FrameWorker.jsm", fw);
 
-    // get a real handle to the worker so we can watch the unload event
-    // we watch for the unload of the worker to know it is infact being
-    // unloaded, after that if we get worker.connected we know that
-    // the worker was loaded again and ports reconnected.
-
-    // Given our <browser remote="true"> model, we have to some of this in a
-    // content script.  We turn this function into a data: URL...
-    // (and note that this code only makes sense in the context of
-    //  FrameWorkerContent.js...)
-    let contentScript = function() {
-      // the content script may be executed while there are pending messages,
-      // such as ports from the previous test being closed, which screws us up.
-      // By only doing this work on a message, we ensure previous messages have
-      // all been delivered.
-      addMessageListener("frameworker-test:ready", function onready() {
-        removeMessageListener("frameworker-test:ready", onready);
-        // first, find our test's port - it will be the last one.
-        let port, id
-        for ([id, port] of frameworker.ports) {
-          ; // nothing to do - we just want to get the last...
-        }
-        let unloadHasFired = false,
-            haveNoPendingMessagesBefore = true,
-            havePendingMessagesAfter = false;
-        content.addEventListener("unload", function workerUnload(e) {
-          content.removeEventListener("unload", workerUnload);
-          // There should be no "pending" messages with one subtle exception -
-          // there *might* be a social.initialize message - the reload process
-          // posts a message to do the reload, then posts the init message - it
-          // may or maynot have arrived here by now - but there certainly should
-          // be no other messages.
-          for (let [temp_id, temp_port] of frameworker.ports) {
-            if (temp_port._pendingMessagesOutgoing.length == 0)
-              continue;
-            if (temp_port._pendingMessagesOutgoing.length == 1 &&
-                temp_port._pendingMessagesOutgoing[0].data &&
-                JSON.parse(temp_port._pendingMessagesOutgoing[0].data).topic == "social.initialize")
-              continue;
-            // we found something we aren't expecting...
-            haveNoPendingMessagesBefore = false;
-          }
-          unloadHasFired = true; // at the end, so errors above aren't masked.
-        });
-        addEventListener("DOMWindowCreated", function workerLoaded(e) {
-          removeEventListener("DOMWindowCreated", workerLoaded);
-          // send a message which should end up pending
-          port.postMessage({topic: "test-pending-msg"});
-          for (let [temp_id, temp_port] of frameworker.ports) {
-            if (temp_port._pendingMessagesOutgoing.length >= 0) {
-              havePendingMessagesAfter = true;
-            }
-          }
-          let ok = unloadHasFired && haveNoPendingMessagesBefore && havePendingMessagesAfter;
-          sendAsyncMessage("test-result", {ok: ok});
-        });
-      });
-    };
-
-    let reloading = false;
     let worker = fw.getFrameWorkerHandle(provider.workerURL, undefined, "testWorkerReload");
     let port = provider.getWorkerPort();
-    worker._worker.browserPromise.then(browser => {
-      browser.messageManager.loadFrameScript("data:," + encodeURI(contentScript.toSource()) + "();", false);
-      browser.messageManager.sendAsyncMessage("frameworker-test:ready");
-      let seenTestResult = false;
-      browser.messageManager.addMessageListener("test-result", function _onTestResult(msg) {
-        browser.messageManager.removeMessageListener("test-result", _onTestResult);
-        ok(msg.json.ok, "test result from content-script is ok");
-        seenTestResult = true;
-      });
-
-      port.onmessage = function (e) {
+    // this observer will be fired when the worker reloads - it ensures the
+    // old port was closed and the new worker is functioning.
+    Services.obs.addObserver(function reloadObserver() {
+      Services.obs.removeObserver(reloadObserver, "social:provider-reload");
+      ok(port._closed, "old port was closed by the reload");
+      let newWorker = fw.getFrameWorkerHandle(provider.workerURL, undefined, "testWorkerReload - worker2");
+      let newPort = provider.getWorkerPort();
+      newPort.onmessage = function (e) {
         let topic = e.data.topic;
         switch (topic) {
           case "test-initialization-complete":
-            // tell the worker to send the reload msg - that will trigger the
-            // frameworker to unload and for our content script's unload
-            // handler to post a "test-pending" message, which in-turn causes
-            // the worker to send the test-pending-response.
-            // All of which goes to prove that if a message is delivered while
-            // the frameworker is unloaded due to a reload, that message still
-            // gets delivered.
-            port.postMessage({topic: "test-reload-init"});
-            break;
-          case "test-pending-response":
-            ok(e.data.data.seenInit, "worker has seen the social.initialize message");
-            // now we've been reloaded, check that our worker is still the same
-            let newWorker = fw.getFrameWorkerHandle(provider.workerURL, undefined, "testWorkerReload");
-            is(worker._worker, newWorker._worker, "worker is the same after reload");
-            ok(true, "worker reloaded and testPort was reconnected");
-            ok(seenTestResult, "saw result message from content");
+            // and we are done.
+            newPort.close();
             next();
             break;
         }
       }
-      port.postMessage({topic: "test-initialization"});
-    });
+      newPort.postMessage({topic: "test-initialization"});
+    }, "social:provider-reload", false);
+
+    port.onmessage = function (e) {
+      let topic = e.data.topic;
+      switch (topic) {
+        case "test-initialization-complete":
+          // tell the worker to send the reload msg - that will trigger the
+          // frameworker to unload and for our content script's unload
+          // handler to post a "test-result" message.
+          port.postMessage({topic: "test-reload-init"});
+          // and the "social:provider-reload" observer should fire...
+          break;
+      }
+    }
+    port.postMessage({topic: "test-initialization"});
   },
 
   testNotificationLinks: function(next) {
     let port = provider.getWorkerPort();
     let data = {
       id: 'an id',
       body: 'the text',
       action: 'link',
--- a/toolkit/components/social/test/browser/worker_social.js
+++ b/toolkit/components/social/test/browser/worker_social.js
@@ -20,33 +20,26 @@ onconnect = function(e) {
         break;
       case "test-initialization":
         testerPort = port;
         port.postMessage({topic: "test-initialization-complete"});
         break;
       case "test-profile":
         apiPort.postMessage({topic: "social.user-profile", data: data});
         break;
-      case "test-pending-msg":
-        // we also want to check we have seen a social.initialize message before
-        // this one, so send that back in the response.
-        port.postMessage({topic: "test-pending-response", data: {seenInit: !!apiPort}});
-        break;
       case "test-ambient":
         apiPort.postMessage({topic: "social.ambient-notification", data: data});
         break;
       case "test.cookies-get":
         apiPort.postMessage({topic: "social.cookies-get"});
         break;
       case "social.cookies-get-response":
         testerPort.postMessage({topic: "test.cookies-get-response", data: data});
         break;
       case "test-reload-init":
-        // browser_social_sidebar.js started test, tell the sidebar to
-        // start
         apiPort.postMessage({topic: 'social.reload-worker'});
         break;
       case "test-notification-create":
         apiPort.postMessage({topic: 'social.notification-create',
                              data: data});
         testerPort.postMessage({topic: 'did-notification-create'});
         break;
       case "test-indexeddb-create":
@@ -58,14 +51,9 @@ onconnect = function(e) {
           // Do something with request.result!
           var db = request.result;
           db.close();
           port.postMessage({topic: 'social.indexeddb-result', data: { result: "ok" }});
         };
         break;
     }
   }
-  // used for "test-reload-worker"
-  if (apiPort && apiPort != port) {
-    port.postMessage({topic: "worker.connected"})
-  }
-
 }
--- a/toolkit/components/thumbnails/BackgroundPageThumbs.jsm
+++ b/toolkit/components/thumbnails/BackgroundPageThumbs.jsm
@@ -190,16 +190,36 @@ const BackgroundPageThumbs = {
     let bwidth = Math.min(1024, swidth.value);
     // Setting the width and height attributes doesn't work -- the resulting
     // thumbnails are blank and transparent -- but setting the style does.
     browser.style.width = bwidth + "px";
     browser.style.height = (bwidth * sheight.value / swidth.value) + "px";
 
     this._parentWin.document.documentElement.appendChild(browser);
 
+    // an event that is sent if the remote process crashes - no need to remove
+    // it as we want it to be there as long as the browser itself lives.
+    browser.addEventListener("oop-browser-crashed", () => {
+      Cu.reportError("BackgroundThumbnails remote process crashed - recovering");
+      this._destroyBrowser();
+      let curCapture = this._captureQueue.length ? this._captureQueue[0] : null;
+      // we could retry the pending capture, but it's possible the crash
+      // was due directly to it, so trying again might just crash again.
+      // We could keep a flag to indicate if it previously crashed, but
+      // "resetting" the capture requires more work - so for now, we just
+      // discard it.
+      if (curCapture && curCapture.pending) {
+        curCapture._done(null);
+        // _done automatically continues queue processing.
+      }
+      // else: we must have been idle and not currently doing a capture (eg,
+      // maybe a GC or similar crashed) - so there's no need to attempt a
+      // queue restart - the next capture request will set everything up.
+    });
+
     browser.messageManager.loadFrameScript(FRAME_SCRIPT_URL, false);
     this._thumbBrowser = browser;
   },
 
   _destroyBrowser: function () {
     if (!this._thumbBrowser)
       return;
     this._thumbBrowser.remove();
@@ -221,17 +241,18 @@ const BackgroundPageThumbs = {
     this._captureQueue[0].start(this._thumbBrowser.messageManager);
     if (this._destroyBrowserTimer) {
       this._destroyBrowserTimer.cancel();
       delete this._destroyBrowserTimer;
     }
   },
 
   /**
-   * Called when the current capture completes or times out.
+   * Called when the current capture completes or fails (eg, times out, remote
+   * process crashes.)
    */
   _onCaptureOrTimeout: function (capture) {
     // Since timeouts start as an item is being processed, only the first
     // item in the queue can be passed to this method.
     if (capture !== this._captureQueue[0])
       throw new Error("The capture should be at the head of the queue.");
     this._captureQueue.shift();
     this._capturesByURL.delete(capture.url);
--- a/toolkit/components/thumbnails/test/Makefile.in
+++ b/toolkit/components/thumbnails/test/Makefile.in
@@ -17,13 +17,15 @@ MOCHITEST_BROWSER_FILES := \
 	background_red_scroll.html \
 	background_red_redirect.sjs \
 	privacy_cache_control.sjs \
 	$(NULL)
 
 ifndef RELEASE_BUILD
 MOCHITEST_BROWSER_FILES += \
 	browser_thumbnails_background.js \
+	browser_thumbnails_background_crash.js \
 	browser_thumbnails_update.js \
 	thumbnails_background.sjs \
+	thumbnails_crash_content_helper.js \
 	thumbnails_update.sjs \
 	$(NULL)
 endif
new file mode 100644
--- /dev/null
+++ b/toolkit/components/thumbnails/test/browser_thumbnails_background_crash.js
@@ -0,0 +1,162 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const TEST_PAGE_URL = "http://mochi.test:8888/browser/toolkit/components/thumbnails/test/thumbnails_background.sjs";
+const TEST_CONTENT_HELPER = "chrome://mochitests/content/browser/toolkit/components/thumbnails/test/thumbnails_crash_content_helper.js";
+
+const imports = {};
+Cu.import("resource://gre/modules/BackgroundPageThumbs.jsm", imports);
+Cu.import("resource://gre/modules/PageThumbs.jsm", imports);
+Cu.import("resource://gre/modules/Task.jsm", imports);
+Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", imports);
+
+function test() {
+  waitForExplicitFinish();
+  Services.obs.addObserver(crashObserver, 'ipc:content-shutdown', false);
+
+  spawnNextTest();
+}
+
+function spawnNextTest() {
+  if (!tests.length) {
+    Services.obs.removeObserver(crashObserver, 'ipc:content-shutdown');
+    is(numCrashes, 2, "saw 2 crashes from this test");
+    finish();
+    return;
+  }
+  let test = tests.shift();
+  info("Running sub-test " + test.name);
+  imports.Task.spawn(test).then(spawnNextTest, function onError(err) {
+    ok(false, err);
+    spawnNextTest();
+  });
+}
+
+let tests = [
+
+  function crashDuringCapture() {
+    // make a good capture first - this ensures we have the <browser>
+    let goodUrl = testPageURL();
+    yield capture(goodUrl);
+    let goodFile = fileForURL(goodUrl);
+    ok(goodFile.exists(), "Thumbnail should be cached after capture: " + goodFile.path);
+    goodFile.remove(false);
+    // inject our content script.
+    let mm = injectContentScript();
+    // queue up 2 captures - the first has a wait, so this is the one that
+    // will die.  The second one should immediately capture after the crash.
+    let waitUrl = testPageURL({ wait: 30000 });
+    let deferred1 = capture(waitUrl);
+    let deferred2 = capture(goodUrl);
+    info("Crashing the thumbnail content process.");
+    mm.sendAsyncMessage("thumbnails-test:crash");
+    yield deferred1;
+    let waitFile = fileForURL(waitUrl);
+    ok(!waitFile.exists(), "Thumbnail should not have been saved due to the crash: " + waitFile.path);
+    yield deferred2;
+    ok(goodFile.exists(), "We should have recovered and completed the 2nd capture after the crash: " + goodFile.path);
+    goodFile.remove(false);
+  },
+
+  function crashWhileIdle() {
+    // make a good capture first - this ensures we have the <browser>
+    let goodUrl = testPageURL();
+    yield capture(goodUrl);
+    let goodFile = fileForURL(goodUrl);
+    ok(goodFile.exists(), "Thumbnail should be cached after capture: " + goodFile.path);
+    goodFile.remove(false);
+    // inject our content script.
+    let mm = injectContentScript();
+    // the observer for the crashing process is basically async, so it's hard
+    // to know when the <browser> has actually seen it.  Easist is to just add
+    // our own observer.
+    let deferred = imports.Promise.defer();
+    Services.obs.addObserver(function crashObserver() {
+      Services.obs.removeObserver(crashObserver, "oop-frameloader-crashed");
+      // spin the event loop to ensure the BPT observer was called.
+      executeSoon(function() {
+        // Now queue another capture and ensure it recovers.
+        capture(goodUrl).then(() => {
+          ok(goodFile.exists(), "We should have recovered and handled new capture requests: " + goodFile.path);
+          goodFile.remove(false);
+          deferred.resolve();
+        });
+      });
+    } , "oop-frameloader-crashed", false);
+
+    // Nothing is pending - crash the process.
+    info("Crashing the thumbnail content process.");
+    mm.sendAsyncMessage("thumbnails-test:crash");
+    yield deferred.promise;
+  },
+];
+
+function injectContentScript() {
+  let thumbnailBrowser = imports.BackgroundPageThumbs._thumbBrowser;
+  let mm = thumbnailBrowser.messageManager;
+  mm.loadFrameScript(TEST_CONTENT_HELPER, false);
+  return mm;
+}
+
+function capture(url, options) {
+  let deferred = imports.Promise.defer();
+  options = options || {};
+  options.onDone = function onDone(capturedURL) {
+    deferred.resolve(capturedURL);
+  };
+  imports.BackgroundPageThumbs.capture(url, options);
+  return deferred.promise;
+}
+
+function fileForURL(url) {
+  let path = imports.PageThumbsStorage.getFilePathForURL(url);
+  let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+  file.initWithPath(path);
+  return file;
+}
+
+function testPageURL(opts) {
+  return TEST_PAGE_URL + "?" + encodeURIComponent(JSON.stringify(opts || {}));
+}
+
+// This observer is needed so we can clean up all evidence of the crash so
+// the testrunner thinks things are peachy.
+let numCrashes = 0;
+function crashObserver(subject, topic, data) {
+  is(topic, 'ipc:content-shutdown', 'Received correct observer topic.');
+  ok(subject instanceof Components.interfaces.nsIPropertyBag2,
+     'Subject implements nsIPropertyBag2.');
+  // we might see this called as the process terminates due to previous tests.
+  // We are only looking for "abnormal" exits...
+  if (!subject.hasKey("abnormal")) {
+    info("This is a normal termination and isn't the one we are looking for...");
+    return;
+  }
+  numCrashes++;
+
+  var dumpID;
+  if ('nsICrashReporter' in Components.interfaces) {
+    dumpID = subject.getPropertyAsAString('dumpID');
+    ok(dumpID, "dumpID is present and not an empty string");
+  }
+
+  if (dumpID) {
+    var minidumpDirectory = getMinidumpDirectory();
+    removeFile(minidumpDirectory, dumpID + '.dmp');
+    removeFile(minidumpDirectory, dumpID + '.extra');
+  }
+
+}
+
+function getMinidumpDirectory() {
+  var dir = Services.dirsvc.get('ProfD', Components.interfaces.nsIFile);
+  dir.append("minidumps");
+  return dir;
+}
+function removeFile(directory, filename) {
+  var file = directory.clone();
+  file.append(filename);
+  if (file.exists()) {
+    file.remove(false);
+  }
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/thumbnails/test/thumbnails_crash_content_helper.js
@@ -0,0 +1,29 @@
+/* Any copyright is dedicated to the Public Domain.
+* http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Ideally we would use CrashTestUtils.jsm, but that's only available for
+// xpcshell tests - so we just copy a ctypes crasher from it.
+Cu.import("resource://gre/modules/ctypes.jsm");
+let crash = function() { // this will crash when called.
+  let zero = new ctypes.intptr_t(8);
+  let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
+  badptr.contents
+};
+
+
+TestHelper = {
+  init: function() {
+    addMessageListener("thumbnails-test:crash", this);
+  },
+
+  receiveMessage: function(msg) {
+    switch (msg.name) {
+      case "thumbnails-test:crash":
+        privateNoteIntentionalCrash();
+        crash();
+      break;
+    }
+  },
+}
+
+TestHelper.init();
--- a/toolkit/devtools/server/actors/webbrowser.js
+++ b/toolkit/devtools/server/actors/webbrowser.js
@@ -32,17 +32,17 @@ function appShellDOMWindowType(aWindow) 
   /* This is what nsIWindowMediator's enumerator checks. */
   return aWindow.document.documentElement.getAttribute('windowtype');
 }
 
 /**
  * Send Debugger:Shutdown events to all "navigator:browser" windows.
  */
 function sendShutdownEvent() {
-  for (let win of allAppShellDOMWindows("navigator:browser")) {
+  for (let win of allAppShellDOMWindows(DebuggerServer.chromeWindowType)) {
     let evt = win.document.createEvent("Event");
     evt.initEvent("Debugger:Shutdown", true, false);
     win.document.documentElement.dispatchEvent(evt);
   }
 }
 
 /**
  * Construct a root actor appropriate for use in a server running in a
@@ -191,30 +191,30 @@ BrowserTabList.prototype._getSelectedBro
   return aWindow.gBrowser.selectedBrowser;
 };
 
 BrowserTabList.prototype._getChildren = function(aWindow) {
   return aWindow.gBrowser.browsers;
 };
 
 BrowserTabList.prototype.getList = function() {
-  let topXULWindow = windowMediator.getMostRecentWindow("navigator:browser");
+  let topXULWindow = windowMediator.getMostRecentWindow(DebuggerServer.chromeWindowType);
 
   // As a sanity check, make sure all the actors presently in our map get
   // picked up when we iterate over all windows' tabs.
   let initialMapSize = this._actorByBrowser.size;
   let foundCount = 0;
 
   // To avoid mysterious behavior if tabs are closed or opened mid-iteration,
   // we update the map first, and then make a second pass over it to yield
   // the actors. Thus, the sequence yielded is always a snapshot of the
   // actors that were live when we began the iteration.
 
   // Iterate over all navigator:browser XUL windows.
-  for (let win of allAppShellDOMWindows("navigator:browser")) {
+  for (let win of allAppShellDOMWindows(DebuggerServer.chromeWindowType)) {
     let selectedBrowser = this._getSelectedBrowser(win);
 
     // For each tab in this XUL window, ensure that we have an actor for
     // it, reusing existing actors where possible. We actually iterate
     // over 'browser' XUL elements, and BrowserTabActor uses
     // browser.contentWindow.wrappedJSObject as the debuggee global.
     for (let browser of this._getChildren(win)) {
       // Do we have an existing actor for this browser? If not, create one.
@@ -328,17 +328,17 @@ BrowserTabList.prototype._checkListening
  *    The name of a guard property of 'this', indicating whether we're
  *    already listening for those events.
  * @param aEventNames array of strings
  *    An array of event names.
  */
 BrowserTabList.prototype._listenForEventsIf = function(aShouldListen, aGuard, aEventNames) {
   if (!aShouldListen !== !this[aGuard]) {
     let op = aShouldListen ? "addEventListener" : "removeEventListener";
-    for (let win of allAppShellDOMWindows("navigator:browser")) {
+    for (let win of allAppShellDOMWindows(DebuggerServer.chromeWindowType)) {
       for (let name of aEventNames) {
         win[op](name, this, false);
       }
     }
     this[aGuard] = aShouldListen;
   }
 };
 
@@ -386,17 +386,17 @@ BrowserTabList.prototype._listenToMediat
  */
 BrowserTabList.prototype.onWindowTitleChange = () => { };
 
 BrowserTabList.prototype.onOpenWindow = makeInfallible(function(aWindow) {
   let handleLoad = makeInfallible(() => {
     /* We don't want any further load events from this window. */
     aWindow.removeEventListener("load", handleLoad, false);
 
-    if (appShellDOMWindowType(aWindow) !== "navigator:browser")
+    if (appShellDOMWindowType(aWindow) !== DebuggerServer.chromeWindowType)
       return;
 
     // Listen for future tab activity.
     if (this._listeningForTabOpen) {
       aWindow.addEventListener("TabOpen", this, false);
       aWindow.addEventListener("TabSelect", this, false);
     }
     if (this._listeningForTabClose) {
@@ -420,17 +420,17 @@ BrowserTabList.prototype.onOpenWindow = 
 
   aWindow.addEventListener("load", handleLoad, false);
 }, "BrowserTabList.prototype.onOpenWindow");
 
 BrowserTabList.prototype.onCloseWindow = makeInfallible(function(aWindow) {
   aWindow = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                    .getInterface(Ci.nsIDOMWindow);
 
-  if (appShellDOMWindowType(aWindow) !== "navigator:browser")
+  if (appShellDOMWindowType(aWindow) !== DebuggerServer.chromeWindowType)
     return;
 
   /*
    * nsIWindowMediator deadlocks if you call its GetEnumerator method from
    * a nsIWindowMediatorListener's onCloseWindow hook (bug 873589), so
    * handle the close in a different tick.
    */
   Services.tm.currentThread.dispatch(makeInfallible(() => {
--- a/toolkit/devtools/server/main.js
+++ b/toolkit/devtools/server/main.js
@@ -345,18 +345,18 @@ var DebuggerServer = {
     mod.module.unregister(mod.api);
     mod.api.destroy();
     delete gRegisteredModules[id];
   },
 
   /**
    * Install Firefox-specific actors.
    */
-  addBrowserActors: function DS_addBrowserActors() {
-    this.chromeWindowType = "navigator:browser";
+  addBrowserActors: function(aWindowType) {
+    this.chromeWindowType = aWindowType ? aWindowType : "navigator:browser";
     this.addActors("resource://gre/modules/devtools/server/actors/webbrowser.js");
     this.addActors("resource://gre/modules/devtools/server/actors/script.js");
     this.addGlobalActor(this.ChromeDebuggerActor, "chromeDebugger");
     this.addActors("resource://gre/modules/devtools/server/actors/webconsole.js");
     this.addActors("resource://gre/modules/devtools/server/actors/gcli.js");
     if ("nsIProfiler" in Ci)
       this.addActors("resource://gre/modules/devtools/server/actors/profiler.js");
 
--- a/webapprt/CommandLineHandler.js
+++ b/webapprt/CommandLineHandler.js
@@ -16,16 +16,22 @@ CommandLineHandler.prototype = {
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler]),
 
   handle: function handle(cmdLine) {
     let args = Cc["@mozilla.org/hash-property-bag;1"].
                createInstance(Ci.nsIWritablePropertyBag);
     let inTestMode = this._handleTestMode(cmdLine, args);
 
+    let debugPort = this._handleDebugMode(cmdLine);
+    if (!isNaN(debugPort)) {
+      Cu.import("resource://webapprt/modules/RemoteDebugger.jsm");
+      RemoteDebugger.init(debugPort);
+    }
+
     if (inTestMode) {
       // Open the mochitest shim window, which configures the runtime for tests.
       Services.ww.openWindow(null,
                              "chrome://webapprt/content/mochitest.xul",
                              "_blank",
                              "chrome,dialog=no",
                              args);
     } else {
@@ -36,16 +42,44 @@ CommandLineHandler.prototype = {
                                           "chrome,dialog=no,resizable,scrollbars,centerscreen",
                                           null);
       // Load the module to start up the app
       Cu.import("resource://webapprt/modules/Startup.jsm");
       startup(window);
     }
   },
 
+  /**
+   * Handle debug command line option.
+   *
+   * @param cmdLine A nsICommandLine object.
+   *
+   * @returns the port number if it's specified, the default port number if
+   *          the debug option is specified, NaN if the debug option isn't
+   *          specified or the port number isn't valid.
+   */
+  _handleDebugMode: function(cmdLine) {
+    // -debug [port]
+    let idx = cmdLine.findFlag("debug", true);
+    if (idx < 0) {
+      return NaN;
+    }
+
+    let port;
+    let portIdx = idx + 1;
+    if (portIdx < cmdLine.length) {
+      port = parseInt(cmdLine.getArgument(portIdx));
+      if (port != NaN) {
+        return port;
+      }
+    }
+
+    return Services.prefs.getIntPref('devtools.debugger.remote-port');
+  },
+
   _handleTestMode: function _handleTestMode(cmdLine, args) {
     // -test-mode [url]
     let idx = cmdLine.findFlag("test-mode", true);
     if (idx < 0)
       return false;
     let url;
     let urlIdx = idx + 1;
     if (urlIdx < cmdLine.length) {
new file mode 100644
--- /dev/null
+++ b/webapprt/RemoteDebugger.jsm
@@ -0,0 +1,26 @@
+/* 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";
+
+this.EXPORTED_SYMBOLS = ["RemoteDebugger"];
+
+let Cc = Components.classes;
+let Ci = Components.interfaces;
+let Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import('resource://gre/modules/devtools/dbg-server.jsm');
+
+this.RemoteDebugger = {
+  init: function(port) {
+    if (!DebuggerServer.initialized) {
+      DebuggerServer.init();
+      DebuggerServer.addBrowserActors("webapprt:webapp");
+      DebuggerServer.addActors("chrome://webapprt/content/dbg-webapp-actors.js");
+    }
+    DebuggerServer.openListener(port);
+  }
+}
new file mode 100644
--- /dev/null
+++ b/webapprt/content/dbg-webapp-actors.js
@@ -0,0 +1,126 @@
+/* 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';
+
+let promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {}).Promise;
+
+/**
+ * WebappRT-specific actors.
+ */
+
+/**
+ * Construct a root actor appropriate for use in a server running in the webapp
+ * runtime. The returned root actor:
+ * - respects the factories registered with DebuggerServer.addGlobalActor,
+ * - uses a WebappTabList to supply tab actors,
+ * - sends all webapprt:webapp window documents a Debugger:Shutdown event
+ *   when it exits.
+ *
+ * * @param connection DebuggerServerConnection
+ *        The conection to the client.
+ */
+function createRootActor(connection)
+{
+  let parameters = {
+    tabList: new WebappTabList(connection),
+    globalActorFactories: DebuggerServer.globalActorFactories,
+    onShutdown: sendShutdownEvent
+  };
+  return new RootActor(connection, parameters);
+}
+
+/**
+ * A live list of BrowserTabActors representing the current webapp windows,
+ * to be provided to the root actor to answer 'listTabs' requests. In the
+ * webapp runtime, only a single tab per window is ever present.
+ *
+ * @param connection DebuggerServerConnection
+ *     The connection in which this list's tab actors may participate.
+ *
+ * @see BrowserTabList for more a extensive description of how tab list objects
+ *      work.
+ */
+function WebappTabList(connection)
+{
+  BrowserTabList.call(this, connection);
+}
+
+WebappTabList.prototype = Object.create(BrowserTabList.prototype);
+
+WebappTabList.prototype.constructor = WebappTabList;
+
+WebappTabList.prototype.getList = function() {
+  let topXULWindow = windowMediator.getMostRecentWindow(this._windowType);
+
+  // As a sanity check, make sure all the actors presently in our map get
+  // picked up when we iterate over all windows.
+  let initialMapSize = this._actorByBrowser.size;
+  let foundCount = 0;
+
+  // To avoid mysterious behavior if windows are closed or opened mid-iteration,
+  // we update the map first, and then make a second pass over it to yield
+  // the actors. Thus, the sequence yielded is always a snapshot of the
+  // actors that were live when we began the iteration.
+
+  // Iterate over all webapprt:webapp XUL windows.
+  for (let win of allAppShellDOMWindows(this._windowType)) {
+    let browser = win.document.getElementById("content");
+    if (!browser) {
+      continue;
+    }
+
+    // Do we have an existing actor for this browser? If not, create one.
+    let actor = this._actorByBrowser.get(browser);
+    if (actor) {
+      foundCount++;
+    } else {
+      actor = new WebappTabActor(this._connection, browser);
+      this._actorByBrowser.set(browser, actor);
+    }
+
+    actor.selected = (win == topXULWindow);
+  }
+
+  if (this._testing && initialMapSize !== foundCount) {
+    throw Error("_actorByBrowser map contained actors for dead tabs");
+  }
+
+  this._mustNotify = true;
+  this._checkListening();
+
+  return promise.resolve([actor for ([_, actor] of this._actorByBrowser)]);
+};
+
+/**
+ * Creates a tab actor for handling requests to the single tab, like
+ * attaching and detaching. WebappTabActor respects the actor factories
+ * registered with DebuggerServer.addTabActor.
+ *
+ * We override the title of the XUL window in content/webapp.js so here
+ * we need to override the title property to avoid confusion to the user.
+ * We won't return the title of the contained browser, but the title of
+ * the webapp window.
+ *
+ * @param connection DebuggerServerConnection
+ *        The conection to the client.
+ * @param browser browser
+ *        The browser instance that contains this tab.
+ */
+function WebappTabActor(connection, browser)
+{
+  BrowserTabActor.call(this, connection, browser);
+}
+
+WebappTabActor.prototype.constructor = WebappTabActor;
+
+WebappTabActor.prototype = Object.create(BrowserTabActor.prototype);
+
+Object.defineProperty(WebappTabActor.prototype, "title", {
+  get: function() {
+    return this.browser.ownerDocument.defaultView.document.title;
+  },
+  enumerable: true,
+  configurable: false
+});
--- a/webapprt/content/webapp.js
+++ b/webapprt/content/webapp.js
@@ -27,16 +27,20 @@ let progressListener = {
     // Set the title of the window to the name of the webapp, adding the origin
     // of the page being loaded if it's from a different origin than the app
     // (per security bug 741955, which specifies that other-origin pages loaded
     // in runtime windows must be identified in chrome).
     let title = WebappRT.config.app.manifest.name;
     let origin = location.prePath;
     if (origin != WebappRT.config.app.origin) {
       title = origin + " - " + title;
+
+      // We should exit fullscreen mode if the user navigates off the app
+      // origin.
+      document.mozCancelFullScreen();
     }
     document.documentElement.setAttribute("title", title);
   },
 
   onStateChange: function onStateChange(aProgress, aRequest, aFlags, aStatus) {
     if (aRequest instanceof Ci.nsIChannel &&
         aFlags & Ci.nsIWebProgressListener.STATE_START &&
         aFlags & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT) {
--- a/webapprt/jar.mn
+++ b/webapprt/jar.mn
@@ -4,8 +4,9 @@
 
 webapprt.jar:
 % content webapprt %content/
 * content/webapp.js                     (content/webapp.js)
 * content/webapp.xul                    (content/webapp.xul)
   content/mochitest-shared.js           (content/mochitest-shared.js)
   content/mochitest.js                  (content/mochitest.js)
   content/mochitest.xul                 (content/mochitest.xul)
+  content/dbg-webapp-actors.js          (content/dbg-webapp-actors.js)
--- a/webapprt/moz.build
+++ b/webapprt/moz.build
@@ -17,12 +17,13 @@ TEST_DIRS += ['test']
 EXTRA_COMPONENTS += [
     'CommandLineHandler.js',
     'ContentPermission.js',
     'DirectoryProvider.js',
     'components.manifest',
 ]
 
 EXTRA_JS_MODULES += [
+    'RemoteDebugger.jsm',
     'Startup.jsm',
     'WebappRT.jsm',
     'WebappsHandler.jsm',
 ]
--- a/webapprt/prefs.js
+++ b/webapprt/prefs.js
@@ -46,16 +46,19 @@ pref("dom.mozTCPSocket.enabled", true);
 // Enable smooth scrolling
 pref("general.smoothScroll", true);
 
 // Enable window resize and move
 pref("dom.always_allow_move_resize_window", true);
 
 pref("plugin.allowed_types", "application/x-shockwave-flash,application/futuresplash");
 
+pref("devtools.debugger.remote-enabled", true);
+pref("devtools.debugger.force-local", true);
+
 // The default for this pref reflects whether the build is capable of IPC.
 // (Turning it on in a no-IPC build will have no effect.)
 #ifdef XP_MACOSX
 // i386 ipc preferences
 pref("dom.ipc.plugins.enabled.i386", false);
 pref("dom.ipc.plugins.enabled.i386.flash player.plugin", true);
 // x86_64 ipc preferences
 pref("dom.ipc.plugins.enabled.x86_64", true);
--- a/webapprt/test/chrome/Makefile.in
+++ b/webapprt/test/chrome/Makefile.in
@@ -23,9 +23,13 @@ MOCHITEST_WEBAPPRT_CHROME_FILES = \
   browser_geolocation-prompt-perm.js \
   browser_geolocation-prompt-noperm.js \
     geolocation-prompt-perm.webapp \
     geolocation-prompt-perm.webapp^headers^ \
     geolocation-prompt-noperm.webapp \
     geolocation-prompt-noperm.webapp^headers^ \
     geolocation-prompt-perm.html \
     geolocation-prompt-noperm.html \
+  browser_debugger.js \
+    debugger.webapp \
+    debugger.webapp^headers^ \
+    debugger.html \
   $(NULL)
new file mode 100644
--- /dev/null
+++ b/webapprt/test/chrome/browser_debugger.js
@@ -0,0 +1,39 @@
+Cu.import("resource://gre/modules/Services.jsm");
+let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
+let { DebuggerClient } = Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {});
+let { RemoteDebugger } = Cu.import("resource://webapprt/modules/RemoteDebugger.jsm", {});
+
+function test() {
+  waitForExplicitFinish();
+
+  loadWebapp("debugger.webapp", undefined, () => {
+    RemoteDebugger.init(Services.prefs.getIntPref('devtools.debugger.remote-port'));
+
+    let client = new DebuggerClient(DebuggerServer.connectPipe());
+    client.connect(() => {
+      client.listTabs((aResponse) => {
+        is(aResponse.tabs[0].title, "Debugger Test Webapp", "Title correct");
+        is(aResponse.tabs[0].url, "http://test/webapprtChrome/webapprt/test/chrome/debugger.html", "URL correct");
+        ok(aResponse.tabs[0].consoleActor, "consoleActor set");
+        ok(aResponse.tabs[0].gcliActor, "gcliActor set");
+        ok(aResponse.tabs[0].styleEditorActor, "styleEditorActor set");
+        ok(aResponse.tabs[0].inspectorActor, "inspectorActor set");
+        ok(aResponse.tabs[0].traceActor, "traceActor set");
+        ok(aResponse.chromeDebugger, "chromeDebugger set");
+        ok(aResponse.consoleActor, "consoleActor set");
+        ok(aResponse.gcliActor, "gcliActor set");
+        ok(aResponse.profilerActor, "profilerActor set");
+        ok(aResponse.gcliActor, "gcliActor set");
+        ok(aResponse.deviceActor, "deviceActor set");
+
+        client.close(() => {
+          finish();
+        });
+      });
+    });
+  });
+
+  registerCleanupFunction(function() {
+    DebuggerServer.destroy();
+  });
+}
new file mode 100644
--- /dev/null
+++ b/webapprt/test/chrome/debugger.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset="utf-8">
+  </head>
+  <body>
+    <p>This is the test webapp.</p>
+  </body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/webapprt/test/chrome/debugger.webapp
@@ -0,0 +1,1 @@
+{"name": "Debugger Test Webapp", "description": "A debugger test app.", "launch_path": "/webapprtChrome/webapprt/test/chrome/debugger.html" }
new file mode 100644
--- /dev/null
+++ b/webapprt/test/chrome/debugger.webapp^headers^
@@ -0,0 +1,1 @@
+Content-Type: application/x-web-app-manifest+json