Merge mozilla-central to mozilla-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 12 Dec 2014 13:57:12 +0100
changeset 246910 688b047708527e7a6ef2c114828b1d0a210809c0
parent 246909 9877aca65ef5253dd34bcff7157a3813c8128094 (current diff)
parent 246900 28fdae8302890303fb4d3fec86ee1906aab7e404 (diff)
child 246911 b7d6bbed77626a2d248b257e80140e8e0fb1f260
push id698
push userjlund@mozilla.com
push dateMon, 23 Mar 2015 22:08:11 +0000
treeherdermozilla-release@b0c0ae7b02a3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone37.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/modules/BrowserNewTabPreloader.jsm
--- a/accessible/tests/mochitest/actions/test_link.html
+++ b/accessible/tests/mochitest/actions/test_link.html
@@ -17,18 +17,27 @@
           src="../actions.js"></script>
 
   <script type="application/javascript">
     function getAnchorTargetDocumentAcc()
     {
       var thisTabDocAcc = getTabDocAccessible();
       var thisDocTabPanelAcc = thisTabDocAcc.parent.parent;
       var tabPanelsAcc = thisDocTabPanelAcc.parent;
-      var newDocTabPanelAcc = tabPanelsAcc.lastChild.firstChild;
-      return newDocTabPanelAcc.firstChild;
+      var newDocTabPanelAcc = tabPanelsAcc.firstChild;
+      var nextAcc = newDocTabPanelAcc;
+
+      while (nextAcc = nextAcc.nextSibling) {
+        // Find the last accessible for a browser with about:mozilla loaded.
+        if (nextAcc.firstChild.DOMNode.currentURI.spec == "about:mozilla") {
+          newDocTabPanelAcc = nextAcc;
+        }
+      }
+
+      return newDocTabPanelAcc.firstChild.firstChild;
     }
 
     function linkChecker(aID)
     {
       this.type = EVENT_DOCUMENT_LOAD_COMPLETE;
       this.__defineGetter__("target", getAnchorTargetDocumentAcc);
 
       this.check = function linkChecker_check()
--- a/accessible/tests/mochitest/events/test_docload.html
+++ b/accessible/tests/mochitest/events/test_docload.html
@@ -10,35 +10,16 @@
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
 
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
           src="../role.js"></script>
   <script type="application/javascript"
           src="../states.js"></script>
-
-  <script type="application/javascript">
-    // Front end stuff sometimes likes to stuff things in the hidden window(s)
-    // in which case there's accessibles for that content.
-    Components.utils.import("resource://gre/modules/Services.jsm");
-
-    // Force the creation of an accessible for the hidden window's document.
-    var doc = Services.appShell.hiddenDOMWindow.document;
-    gAccRetrieval.getAccessibleFor(doc);
-
-    // The private hidden window will be lazily created that's why we need to do
-    // it here *before* loading '../events.js' or else we'll have a duplicate
-    // reorder event.
-    var privateDoc = Services.appShell.hiddenPrivateDOMWindow.document;
-
-    // Force the creation of an accessible for the private hidden window's doc.
-    gAccRetrieval.getAccessibleFor(privateDoc);
-  </script>
-
   <script type="application/javascript"
           src="../events.js"></script>
 
   <script type="application/javascript">
     ////////////////////////////////////////////////////////////////////////////
     // Invokers
 
     function changeIframeSrc(aIdentifier, aURL)
@@ -193,22 +174,16 @@
         var accTree = {
           role: ROLE_APP_ROOT,
           children: [
             {
               role: ROLE_CHROME_WINDOW
             },
             {
               role: ROLE_CHROME_WINDOW
-            },
-            {
-              role: ROLE_CHROME_WINDOW
-            },
-            {
-              role: ROLE_CHROME_WINDOW
             }
           ]
         };
 
         testAccessibleTree(this.mRootAcc, accTree);
 
         var dlgDoc = this.mDialog.document;
         ok(isAccessibleInCache(dlgDoc),
--- a/accessible/tests/mochitest/tree/test_dochierarchy.html
+++ b/accessible/tests/mochitest/tree/test_dochierarchy.html
@@ -26,20 +26,25 @@
       getAccessible(window.parent.document, [nsIAccessibleDocument]) :
       getAccessible(document, [nsIAccessibleDocument]);
     var testDoc = getAccessible(document, [nsIAccessibleDocument]);
     var iframeDoc = getAccessible("iframe").firstChild.
       QueryInterface(nsIAccessibleDocument);
 
     is(root.parentDocument, null,
        "Wrong parent document of root accessible");
-    is(root.childDocumentCount, 1,
+    ok(root.childDocumentCount >= 1,
        "Wrong child document count of root accessible");
-    is(root.getChildDocumentAt(0), tabDoc,
-       "Wrong child document at index 0 of root accessible");
+
+    var tabDocumentFound = false;
+    for (var i = 0; i < root.childDocumentCount && !tabDocumentFound; i++) {
+      tabDocumentFound = root.getChildDocumentAt(i) == tabDoc;
+    }
+    ok(tabDocumentFound,
+       "Tab document not found in children of the root accessible");
 
     is(tabDoc.parentDocument, root,
        "Wrong parent document of tab document");
     is(tabDoc.childDocumentCount, 1,
        "Wrong child document count of tab document");
     is(tabDoc.getChildDocumentAt(0), (tabDoc == testDoc ? iframeDoc : testDoc),
        "Wrong child document at index 0 of tab document");
 
--- a/accessible/tests/mochitest/tree/test_tabbrowser.xul
+++ b/accessible/tests/mochitest/tree/test_tabbrowser.xul
@@ -164,16 +164,33 @@
                     {
                       // #document ("about:mozilla")
                       role: ROLE_DOCUMENT
                       // children: [ ... ] // Ignore document content.
                     }
                   ]
                 }
               ]
+            },
+            {
+              // notificationbox
+              role: ROLE_PROPERTYPAGE,
+              children: [
+                {
+                  // browser
+                  role: ROLE_INTERNAL_FRAME,
+                  children: [
+                    {
+                      // #document ("about:newtab" preloaded)
+                      role: ROLE_APPLICATION
+                      // children: [ ... ] // Ignore document content.
+                    }
+                  ]
+                }
+              ]
             }
           ]
         };
 
         testAccessibleTree(tabBrowser().mTabBox.tabpanels, tabboxAccTree);
       }
 
       this.getID = function testTabHierarchy_getID()
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="1d9ae9cca415ad093beba9521c429350e1f2b14d"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="0de95b4017de73e72c897e2dcb87564d8f3756eb"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="58aee9864c193acfd03fa196af399d0659ba506d"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="1d9ae9cca415ad093beba9521c429350e1f2b14d"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0de95b4017de73e72c897e2dcb87564d8f3756eb"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="6fa7a4936414ceb4055fd27f7a30e76790f834fb"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="58aee9864c193acfd03fa196af399d0659ba506d"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="0e94c080bee081a50aa2097527b0b40852f9143f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="1d9ae9cca415ad093beba9521c429350e1f2b14d"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="0de95b4017de73e72c897e2dcb87564d8f3756eb"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="58aee9864c193acfd03fa196af399d0659ba506d"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="1d9ae9cca415ad093beba9521c429350e1f2b14d"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="0de95b4017de73e72c897e2dcb87564d8f3756eb"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="58aee9864c193acfd03fa196af399d0659ba506d"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="1d9ae9cca415ad093beba9521c429350e1f2b14d"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0de95b4017de73e72c897e2dcb87564d8f3756eb"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="6fa7a4936414ceb4055fd27f7a30e76790f834fb"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="58aee9864c193acfd03fa196af399d0659ba506d"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="1d9ae9cca415ad093beba9521c429350e1f2b14d"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="0de95b4017de73e72c897e2dcb87564d8f3756eb"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="58aee9864c193acfd03fa196af399d0659ba506d"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="0e94c080bee081a50aa2097527b0b40852f9143f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="1d9ae9cca415ad093beba9521c429350e1f2b14d"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="0de95b4017de73e72c897e2dcb87564d8f3756eb"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="58aee9864c193acfd03fa196af399d0659ba506d"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="e95b4ce22c825da44d14299e1190ea39a5260bde"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "git_revision": "", 
         "remote": "", 
         "branch": ""
     }, 
-    "revision": "f1f983cfb1e42dbc6723c192f73c16bf098b6b4b", 
+    "revision": "21a79199c8869df69769148db45cf08aff37e315", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="1d9ae9cca415ad093beba9521c429350e1f2b14d"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0de95b4017de73e72c897e2dcb87564d8f3756eb"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="58aee9864c193acfd03fa196af399d0659ba506d"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="746bc48f34f5060f90801925dcdd964030c1ab6d"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="1d9ae9cca415ad093beba9521c429350e1f2b14d"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0de95b4017de73e72c897e2dcb87564d8f3756eb"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="0e94c080bee081a50aa2097527b0b40852f9143f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="1d9ae9cca415ad093beba9521c429350e1f2b14d"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="0de95b4017de73e72c897e2dcb87564d8f3756eb"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="58aee9864c193acfd03fa196af399d0659ba506d"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="1d9ae9cca415ad093beba9521c429350e1f2b14d"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0de95b4017de73e72c897e2dcb87564d8f3756eb"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="58aee9864c193acfd03fa196af399d0659ba506d"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/browser/base/content/browser-fxaccounts.js
+++ b/browser/base/content/browser-fxaccounts.js
@@ -6,16 +6,17 @@ XPCOMUtils.defineLazyGetter(this, "FxAcc
   return Cu.import("resource://gre/modules/FxAccountsCommon.js", {});
 });
 
 XPCOMUtils.defineLazyModuleGetter(this, "fxaMigrator",
   "resource://services-sync/FxaMigrator.jsm");
 
 const PREF_SYNC_START_DOORHANGER = "services.sync.ui.showSyncStartDoorhanger";
 const DOORHANGER_ACTIVATE_DELAY_MS = 5000;
+const SYNC_MIGRATION_NOTIFICATION_TITLE = "fxa-migration";
 
 let gFxAccounts = {
 
   _initialized: false,
   _inCustomizationMode: false,
 
   get weave() {
     delete this.weave;
@@ -152,17 +153,17 @@ let gFxAccounts = {
       // Our window might have been in the background while we received the
       // sync:start notification. If still needed, show the doorhanger after
       // a short delay. Without this delay the doorhanger would not show up
       // or with a too small delay show up while we're still animating the
       // window.
       setTimeout(() => this.onSyncStart(), DOORHANGER_ACTIVATE_DELAY_MS);
     } else {
       this._inCustomizationMode = event.type == "customizationstarting";
-      this.updateUI();
+      this.updateAppMenuItem();
     }
   },
 
   showDoorhanger: function (id) {
     let panel = document.getElementById(id);
     let anchor = document.getElementById("PanelUI-menu-button");
 
     let iconAnchor =
@@ -177,18 +178,23 @@ let gFxAccounts = {
     this.showDoorhanger("sync-start-panel");
   },
 
   showSyncFailedDoorhanger: function () {
     this.showDoorhanger("sync-error-panel");
   },
 
   updateUI: function () {
+    this.updateAppMenuItem();
+    this.updateMigrationNotification();
+  },
+
+  updateAppMenuItem: function () {
     if (this._migrationInfo) {
-      this.showMigrationUI();
+      this.updateAppMenuItemForMigration();
       return;
     }
 
     // Bail out if FxA is disabled.
     if (!this.weave.fxAccountsEnabled) {
       // When migration transitions from needs-verification to the null state,
       // fxAccountsEnabled is false because migration has not yet finished.  In
       // that case, hide the button.  We'll get another notification with a null
@@ -236,42 +242,84 @@ let gFxAccounts = {
       // This is most likely in tests, were we quickly log users in and out.
       // The most likely scenario is a user logged out, so reflect that.
       // Bug 995134 calls for better errors so we could retry if we were
       // sure this was the failure reason.
       doUpdate(null);
     });
   },
 
-  showMigrationUI: Task.async(function* () {
+  updateAppMenuItemForMigration: Task.async(function* () {
     let status = null;
     let label = null;
     switch (this._migrationInfo.state) {
       case fxaMigrator.STATE_USER_FXA:
         status = "migrate-signup";
-        label = this.strings.formatStringFromName("needUser",
+        label = this.strings.formatStringFromName("needUserShort",
           [this.button.getAttribute("fxabrandname")], 1);
         break;
       case fxaMigrator.STATE_USER_FXA_VERIFIED:
-        if (this._migrationInfo.email) {
-          status = "migrate-verify";
-          label = this.strings.formatStringFromName("needVerifiedUser",
-                                                    [this._migrationInfo.email],
-                                                    1);
-        }
+        status = "migrate-verify";
+        label = this.strings.formatStringFromName("needVerifiedUserShort",
+                                                  [this._migrationInfo.email],
+                                                  1);
         break;
     }
-    if (label && status) {
-      this.button.label = label;
-      this.button.hidden = false;
-      this.button.setAttribute("fxastatus", status);
-    } else {
-      Cu.reportError("Could not update menu panel button given migration " +
-                     "state: " + this._migrationInfo.state);
+    this.button.label = label;
+    this.button.hidden = false;
+    this.button.setAttribute("fxastatus", status);
+  }),
+
+  updateMigrationNotification: Task.async(function* () {
+    if (!this._migrationInfo) {
+      Weave.Notifications.removeAll(SYNC_MIGRATION_NOTIFICATION_TITLE);
+      return;
+    }
+    if (gBrowser.currentURI.spec.split("?")[0] == "about:accounts") {
+      // If the current tab is about:accounts, assume the user just completed a
+      // migration step and don't bother them with a redundant notification.
+      return;
     }
+    let note = null;
+    switch (this._migrationInfo.state) {
+      case fxaMigrator.STATE_USER_FXA: {
+        let msg = this.strings.GetStringFromName("needUserLong");
+        let upgradeLabel =
+          this.strings.GetStringFromName("upgradeToFxA.label");
+        let upgradeAccessKey =
+          this.strings.GetStringFromName("upgradeToFxA.accessKey");
+        note = new Weave.Notification(
+          undefined, msg, undefined, Weave.Notifications.PRIORITY_WARNING, [
+            new Weave.NotificationButton(upgradeLabel, upgradeAccessKey, () => {
+              fxaMigrator.createFxAccount(window);
+            }),
+          ]
+        );
+        break;
+      }
+      case fxaMigrator.STATE_USER_FXA_VERIFIED: {
+        let msg =
+          this.strings.formatStringFromName("needVerifiedUserLong",
+                                            [this._migrationInfo.email], 1);
+        let resendLabel =
+          this.strings.GetStringFromName("resendVerificationEmail.label");
+        let resendAccessKey =
+          this.strings.GetStringFromName("resendVerificationEmail.accessKey");
+        note = new Weave.Notification(
+          undefined, msg, undefined, Weave.Notifications.PRIORITY_INFO, [
+            new Weave.NotificationButton(resendLabel, resendAccessKey, () => {
+              fxaMigrator.resendVerificationMail();
+            }),
+          ]
+        );
+        break;
+      }
+    }
+    note.title = SYNC_MIGRATION_NOTIFICATION_TITLE;
+    Weave.Notifications.replaceTitle(note);
   }),
 
   onMenuPanelCommand: function (event) {
     let button = event.originalTarget;
 
     switch (button.getAttribute("fxastatus")) {
     case "signedin":
       this.openPreferences();
--- a/browser/base/content/browser-syncui.js
+++ b/browser/base/content/browser-syncui.js
@@ -8,31 +8,28 @@ Cu.import("resource://gre/modules/XPCOMU
 XPCOMUtils.defineLazyModuleGetter(this, "CloudSync",
                                   "resource://gre/modules/CloudSync.jsm");
 #else
 let CloudSync = null;
 #endif
 
 // gSyncUI handles updating the tools menu and displaying notifications.
 let gSyncUI = {
-  DEFAULT_EOL_URL: "https://www.mozilla.org/firefox/?utm_source=synceol",
-
   _obs: ["weave:service:sync:start",
          "weave:service:quota:remaining",
          "weave:service:setup-complete",
          "weave:service:login:start",
          "weave:service:login:finish",
          "weave:service:logout:finish",
          "weave:service:start-over",
          "weave:service:start-over:finish",
          "weave:ui:login:error",
          "weave:ui:sync:error",
          "weave:ui:sync:finish",
          "weave:ui:clear-error",
-         "weave:eol",
   ],
 
   _unloaded: false,
 
   init: function () {
     Cu.import("resource://services-common/stringbundle.js");
 
     // Proceed to set up the UI if Sync has already started up.
@@ -255,42 +252,16 @@ let gSyncUI = {
     Weave.Notifications.replaceTitle(notification);
   },
 
   _getAppName: function () {
     let brand = new StringBundle("chrome://branding/locale/brand.properties");
     return brand.get("brandShortName");
   },
 
-  onEOLNotice: function (data) {
-    let code = data.code;
-    let kind = (code == "hard-eol") ? "error" : "warning";
-    let url = data.url || gSyncUI.DEFAULT_EOL_URL;
-
-    let title = this._stringBundle.GetStringFromName(kind + ".sync.eol.label");
-    let description = this._stringBundle.formatStringFromName(kind + ".sync.eol.description",
-                                                              [this._getAppName()],
-                                                              1);
-
-    let buttons = [];
-    buttons.push(new Weave.NotificationButton(
-      this._stringBundle.GetStringFromName("sync.eol.learnMore.label"),
-      this._stringBundle.GetStringFromName("sync.eol.learnMore.accesskey"),
-      function() {
-        window.openUILinkIn(url, "tab");
-        return true;
-      }
-    ));
-
-    let priority = (kind == "error") ? Weave.Notifications.PRIORITY_WARNING :
-                                       Weave.Notifications.PRIORITY_INFO;
-    let notification = new Weave.Notification(title, description, null, priority, buttons);
-    Weave.Notifications.replaceTitle(notification);
-  },
-
   openServerStatus: function () {
     let statusURL = Services.prefs.getCharPref("services.sync.statusURL");
     window.openUILinkIn(statusURL, "tab");
   },
 
   // Commands
   doSync: function SUI_doSync() {
     let needsSetup = this._needsSetup();
@@ -546,19 +517,16 @@ let gSyncUI = {
         this.initUI();
         break;
       case "weave:notification:added":
         this.initNotifications();
         break;
       case "weave:ui:clear-error":
         this.clearError();
         break;
-      case "weave:eol":
-        this.onEOLNotice(subject);
-        break;
     }
   },
 
   QueryInterface: XPCOMUtils.generateQI([
     Ci.nsIObserver,
     Ci.nsISupportsWeakReference
   ])
 };
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -149,19 +149,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "PageThumbs",
   "resource://gre/modules/PageThumbs.jsm");
 
 #ifdef MOZ_SAFE_BROWSING
 XPCOMUtils.defineLazyModuleGetter(this, "SafeBrowsing",
   "resource://gre/modules/SafeBrowsing.jsm");
 #endif
 
-XPCOMUtils.defineLazyModuleGetter(this, "gBrowserNewTabPreloader",
-  "resource:///modules/BrowserNewTabPreloader.jsm", "BrowserNewTabPreloader");
-
 XPCOMUtils.defineLazyModuleGetter(this, "gCustomizationTabPreloader",
   "resource:///modules/CustomizationTabPreloader.jsm", "CustomizationTabPreloader");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Translation",
   "resource:///modules/translation/Translation.jsm");
--- a/browser/base/content/socialchat.xml
+++ b/browser/base/content/socialchat.xml
@@ -664,16 +664,22 @@
 
           let otherWin = window.openDialog("chrome://browser/content/chatWindow.xul",
                                            "_blank", "chrome,all,dialog=no" + options);
 
           otherWin.addEventListener("load", function _chatLoad(event) {
             if (event.target != otherWin.document)
               return;
 
+            let detachEvent = new aChatbox.contentWindow.CustomEvent("socialFrameDetached", {
+              bubbles: true,
+              cancelable: true,
+            });
+            aChatbox.contentDocument.dispatchEvent(detachEvent);
+
             otherWin.removeEventListener("load", _chatLoad, true);
             let otherChatbox = otherWin.document.getElementById("chatter");
             aChatbox.swapDocShells(otherChatbox);
             aChatbox.close();
             chatbar.chatboxForURL.set(aChatbox.src, Cu.getWeakReference(otherChatbox));
 
             deferred.resolve(otherChatbox);
           }, true);
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1508,16 +1508,144 @@
           <![CDATA[
             let shouldBeRemote = gMultiProcessBrowser &&
                                  E10SUtils.shouldBrowserBeRemote(aURL);
             return this.updateBrowserRemoteness(aBrowser, shouldBeRemote);
           ]]>
         </body>
       </method>
 
+      <field name="_preloadedBrowser">null</field>
+      <method name="_getPreloadedBrowser">
+        <body>
+          <![CDATA[
+            if (!this._isPreloadingEnabled()) {
+              return null;
+            }
+
+            // The preloaded browser might be null.
+            let browser = this._preloadedBrowser;
+
+            // Consume the browser.
+            this._preloadedBrowser = null;
+
+            // Attach the nsIFormFillController now that we know the browser
+            // will be used. If we do that before and the preloaded browser
+            // won't be consumed until shutdown then we leak a docShell.
+            if (browser && this.hasAttribute("autocompletepopup")) {
+              browser.setAttribute("autocompletepopup", this.getAttribute("autocompletepopup"));
+              browser.attachFormFill();
+            }
+
+            return browser;
+          ]]>
+        </body>
+      </method>
+
+      <method name="_isPreloadingEnabled">
+        <body>
+          <![CDATA[
+            // Preloading for the newtab page is enabled when the pref is true
+            // and the URL is "about:newtab". We do not support preloading for
+            // custom newtab URLs.
+            return Services.prefs.getBoolPref("browser.newtab.preload") &&
+                   !Services.prefs.prefHasUserValue("browser.newtab.url");
+          ]]>
+        </body>
+      </method>
+
+      <method name="_createPreloadBrowser">
+        <body>
+          <![CDATA[
+            // Do nothing if we have a preloaded browser already
+            // or preloading of newtab pages is disabled.
+            if (this._preloadedBrowser || !this._isPreloadingEnabled()) {
+              return;
+            }
+
+            let browser = this._createBrowser({isPreloadBrowser: true});
+            this._preloadedBrowser = browser;
+
+            let notificationbox = this.getNotificationBox(browser);
+            this.mPanelContainer.appendChild(notificationbox);
+
+            browser.loadURI(BROWSER_NEW_TAB_URL);
+            browser.docShellIsActive = false;
+          ]]>
+        </body>
+      </method>
+
+      <method name="_createBrowser">
+        <parameter name="aParams"/>
+        <body>
+          <![CDATA[
+            const NS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+            let remote = aParams && aParams.remote;
+            let uriIsAboutBlank = aParams && aParams.uriIsAboutBlank;
+            let isPreloadBrowser = aParams && aParams.isPreloadBrowser;
+
+            let b = document.createElementNS(NS_XUL, "browser");
+            b.setAttribute("type", "content-targetable");
+            b.setAttribute("message", "true");
+            b.setAttribute("messagemanagergroup", "browsers");
+            b.setAttribute("contextmenu", this.getAttribute("contentcontextmenu"));
+            b.setAttribute("tooltip", this.getAttribute("contenttooltip"));
+
+            if (remote)
+              b.setAttribute("remote", "true");
+
+            if (window.gShowPageResizers && window.windowState == window.STATE_NORMAL) {
+              b.setAttribute("showresizer", "true");
+            }
+
+            if (!isPreloadBrowser && this.hasAttribute("autocompletepopup"))
+              b.setAttribute("autocompletepopup", this.getAttribute("autocompletepopup"));
+
+            if (this.hasAttribute("selectpopup"))
+              b.setAttribute("selectpopup", this.getAttribute("selectpopup"));
+
+            b.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
+
+            // Create the browserStack container
+            var stack = document.createElementNS(NS_XUL, "stack");
+            stack.className = "browserStack";
+            stack.appendChild(b);
+            stack.setAttribute("flex", "1");
+
+            // Create the browserContainer
+            var browserContainer = document.createElementNS(NS_XUL, "vbox");
+            browserContainer.className = "browserContainer";
+            browserContainer.appendChild(stack);
+            browserContainer.setAttribute("flex", "1");
+
+            // Create the sidebar container
+            var browserSidebarContainer = document.createElementNS(NS_XUL,
+                                                                   "hbox");
+            browserSidebarContainer.className = "browserSidebarContainer";
+            browserSidebarContainer.appendChild(browserContainer);
+            browserSidebarContainer.setAttribute("flex", "1");
+
+            // Add the Message and the Browser to the box
+            var notificationbox = document.createElementNS(NS_XUL,
+                                                           "notificationbox");
+            notificationbox.setAttribute("flex", "1");
+            notificationbox.appendChild(browserSidebarContainer);
+
+            // Prevent the superfluous initial load of a blank document
+            // if we're going to load something other than about:blank.
+            if (!uriIsAboutBlank) {
+              b.setAttribute("nodefaultsrc", "true");
+            }
+
+            return b;
+          ]]>
+        </body>
+      </method>
+
       <method name="addTab">
         <parameter name="aURI"/>
         <parameter name="aReferrerURI"/>
         <parameter name="aCharset"/>
         <parameter name="aPostData"/>
         <parameter name="aOwner"/>
         <parameter name="aAllowThirdPartyFixup"/>
         <body>
@@ -1578,88 +1706,53 @@
             this._visibleTabs = null;
 
             this.tabContainer.appendChild(t);
 
             // If this new tab is owned by another, assert that relationship
             if (aOwner)
               t.owner = aOwner;
 
-            var b = document.createElementNS(
-              "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
-                                             "browser");
-            b.setAttribute("type", "content-targetable");
-            b.setAttribute("message", "true");
-            b.setAttribute("messagemanagergroup", "browsers");
-            b.setAttribute("contextmenu", this.getAttribute("contentcontextmenu"));
-            b.setAttribute("tooltip", this.getAttribute("contenttooltip"));
-
-            if (remote)
-              b.setAttribute("remote", "true");
-
-            if (window.gShowPageResizers && window.windowState == window.STATE_NORMAL) {
-              b.setAttribute("showresizer", "true");
+            let b;
+            let usingPreloadedContent = false;
+            let isPrivateWindow = PrivateBrowsingUtils.isWindowPrivate(window);
+
+            // If we open a new tab with the newtab URL,
+            // check if there is a preloaded browser ready.
+            if (aURI == BROWSER_NEW_TAB_URL && !isPrivateWindow) {
+              b = this._getPreloadedBrowser();
+              usingPreloadedContent = !!b;
             }
 
-            if (this.hasAttribute("autocompletepopup"))
-              b.setAttribute("autocompletepopup", this.getAttribute("autocompletepopup"));
-
-            if (this.hasAttribute("selectpopup"))
-              b.setAttribute("selectpopup", this.getAttribute("selectpopup"));
-
-            b.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
-
-            // Create the browserStack container
-            var stack = document.createElementNS(NS_XUL, "stack");
-            stack.className = "browserStack";
-            stack.appendChild(b);
-            stack.setAttribute("flex", "1");
-
-            // Create the browserContainer
-            var browserContainer = document.createElementNS(NS_XUL, "vbox");
-            browserContainer.className = "browserContainer";
-            browserContainer.appendChild(stack);
-            browserContainer.setAttribute("flex", "1");
-
-            // Create the sidebar container
-            var browserSidebarContainer = document.createElementNS(NS_XUL,
-                                                                   "hbox");
-            browserSidebarContainer.className = "browserSidebarContainer";
-            browserSidebarContainer.appendChild(browserContainer);
-            browserSidebarContainer.setAttribute("flex", "1");
-
-            // Add the Message and the Browser to the box
-            var notificationbox = document.createElementNS(NS_XUL,
-                                                           "notificationbox");
-            notificationbox.setAttribute("flex", "1");
-            notificationbox.appendChild(browserSidebarContainer);
-
+            if (!b) {
+              // No preloaded browser found, create one.
+              b = this._createBrowser({remote, uriIsAboutBlank});
+            }
+
+            let notificationbox = this.getNotificationBox(b);
             var position = this.tabs.length - 1;
             var uniqueId = this._generateUniquePanelID();
             notificationbox.id = uniqueId;
             t.linkedPanel = uniqueId;
             t.linkedBrowser = b;
             this._tabForBrowser.set(b, t);
             t._tPos = position;
             t.lastAccessed = Date.now();
             this.tabContainer._setPositionalAttributes();
 
-            // Prevent the superfluous initial load of a blank document
-            // if we're going to load something other than about:blank.
-            if (!uriIsAboutBlank) {
-              b.setAttribute("nodefaultsrc", "true");
+            // Inject the <browser> into the DOM if necessary.
+            if (!notificationbox.parentNode) {
+              // NB: this appendChild call causes us to run constructors for the
+              // browser element, which fires off a bunch of notifications. Some
+              // of those notifications can cause code to run that inspects our
+              // state, so it is important that the tab element is fully
+              // initialized by this point.
+              this.mPanelContainer.appendChild(notificationbox);
             }
 
-            // NB: this appendChild call causes us to run constructors for the
-            // browser element, which fires off a bunch of notifications. Some
-            // of those notifications can cause code to run that inspects our
-            // state, so it is important that the tab element is fully
-            // initialized by this point.
-            this.mPanelContainer.appendChild(notificationbox);
-
             // We've waited until the tab is in the DOM to set the label. This
             // allows the TabLabelModified event to be properly dispatched.
             if (!aURI || isBlankPageURL(aURI)) {
               t.label = this.mStringBundle.getString("tabs.emptyTabTitle");
             } else if (aURI.toLowerCase().startsWith("javascript:")) {
               // This can go away when bug 672618 or bug 55696 are fixed.
               t.label = aURI;
             }
@@ -1672,38 +1765,31 @@
                                      .createInstance(Components.interfaces.nsIWebProgress);
             filter.addProgressListener(tabListener, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
             b.webProgress.addProgressListener(filter, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
             this.mTabListeners[position] = tabListener;
             this.mTabFilters[position] = filter;
 
             b.droppedLinkHandler = handleDroppedLink;
 
-            // If we just created a new tab that loads the default
-            // newtab url, swap in a preloaded page if possible.
-            // Do nothing if we're a private window.
-            let docShellsSwapped = false;
-            if (aURI == BROWSER_NEW_TAB_URL &&
-                !PrivateBrowsingUtils.isWindowPrivate(window) &&
-                !gMultiProcessBrowser) {
-              docShellsSwapped = gBrowserNewTabPreloader.newTab(t);
-            } else if (aURI == "about:customizing") {
-              docShellsSwapped = gCustomizationTabPreloader.newTab(t);
+            // Swap in a preloaded customize tab, if available.
+            if (aURI == "about:customizing") {
+              usingPreloadedContent = gCustomizationTabPreloader.newTab(t);
             }
 
             // Dispatch a new tab notification.  We do this once we're
             // entirely done, so that things are in a consistent state
             // even if the event listener opens or closes tabs.
             var evt = document.createEvent("Events");
             evt.initEvent("TabOpen", true, false);
             t.dispatchEvent(evt);
 
             // If we didn't swap docShells with a preloaded browser
             // then let's just continue loading the page normally.
-            if (!docShellsSwapped && !uriIsAboutBlank) {
+            if (!usingPreloadedContent && !uriIsAboutBlank) {
               // pretend the user typed this so it'll be available till
               // the document successfully loads
               if (aURI && gInitialPages.indexOf(aURI) == -1)
                 b.userTypedValue = aURI;
 
               let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
               if (aAllowThirdPartyFixup) {
                 flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
@@ -4274,16 +4360,19 @@
           }
 
           // XXXmano: this is a temporary workaround for bug 345399
           // We need to manually update the scroll buttons disabled state
           // if a tab was inserted to the overflow area or removed from it
           // without any scrolling and when the tabbar has already
           // overflowed.
           this.mTabstrip._updateScrollButtonsDisabledState();
+
+          // Preload the next about:newtab if there isn't one already.
+          this.tabbrowser._createPreloadBrowser();
         ]]></body>
       </method>
 
       <method name="_canAdvanceToTab">
         <parameter name="aTab"/>
         <body>
         <![CDATA[
           return !aTab.closing;
--- a/browser/base/content/test/general/browser_fxa_migrate.js
+++ b/browser/base/content/test/general/browser_fxa_migrate.js
@@ -1,43 +1,56 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const STATE_CHANGED_TOPIC = "fxa-migration:state-changed";
+const NOTIFICATION_TITLE = "fxa-migration";
 
 let imports = {};
 Cu.import("resource://services-sync/FxaMigrator.jsm", imports);
 
 add_task(function* test() {
   // Fake the state where we need an FxA user.
   let buttonPromise = promiseButtonMutation();
-  Services.obs.notifyObservers(null, "fxa-migration:state-changed",
+  Services.obs.notifyObservers(null, STATE_CHANGED_TOPIC,
                                fxaMigrator.STATE_USER_FXA);
   let buttonState = yield buttonPromise;
   assertButtonState(buttonState, "migrate-signup", true);
+  Assert.ok(Weave.Notifications.notifications.some(n => {
+    return n.title == NOTIFICATION_TITLE;
+  }), "Needs-user notification should be present");
 
   // Fake the state where we need a verified FxA user.
   buttonPromise = promiseButtonMutation();
   let email = Cc["@mozilla.org/supports-string;1"].
               createInstance(Ci.nsISupportsString);
   email.data = "foo@example.com";
-  Services.obs.notifyObservers(email, "fxa-migration:state-changed",
+  Services.obs.notifyObservers(email, STATE_CHANGED_TOPIC,
                                fxaMigrator.STATE_USER_FXA_VERIFIED);
   buttonState = yield buttonPromise;
   assertButtonState(buttonState, "migrate-verify", true,
                     "foo@example.com not verified");
+  let note = Weave.Notifications.notifications.find(n => {
+    return n.title == NOTIFICATION_TITLE;
+  });
+  Assert.ok(!!note, "Needs-verification notification should be present");
+  Assert.ok(note.description.contains(email.data),
+            "Needs-verification notification should include email");
 
   // Fake the state where no migration is needed.
   buttonPromise = promiseButtonMutation();
-  Services.obs.notifyObservers(null, "fxa-migration:state-changed", null);
+  Services.obs.notifyObservers(null, STATE_CHANGED_TOPIC, null);
   buttonState = yield buttonPromise;
   // In this case, the front end has called fxAccounts.getSignedInUser() to
   // update the button label and status.  But since there isn't actually a user,
   // the button is left with no fxastatus.
   assertButtonState(buttonState, "", true);
+  Assert.ok(!Weave.Notifications.notifications.some(n => {
+    return n.title == NOTIFICATION_TITLE;
+  }), "Migration notifications should no longer be present");
 });
 
 function assertButtonState(buttonState, expectedStatus, expectedVisible,
                            expectedLabel=undefined) {
   Assert.equal(buttonState.fxastatus, expectedStatus,
                "Button fxstatus attribute");
   Assert.equal(!buttonState.hidden, expectedVisible, "Button visibility");
   if (expectedLabel !== undefined) {
--- a/browser/base/content/test/newtab/browser_newtab_background_captures.js
+++ b/browser/base/content/test/newtab/browser_newtab_background_captures.js
@@ -6,17 +6,16 @@
  * when unhidden, do allow background captures.
  */
 
 const CAPTURE_PREF = "browser.pagethumbnails.capturing_disabled";
 
 function runTests() {
   let imports = {};
   Cu.import("resource://gre/modules/PageThumbs.jsm", imports);
-  Cu.import("resource:///modules/BrowserNewTabPreloader.jsm", imports);
 
   // Disable captures.
   let originalDisabledState = Services.prefs.getBoolPref(CAPTURE_PREF);
   Services.prefs.setBoolPref(CAPTURE_PREF, true);
 
   // Make sure the thumbnail doesn't exist yet.
   let url = "http://example.com/";
   let path = imports.PageThumbsStorage.getFilePathForURL(url);
@@ -26,57 +25,39 @@ function runTests() {
     file.remove(false);
   }
   catch (err) {}
 
   // Add a top site.
   yield setLinks("-1");
 
   // We need a handle to a hidden, pre-loaded newtab so we can verify that it
-  // doesn't allow background captures.  Add a newtab, which triggers creation
-  // of a hidden newtab, and then keep calling BrowserNewTabPreloader.newTab
-  // until it returns true, meaning that it swapped the passed-in tab's docshell
-  // for the hidden newtab docshell.
-  let tab = gWindow.gBrowser.addTab("about:blank");
-  yield addNewTabPageTab();
-
-  // When newtab is loaded very quickly (which is what happens in 99% of cases)
-  // there is no need to wait so no tests are run. Because each test requires
-  // either a pass, fail or todo we run a dummy test here.
-  ok(true, "Each test requires at least one pass, fail or todo so here is a pass.");
+  // doesn't allow background captures. Ensure we have a preloaded browser.
+  gBrowser._createPreloadBrowser();
 
-  let swapWaitCount = 0;
-  let swapped = imports.BrowserNewTabPreloader.newTab(tab);
-  while (!swapped) {
-    if (++swapWaitCount == 10) {
-      ok(false, "Timed out waiting for newtab docshell swap.");
-      return;
-    }
-    // Give the hidden newtab some time to finish loading.
-    yield wait(2000);
-    info("Checking newtab swap " + swapWaitCount);
-    swapped = imports.BrowserNewTabPreloader.newTab(tab);
-  }
+  // Wait for the preloaded browser to load.
+  yield waitForBrowserLoad(gBrowser._preloadedBrowser);
 
-  // The tab's docshell is now the previously hidden newtab docshell.
+  // We're now ready to use the preloaded browser.
+  BrowserOpenTab();
+  let tab = gBrowser.selectedTab;
   let doc = tab.linkedBrowser.contentDocument;
 
   // Enable captures.
   Services.prefs.setBoolPref(CAPTURE_PREF, false);
 
   // Showing the preloaded tab should trigger thumbnail capture.
   Services.obs.addObserver(function onCreate(subj, topic, data) {
     if (data != url)
       return;
     Services.obs.removeObserver(onCreate, "page-thumbnail:create");
+    ok(true, "thumbnail created after preloaded tab was shown");
+
     // Test finished!
     Services.prefs.setBoolPref(CAPTURE_PREF, originalDisabledState);
+    gBrowser.removeTab(tab);
     file.remove(false);
     TestRunner.next();
   }, "page-thumbnail:create", false);
 
   info("Waiting for thumbnail capture");
   yield true;
 }
-
-function wait(ms) {
-  setTimeout(TestRunner.next, ms);
-}
--- a/browser/base/content/test/newtab/head.js
+++ b/browser/base/content/test/newtab/head.js
@@ -369,29 +369,35 @@ function addNewTabPageTabPromise() {
       NewTabUtils.links.populateCache(function () {
         deferred.resolve(whenSearchInitDone());
       });
     } else {
       deferred.resolve();
     }
   }
 
-  // The new tab page might have been preloaded in the background.
+  // Wait for the new tab page to be loaded.
+  waitForBrowserLoad(browser, function () {
+    // Wait for the document to become visible in case it was preloaded.
+    waitForCondition(() => !browser.contentDocument.hidden).then(whenNewTabLoaded);
+  });
+
+  return deferred.promise;
+}
+
+function waitForBrowserLoad(browser, callback = TestRunner.next) {
   if (browser.contentDocument.readyState == "complete") {
-    waitForCondition(() => !browser.contentDocument.hidden).then(whenNewTabLoaded);
-    return deferred.promise;
+    executeSoon(callback);
+    return;
   }
 
-  // Wait for the new tab page to be loaded.
   browser.addEventListener("load", function onLoad() {
     browser.removeEventListener("load", onLoad, true);
-    whenNewTabLoaded();
+    executeSoon(callback);
   }, true);
-
-  return deferred.promise;
 }
 
 /**
  * Compares the current grid arrangement with the given pattern.
  * @param the pattern (see below)
  * @param the array of sites to compare with (optional)
  *
  * Example: checkGrid("3p,2,,1p")
--- a/browser/components/loop/MozLoopService.jsm
+++ b/browser/components/loop/MozLoopService.jsm
@@ -823,19 +823,25 @@ let MozLoopServiceInternal = {
       chatbox.addEventListener("DOMContentLoaded", function loaded(event) {
         if (event.target != chatbox.contentDocument) {
           return;
         }
         chatbox.removeEventListener("DOMContentLoaded", loaded, true);
 
         let window = chatbox.contentWindow;
 
-        window.addEventListener("unload", function onUnloadChat(evt) {
-          UITour.notify("Loop:ChatWindowClosed");
-        });
+        function socialFrameChanged(eventName) {
+          UITour.availableTargetsCache.clear();
+          UITour.notify(eventName);
+        }
+
+        window.addEventListener("socialFrameHide", socialFrameChanged.bind(null, "Loop:ChatWindowHidden"));
+        window.addEventListener("socialFrameShow", socialFrameChanged.bind(null, "Loop:ChatWindowShown"));
+        window.addEventListener("socialFrameDetached", socialFrameChanged.bind(null, "Loop:ChatWindowDetached"));
+        window.addEventListener("unload", socialFrameChanged.bind(null, "Loop:ChatWindowClosed"));
 
         injectLoopAPI(window);
 
         let ourID = window.QueryInterface(Ci.nsIInterfaceRequestor)
             .getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
 
         let onPCLifecycleChange = (pc, winID, type) => {
           if (winID != ourID) {
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -48,19 +48,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource:///modules/WebappManager.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PageThumbs",
                                   "resource://gre/modules/PageThumbs.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "NewTabUtils",
                                   "resource://gre/modules/NewTabUtils.jsm");
 
-XPCOMUtils.defineLazyModuleGetter(this, "BrowserNewTabPreloader",
-                                  "resource:///modules/BrowserNewTabPreloader.jsm");
-
 XPCOMUtils.defineLazyModuleGetter(this, "CustomizationTabPreloader",
                                   "resource:///modules/CustomizationTabPreloader.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PdfJs",
                                   "resource://pdf.js/PdfJs.jsm");
 
 #ifdef NIGHTLY_BUILD
 XPCOMUtils.defineLazyModuleGetter(this, "ShumwayUtils",
@@ -786,17 +783,16 @@ BrowserGlue.prototype = {
     try {
       let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"]
                          .getService(Ci.nsIAppStartup);
       appStartup.trackStartupCrashEnd();
     } catch (e) {
       Cu.reportError("Could not end startup crash tracking in quit-application-granted: " + e);
     }
 
-    BrowserNewTabPreloader.uninit();
     CustomizationTabPreloader.uninit();
     WebappManager.uninit();
 #ifdef NIGHTLY_BUILD
     if (Services.prefs.getBoolPref("dom.identity.enabled")) {
       SignInToWebsiteUX.uninit();
     }
 #endif
     webrtcUI.uninit();
--- a/browser/locales/en-US/chrome/browser/accounts.properties
+++ b/browser/locales/en-US/chrome/browser/accounts.properties
@@ -1,7 +1,19 @@
-# LOCALIZATION NOTE (needUser)
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# LOCALIZATION NOTE (needUserShort)
 # %S = Firefox Accounts brand name from syncBrand.dtd
-needUser = %S required for sync
+needUserShort = %S required for sync
+needUserLong = We've rebuilt Sync to make it easier for everyone. Please upgrade to a Firefox Account to continue syncing.
 
-# LOCALIZATION NOTE (needVerifiedUser)
+upgradeToFxA.label = Upgrade
+upgradeToFxA.accessKey = U
+
+# LOCALIZATION NOTE (needVerifiedUserShort, needVerifiedUserLong)
 # %S = Email address of user's Firefox Account
-needVerifiedUser = %S not verified
+needVerifiedUserShort = %S not verified
+needVerifiedUserLong = Please click the verification link in the email sent to %S
+
+resendVerificationEmail.label = Resend
+resendVerificationEmail.accessKey = R
deleted file mode 100644
--- a/browser/modules/BrowserNewTabPreloader.jsm
+++ /dev/null
@@ -1,379 +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/. */
-
-"use strict";
-
-this.EXPORTED_SYMBOLS = ["BrowserNewTabPreloader"];
-
-const Cu = Components.utils;
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Promise.jsm");
-
-const HTML_NS = "http://www.w3.org/1999/xhtml";
-const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-const XUL_PAGE = "data:application/vnd.mozilla.xul+xml;charset=utf-8,<window%20id='win'/>";
-const NEWTAB_URL = "about:newtab";
-
-const PREF_NEWTAB_URL = "browser.newtab.url";
-const PREF_NEWTAB_PRELOAD = "browser.newtab.preload";
-
-// The interval between swapping in a preload docShell and kicking off the
-// next preload in the background.
-const PRELOADER_INTERVAL_MS = 600;
-// The number of miliseconds we'll wait after we received a notification that
-// causes us to update our list of browsers and tabbrowser sizes. This acts as
-// kind of a damper when too many events are occuring in quick succession.
-const PRELOADER_UPDATE_DELAY_MS = 3000;
-
-const TOPIC_TIMER_CALLBACK = "timer-callback";
-const TOPIC_DELAYED_STARTUP = "browser-delayed-startup-finished";
-const TOPIC_XUL_WINDOW_CLOSED = "xul-window-destroyed";
-
-const BROWSER_CONTENT_SCRIPT = "chrome://browser/content/content.js";
-
-function isPreloadingEnabled() {
-  return Services.prefs.getBoolPref(PREF_NEWTAB_PRELOAD) &&
-         !Services.prefs.prefHasUserValue(PREF_NEWTAB_URL);
-}
-
-function createTimer(obj, delay) {
-  let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-  timer.init(obj, delay, Ci.nsITimer.TYPE_ONE_SHOT);
-  return timer;
-}
-
-function clearTimer(timer) {
-  if (timer) {
-    timer.cancel();
-  }
-  return null;
-}
-
-this.BrowserNewTabPreloader = {
-  uninit: function Preloader_uninit() {
-    HostFrame.destroy();
-    HiddenBrowsers.uninit();
-  },
-
-  newTab: function Preloader_newTab(aTab) {
-    if (!isPreloadingEnabled()) {
-      return false;
-    }
-
-    let win = aTab.ownerDocument.defaultView;
-    if (win.gBrowser) {
-      let utils = win.QueryInterface(Ci.nsIInterfaceRequestor)
-                     .getInterface(Ci.nsIDOMWindowUtils);
-
-      let {width, height} = utils.getBoundsWithoutFlushing(win.gBrowser);
-      let hiddenBrowser = HiddenBrowsers.get(width, height)
-      if (hiddenBrowser) {
-        return hiddenBrowser.swapWithNewTab(aTab);
-      }
-    }
-
-    return false;
-  }
-};
-
-Object.freeze(BrowserNewTabPreloader);
-
-let HiddenBrowsers = {
-  _browsers: null,
-  _updateTimer: null,
-
-  _topics: [
-    TOPIC_DELAYED_STARTUP,
-    TOPIC_XUL_WINDOW_CLOSED
-  ],
-
-  _init: function () {
-    this._browsers = new Map();
-    this._updateBrowserSizes();
-    this._topics.forEach(t => Services.obs.addObserver(this, t, false));
-  },
-
-  uninit: function () {
-    if (this._browsers) {
-      this._topics.forEach(t => Services.obs.removeObserver(this, t, false));
-      this._updateTimer = clearTimer(this._updateTimer);
-
-      for (let [key, browser] of this._browsers) {
-        browser.destroy();
-      }
-      this._browsers = null;
-    }
-  },
-
-  get: function (width, height) {
-    // Initialize if this is the first call.
-    if (!this._browsers) {
-      this._init();
-    }
-
-    let key = width + "x" + height;
-    if (!this._browsers.has(key)) {
-      // Update all browsers' sizes if we can't find a matching one.
-      this._updateBrowserSizes();
-    }
-
-    // We should now have a matching browser.
-    if (this._browsers.has(key)) {
-      return this._browsers.get(key);
-    }
-
-    // We should never be here. Return the first browser we find.
-    Cu.reportError("NewTabPreloader: no matching browser found after updating");
-    for (let [size, browser] of this._browsers) {
-      return browser;
-    }
-
-    // We should really never be here.
-    Cu.reportError("NewTabPreloader: not even a single browser was found?");
-    return null;
-  },
-
-  observe: function (subject, topic, data) {
-    if (topic === TOPIC_TIMER_CALLBACK) {
-      this._updateTimer = null;
-      this._updateBrowserSizes();
-    } else {
-      this._updateTimer = clearTimer(this._updateTimer);
-      this._updateTimer = createTimer(this, PRELOADER_UPDATE_DELAY_MS);
-    }
-  },
-
-  _updateBrowserSizes: function () {
-    let sizes = this._collectTabBrowserSizes();
-    let toRemove = [];
-
-    // Iterate all browsers and check that they
-    // each can be assigned to one of the sizes.
-    for (let [key, browser] of this._browsers) {
-      if (sizes.has(key)) {
-        // We already have a browser for that size, great!
-        sizes.delete(key);
-      } else {
-        // This browser is superfluous or needs to be resized.
-        toRemove.push(browser);
-        this._browsers.delete(key);
-      }
-    }
-
-    // Iterate all sizes that we couldn't find a browser for.
-    for (let [key, {width, height}] of sizes) {
-      let browser;
-      if (toRemove.length) {
-        // Let's just resize one of the superfluous
-        // browsers and put it back into the map.
-        browser = toRemove.shift();
-        browser.resize(width, height);
-      } else {
-        // No more browsers to reuse, create a new one.
-        browser = new HiddenBrowser(width, height);
-      }
-
-      this._browsers.set(key, browser);
-    }
-
-    // Finally, remove all browsers we don't need anymore.
-    toRemove.forEach(b => b.destroy());
-  },
-
-  _collectTabBrowserSizes: function () {
-    let sizes = new Map();
-
-    function tabBrowserBounds() {
-      let wins = Services.ww.getWindowEnumerator("navigator:browser");
-      while (wins.hasMoreElements()) {
-        let win = wins.getNext();
-        if (win.gBrowser) {
-          let utils = win.QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIDOMWindowUtils);
-          yield utils.getBoundsWithoutFlushing(win.gBrowser);
-        }
-      }
-    }
-
-    // Collect the sizes of all <tabbrowser>s out there.
-    for (let {width, height} of tabBrowserBounds()) {
-      if (width > 0 && height > 0) {
-        let key = width + "x" + height;
-        if (!sizes.has(key)) {
-          sizes.set(key, {width: width, height: height});
-        }
-      }
-    }
-
-    return sizes;
-  }
-};
-
-function HiddenBrowser(width, height) {
-  this.resize(width, height);
-  this._createBrowser();
-}
-
-HiddenBrowser.prototype = {
-  _width: null,
-  _height: null,
-  _timer: null,
-
-  get isPreloaded() {
-    return this._browser &&
-           this._browser.contentDocument &&
-           this._browser.contentDocument.readyState === "complete" &&
-           this._browser.currentURI.spec === NEWTAB_URL;
-  },
-
-  swapWithNewTab: function (aTab) {
-    if (!this.isPreloaded || this._timer) {
-      return false;
-    }
-
-    let win = aTab.ownerDocument.defaultView;
-    let tabbrowser = win.gBrowser;
-
-    if (!tabbrowser) {
-      return false;
-    }
-
-    // Swap docShells.
-    tabbrowser.swapNewTabWithBrowser(aTab, this._browser);
-
-    // Load all delayed frame scripts attached to the "browers" message manager.
-    // The browser content script was already loaded, so don't load it again.
-    let mm = aTab.linkedBrowser.messageManager;
-    let scripts = win.getGroupMessageManager("browsers").getDelayedFrameScripts();
-    Array.forEach(scripts, ([script, runGlobal]) => {
-      if (script != BROWSER_CONTENT_SCRIPT) {
-        mm.loadFrameScript(script, true, runGlobal);
-      }
-    });
-
-    // Remove the browser, it will be recreated by a timer.
-    this._removeBrowser();
-
-    // Start a timer that will kick off preloading the next newtab page.
-    this._timer = createTimer(this, PRELOADER_INTERVAL_MS);
-
-    // Signal that we swapped docShells.
-    return true;
-  },
-
-  observe: function () {
-    this._timer = null;
-
-    // Start pre-loading the new tab page.
-    this._createBrowser();
-  },
-
-  resize: function (width, height) {
-    this._width = width;
-    this._height = height;
-    this._applySize();
-  },
-
-  destroy: function () {
-    this._removeBrowser();
-    this._timer = clearTimer(this._timer);
-  },
-
-  _applySize: function () {
-    if (this._browser) {
-      this._browser.style.width = this._width + "px";
-      this._browser.style.height = this._height + "px";
-    }
-  },
-
-  _createBrowser: function () {
-    HostFrame.get().then(aFrame => {
-      let doc = aFrame.document;
-      this._browser = doc.createElementNS(XUL_NS, "browser");
-      this._browser.setAttribute("type", "content");
-      this._browser.setAttribute("src", NEWTAB_URL);
-      this._applySize();
-      doc.getElementById("win").appendChild(this._browser);
-
-      // The browser might not have a docShell here if the HostFrame was
-      // destroyed while the promise was resolved. Simply bail out.
-      if (!this._browser.docShell) {
-        return;
-      }
-
-      // Let the docShell be inactive so that document.hidden=true.
-      this._browser.docShell.isActive = false;
-
-      this._browser.messageManager.loadFrameScript(BROWSER_CONTENT_SCRIPT,
-                                                   true);
-    });
-  },
-
-  _removeBrowser: function () {
-    if (this._browser) {
-      this._browser.remove();
-      this._browser = null;
-    }
-  }
-};
-
-let HostFrame = {
-  _frame: null,
-  _deferred: null,
-
-  get hiddenDOMDocument() {
-    return Services.appShell.hiddenDOMWindow.document;
-  },
-
-  get isReady() {
-    return this.hiddenDOMDocument.readyState === "complete";
-  },
-
-  get: function () {
-    if (!this._deferred) {
-      this._deferred = Promise.defer();
-      this._create();
-    }
-
-    return this._deferred.promise;
-  },
-
-  destroy: function () {
-    if (this._frame) {
-      if (!Cu.isDeadWrapper(this._frame)) {
-        this._frame.removeEventListener("load", this, true);
-        this._frame.remove();
-      }
-
-      this._frame = null;
-      this._deferred = null;
-    }
-  },
-
-  handleEvent: function () {
-    let contentWindow = this._frame.contentWindow;
-    if (contentWindow.location.href === XUL_PAGE) {
-      this._frame.removeEventListener("load", this, true);
-      this._deferred.resolve(contentWindow);
-    } else {
-      contentWindow.location = XUL_PAGE;
-    }
-  },
-
-  _create: function () {
-    if (this.isReady) {
-      let doc = this.hiddenDOMDocument;
-      this._frame = doc.createElementNS(HTML_NS, "iframe");
-      this._frame.addEventListener("load", this, true);
-      doc.documentElement.appendChild(this._frame);
-    } else {
-      let flags = Ci.nsIThread.DISPATCH_NORMAL;
-      Services.tm.currentThread.dispatch(() => this._create(), flags);
-    }
-  }
-};
--- a/browser/modules/UITour.jsm
+++ b/browser/modules/UITour.jsm
@@ -137,16 +137,26 @@ this.UITour = {
       query: (aDocument) => {
         let loopBrowser = aDocument.querySelector("#loop-notification-panel > #loop");
         if (!loopBrowser) {
           return null;
         }
         return loopBrowser.contentDocument.querySelector(".room-list");
       },
     }],
+    ["loop-selectedRoomButtons", {
+      infoPanelPosition: "leftcenter bottomright",
+      query: (aDocument) => {
+        let chatbox = aDocument.querySelector("chatbox[src^='about\:loopconversation'][selected]");
+        if (!chatbox || !chatbox.contentDocument) {
+          return null;
+        }
+        return chatbox.contentDocument.querySelector(".call-action-group");
+      },
+    }],
     ["loop-signInUpLink", {
       query: (aDocument) => {
         let loopBrowser = aDocument.querySelector("#loop-notification-panel > #loop");
         if (!loopBrowser) {
           return null;
         }
         return loopBrowser.contentDocument.querySelector(".signin-link");
       },
@@ -1370,18 +1380,17 @@ this.UITour = {
   },
 
   hideAppMenuAnnotations: function(aEvent) {
     UITour.hideAnnotationsForPanel(aEvent, UITour.targetIsInAppMenu);
   },
 
   hideLoopPanelAnnotations: function(aEvent) {
     UITour.hideAnnotationsForPanel(aEvent, (aTarget) => {
-      // TODO: Bug 1104927 - Handle the conversation targets separately.
-      return aTarget.targetName.startsWith("loop-");
+      return aTarget.targetName.startsWith("loop-") && aTarget.targetName != "loop-selectedRoomButtons";
     });
   },
 
   onPanelHidden: function(aEvent) {
     aEvent.target.removeAttribute("noautohide");
     UITour.recreatePopup(aEvent.target);
   },
 
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -6,17 +6,16 @@
 
 BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
 XPCSHELL_TESTS_MANIFESTS += [
     'test/unit/social/xpcshell.ini',
     'test/xpcshell/xpcshell.ini',
 ]
 
 EXTRA_JS_MODULES += [
-    'BrowserNewTabPreloader.jsm',
     'BrowserUITelemetry.jsm',
     'CastingApps.jsm',
     'Chat.jsm',
     'ContentClick.jsm',
     'ContentLinkHandler.jsm',
     'ContentSearch.jsm',
     'ContentWebRTC.jsm',
     'CustomizationTabPreloader.jsm',
--- a/browser/modules/test/browser_UITour_loop.js
+++ b/browser/modules/test/browser_UITour_loop.js
@@ -85,19 +85,22 @@ let tests = [
         }), "Info panel should be anchored to the new room button");
       });
     });
   },
   function test_notifyLoopChatWindowOpenedClosed(done) {
     gContentAPI.observe((event, params) => {
       is(event, "Loop:ChatWindowOpened", "Check Loop:ChatWindowOpened notification");
       gContentAPI.observe((event, params) => {
-        is(event, "Loop:ChatWindowClosed", "Check Loop:ChatWindowClosed notification");
+        is(event, "Loop:ChatWindowShown", "Check Loop:ChatWindowShown notification");
         gContentAPI.observe((event, params) => {
-          ok(false, "No more notifications should have arrived");
+          is(event, "Loop:ChatWindowClosed", "Check Loop:ChatWindowClosed notification");
+          gContentAPI.observe((event, params) => {
+            ok(false, "No more notifications should have arrived");
+          });
         });
         done();
       });
       document.querySelector("#pinnedchats > chatbox").close();
     });
     LoopRooms.open("fakeTourRoom");
   },
   taskify(function* test_arrow_panel_position() {
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -1340,16 +1340,21 @@ nsDocShell::LoadURI(nsIURI * aURI,
     // true; that case will get handled in LoadInternal or LoadHistoryEntry,
     // so we pass false as the second parameter to IsNavigationAllowed.
     // However, we don't allow the page to change location *in the middle of*
     // firing beforeunload, so we do need to check if *beforeunload* is currently
     // firing, so we call IsNavigationAllowed rather than just IsPrintingOrPP.
     if (!IsNavigationAllowed(true, false)) {
       return NS_OK; // JS may not handle returning of an error code
     }
+
+    if (DoAppRedirectIfNeeded(aURI, aLoadInfo, aFirstParty)) {
+      return NS_OK;
+    }
+
     nsCOMPtr<nsIURI> referrer;
     nsCOMPtr<nsIInputStream> postStream;
     nsCOMPtr<nsIInputStream> headersStream;
     nsCOMPtr<nsISupports> owner;
     bool inheritOwner = false;
     bool ownerIsExplicit = false;
     bool sendReferrer = true;
     uint32_t referrerPolicy = mozilla::net::RP_Default;
@@ -6581,20 +6586,16 @@ nsDocShell::ForceRefreshURI(nsIURI * aUR
         if (internalReferrer) {
             loadInfo->SetReferrer(internalReferrer);
         }
     }
     else {
         loadInfo->SetLoadType(nsIDocShellLoadInfo::loadRefresh);
     }
 
-    if (DoAppRedirectIfNeeded(aURI, loadInfo, true)) {
-      return NS_OK;
-    }
-
     /*
      * LoadURI(...) will cancel all refresh timers... This causes the
      * Timer and its refreshData instance to be released...
      */
     LoadURI(aURI, loadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, true);
 
     return NS_OK;
 }
--- a/mobile/android/base/overlays/service/OverlayActionService.java
+++ b/mobile/android/base/overlays/service/OverlayActionService.java
@@ -130,15 +130,15 @@ public class OverlayActionService extend
                         // Show a failure toast with a retry button.
                         OverlayToastHelper.showFailureToast(getApplicationContext(), shareMethod.getFailureMessage(), retryListener);
                         break;
                     case PERMANENT_FAILURE:
                         // Show a failure toast without a retry button.
                         OverlayToastHelper.showFailureToast(getApplicationContext(), shareMethod.getFailureMessage());
                         break;
                     default:
-                        Assert.isTrue(false, "Unknown share method result code: " + result);
+                        Assert.fail("Unknown share method result code: " + result);
                         break;
                 }
             }
         });
     }
 }
--- a/mobile/android/base/overlays/ui/SendTabDeviceListArrayAdapter.java
+++ b/mobile/android/base/overlays/ui/SendTabDeviceListArrayAdapter.java
@@ -158,17 +158,17 @@ public class SendTabDeviceListArrayAdapt
                 break;
             case NONE:
                 showDummyRecord(getContext().getResources().getString(R.string.overlay_share_send_tab_btn_label));
                 break;
             case SHOW_DEVICES:
                 showDummyRecord(getContext().getResources().getString(R.string.overlay_share_send_other));
                 break;
             default:
-                Assert.isTrue(false, "Unexpected state transition: " + newState);
+                Assert.fail("Unexpected state transition: " + newState);
         }
     }
 
     /**
      * Set the dummy override string to the given value and clear the list.
      */
     private void showDummyRecord(String name) {
         dummyRecordName = name;
--- a/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/Prefs.java
+++ b/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/Prefs.java
@@ -43,28 +43,25 @@ public  final class Prefs {
                     .remove("power_saving_mode")
                     .commit();
 
             getPrefs().edit().putInt(VALUES_VERSION_PREF, AppGlobals.appVersionCode).commit();
             getPrefs().edit().commit();
         }
     }
 
-    /* Prefs must be created on application startup or service startup.
-     * TODO: turn into regular singleton if Context dependency can be removed. */
-    public static void createGlobalInstance(Context c) {
-        if (sInstance != null) {
-            return;
+    public static Prefs getInstance(Context c) {
+        if (sInstance == null) {
+            sInstance = new Prefs(c);
         }
-        sInstance = new Prefs(c);
+        return sInstance;
     }
 
-    /* Only access after CreatePrefsInstance(Context) has been called at startup. */
-    public static Prefs getInstance() {
-        assert(sInstance != null);
+    // Allows code without a context handle to grab the prefs. The caller must null check the return value.
+    public static Prefs getInstanceWithoutContext() {
         return sInstance;
     }
 
     ///
     /// Setters
     ///
     public synchronized void setUserAgent(String userAgent) {
         setStringPref(USER_AGENT_PREF, userAgent);
--- a/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/stumblerthread/StumblerService.java
+++ b/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/stumblerthread/StumblerService.java
@@ -1,27 +1,27 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.mozstumbler.service.stumblerthread;
 
+import android.content.Context;
 import android.content.Intent;
 import android.location.Location;
 import android.os.AsyncTask;
 import android.util.Log;
 import java.io.IOException;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.mozilla.mozstumbler.service.AppGlobals;
 import org.mozilla.mozstumbler.service.Prefs;
 import org.mozilla.mozstumbler.service.stumblerthread.blocklist.WifiBlockListInterface;
 import org.mozilla.mozstumbler.service.stumblerthread.datahandling.DataStorageManager;
 import org.mozilla.mozstumbler.service.stumblerthread.scanners.ScanManager;
-import org.mozilla.mozstumbler.service.stumblerthread.scanners.cellscanner.CellScanner;
 import org.mozilla.mozstumbler.service.uploadthread.UploadAlarmReceiver;
 import org.mozilla.mozstumbler.service.utils.NetworkUtils;
 import org.mozilla.mozstumbler.service.utils.PersistentIntentService;
 
 // In stand-alone service mode (a.k.a passive scanning mode), this is created from PassiveServiceReceiver (by calling startService).
 // The StumblerService is a sticky unbound service in this usage.
 //
 public class StumblerService extends PersistentIntentService
@@ -63,18 +63,18 @@ public class StumblerService extends Per
     }
 
     // This is optional, not used in Fennec, and is for clients to specify a (potentially long) list
     // of blocklisted SSIDs/BSSIDs
     public void setWifiBlockList(WifiBlockListInterface list) {
         mScanManager.setWifiBlockList(list);
     }
 
-    public Prefs getPrefs() {
-        return Prefs.getInstance();
+    public Prefs getPrefs(Context c) {
+        return Prefs.getInstance(c);
     }
 
     public void checkPrefs() {
         mScanManager.checkPrefs();
     }
 
     public int getLocationCount() {
         return mScanManager.getLocationCount();
@@ -111,17 +111,18 @@ public class StumblerService extends Per
     public boolean isGeofenced () {
         return mScanManager.isGeofenced();
     }
 
     // Previously this was done in onCreate(). Moved out of that so that in the passive standalone service
     // use (i.e. Fennec), init() can be called from this class's dedicated thread.
     // Safe to call more than once, ensure added code complies with that intent.
     protected void init() {
-        Prefs.createGlobalInstance(this);
+        // Ensure Prefs is created, so internal utility code can use getInstanceWithoutContext
+        Prefs.getInstance(this);
         NetworkUtils.createGlobalInstance(this);
         DataStorageManager.createGlobalInstance(this, this);
 
         mReporter.startup(this);
     }
 
     // Called from the main thread.
     @Override
@@ -145,17 +146,17 @@ public class StumblerService extends Per
         new AsyncTask<Void, Void, Void>() {
             @Override
             protected Void doInBackground(Void... params) {
                 if (AppGlobals.isDebug) {
                     Log.d(LOG_TAG, "onDestroy");
                 }
 
                 if (!sFirefoxStumblingEnabled.get()) {
-                    Prefs.getInstance().setFirefoxScanEnabled(false);
+                    Prefs.getInstance(StumblerService.this).setFirefoxScanEnabled(false);
                 }
 
                 if (DataStorageManager.getInstance() != null) {
                     try {
                         DataStorageManager.getInstance().saveCurrentReportsToDisk();
                     } catch (IOException ex) {
                         AppGlobals.guiLogInfo(ex.toString());
                         Log.e(LOG_TAG, "Exception in onDestroy saving reports" + ex.toString());
@@ -177,58 +178,58 @@ public class StumblerService extends Per
 
         // Post-init(), set the mode to passive.
         mScanManager.setPassiveMode(true);
 
         if (intent == null) {
             return;
         }
 
-        final boolean isScanEnabledInPrefs = Prefs.getInstance().getFirefoxScanEnabled();
+        final boolean isScanEnabledInPrefs = Prefs.getInstance(this).getFirefoxScanEnabled();
 
         if (!isScanEnabledInPrefs && intent.getBooleanExtra(ACTION_NOT_FROM_HOST_APP, false)) {
             stopSelf();
             return;
         }
 
         boolean hasFilesWaiting = !DataStorageManager.getInstance().isDirEmpty();
         if (AppGlobals.isDebug) {
             Log.d(LOG_TAG, "Files waiting:" + hasFilesWaiting);
         }
         if (hasFilesWaiting) {
             // non-empty on startup, schedule an upload
             // This is the only upload trigger in Firefox mode
             // Firefox triggers this ~4 seconds after startup (after Gecko is loaded), add a small delay to avoid
             // clustering with other operations that are triggered at this time.
-            final long lastAttemptedTime = Prefs.getInstance().getLastAttemptedUploadTime();
+            final long lastAttemptedTime = Prefs.getInstance(this).getLastAttemptedUploadTime();
             final long timeNow = System.currentTimeMillis();
 
             if (timeNow - lastAttemptedTime < PASSIVE_UPLOAD_FREQ_GUARD_MSEC) {
                 // TODO Consider telemetry to track this.
                 if (AppGlobals.isDebug) {
                     Log.d(LOG_TAG, "Upload attempt too frequent.");
                 }
             } else {
-                Prefs.getInstance().setLastAttemptedUploadTime(timeNow);
+                Prefs.getInstance(this).setLastAttemptedUploadTime(timeNow);
                 UploadAlarmReceiver.scheduleAlarm(this, DELAY_IN_SEC_BEFORE_STARTING_UPLOAD_IN_PASSIVE_MODE, false /* no repeat*/);
             }
         }
 
         if (!isScanEnabledInPrefs) {
-            Prefs.getInstance().setFirefoxScanEnabled(true);
+            Prefs.getInstance(this).setFirefoxScanEnabled(true);
         }
 
         String apiKey = intent.getStringExtra(ACTION_EXTRA_MOZ_API_KEY);
-        if (apiKey != null && !apiKey.equals(Prefs.getInstance().getMozApiKey())) {
-            Prefs.getInstance().setMozApiKey(apiKey);
+        if (apiKey != null && !apiKey.equals(Prefs.getInstance(this).getMozApiKey())) {
+            Prefs.getInstance(this).setMozApiKey(apiKey);
         }
 
         String userAgent = intent.getStringExtra(ACTION_EXTRA_USER_AGENT);
-        if (userAgent != null && !userAgent.equals(Prefs.getInstance().getUserAgent())) {
-            Prefs.getInstance().setUserAgent(userAgent);
+        if (userAgent != null && !userAgent.equals(Prefs.getInstance(this).getUserAgent())) {
+            Prefs.getInstance(this).setUserAgent(userAgent);
         }
 
         if (!mScanManager.isScanning()) {
             startScanning();
         }
     }
 
     // Note that in passive mode, having data isn't an upload trigger, it is triggered by the start intent
--- a/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/stumblerthread/scanners/GPSScanner.java
+++ b/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/stumblerthread/scanners/GPSScanner.java
@@ -159,17 +159,21 @@ public class GPSScanner implements Locat
         return mLocation;
     }
 
     public void checkPrefs() {
         if (mBlockList != null) {
             mBlockList.updateBlocks();
         }
 
-        mAutoGeofencing = Prefs.getInstance().getGeofenceHere();
+        Prefs prefs = Prefs.getInstanceWithoutContext();
+        if (prefs == null) {
+            return;
+        }
+        mAutoGeofencing = prefs.getGeofenceHere();
     }
 
     public boolean isGeofenced() {
         return (mBlockList != null) && mBlockList.isGeofenced();
     }
 
     private void sendToLogActivity(String msg) {
         AppGlobals.guiLogInfo(msg, "#33ccff", false);
@@ -181,17 +185,17 @@ public class GPSScanner implements Locat
             reportLocationLost();
             return;
         }
 
         String logMsg = (mIsPassiveMode)? "[Passive] " : "[Active] ";
 
         String provider = location.getProvider();
         if (!provider.toLowerCase().contains("gps")) {
-            sendToLogActivity(logMsg + "Discard fused/network location.");
+            Log.d(LOG_TAG, "Discard fused/network location.");
             // only interested in GPS locations
             return;
         }
 
         // Seem to get greater likelihood of non-fused location with higher update freq.
         // Check dist and time threshold here, not set on the listener.
         if (mIsPassiveMode) {
             final long timeDelta = location.getTime() - mLocation.getTime();
--- a/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/stumblerthread/scanners/LocationBlockList.java
+++ b/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/stumblerthread/scanners/LocationBlockList.java
@@ -23,18 +23,22 @@ public final class LocationBlockList {
     private boolean mGeofencingEnabled;
     private boolean mIsGeofenced = false;
 
     public LocationBlockList() {
         updateBlocks();
     }
 
     public void updateBlocks()    {
-        mBlockedLocation = Prefs.getInstance().getGeofenceLocation();
-        mGeofencingEnabled = Prefs.getInstance().getGeofenceEnabled();
+        Prefs prefs = Prefs.getInstanceWithoutContext();
+        if (prefs == null) {
+            return;
+        }
+        mBlockedLocation = prefs.getGeofenceLocation();
+        mGeofencingEnabled = prefs.getGeofenceEnabled();
     }
 
     public boolean contains(Location location) {
         final float inaccuracy = location.getAccuracy();
         final double altitude = location.getAltitude();
         final float bearing = location.getBearing();
         final double latitude = location.getLatitude();
         final double longitude = location.getLongitude();
--- a/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/stumblerthread/scanners/WifiScanner.java
+++ b/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/stumblerthread/scanners/WifiScanner.java
@@ -69,22 +69,23 @@ public class WifiScanner extends Broadca
         if (manager == null) {
             return null;
         }
         return getWifiManager().getScanResults();
     }
 
 
     public synchronized void start(final ActiveOrPassiveStumbling stumblingMode) {
-        if (mStarted) {
+        Prefs prefs = Prefs.getInstanceWithoutContext();
+        if (mStarted || prefs == null) {
             return;
         }
         mStarted = true;
 
-        boolean scanAlways = Prefs.getInstance().getWifiScanAlways();
+        boolean scanAlways = prefs.getWifiScanAlways();
 
         if (scanAlways || isWifiEnabled()) {
             activatePeriodicScan(stumblingMode);
         }
 
         IntentFilter i = new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
         if (!scanAlways) i.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
         mContext.registerReceiver(this, i);
--- a/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/uploadthread/AsyncUploader.java
+++ b/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/uploadthread/AsyncUploader.java
@@ -107,20 +107,16 @@ public class AsyncUploader extends Async
     @Override
     protected void onCancelled(SyncSummary result) {
         sIsUploading.set(false);
     }
 
     private class Submitter extends AbstractCommunicator {
         private static final String SUBMIT_URL = "https://location.services.mozilla.com/v1/submit";
 
-        public Submitter() {
-            super(Prefs.getInstance().getUserAgent());
-        }
-
         @Override
         public String getUrlString() {
             return SUBMIT_URL;
         }
 
         @Override
         public String getNickname(){
             return mNickname;
--- a/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/uploadthread/UploadAlarmReceiver.java
+++ b/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/uploadthread/UploadAlarmReceiver.java
@@ -43,17 +43,18 @@ public class UploadAlarmReceiver extends
         }
 
         public UploadAlarmService() {
             super(LOG_TAG);
         }
 
         @Override
         protected void onHandleIntent(Intent intent) {
-            boolean isRepeating = intent.getBooleanExtra(EXTRA_IS_REPEATING, true);
+            // Default to a repeating alarm, which is what Fennec Stumbler uses
+            boolean isRepeating = (intent == null)? true : intent.getBooleanExtra(EXTRA_IS_REPEATING, true);
             if (DataStorageManager.getInstance() == null) {
                 DataStorageManager.createGlobalInstance(this, null);
             }
             upload(isRepeating);
         }
 
         void upload(boolean isRepeating) {
             if (!isRepeating) {
@@ -72,19 +73,19 @@ public class UploadAlarmReceiver extends
                     return;
                 }
             }
 
             if (NetworkUtils.getInstance().isWifiAvailable() &&
                 !AsyncUploader.isUploading()) {
                 Log.d(LOG_TAG, "Alarm upload(), call AsyncUploader");
                 AsyncUploader.UploadSettings settings =
-                    new AsyncUploader.UploadSettings(Prefs.getInstance().getWifiScanAlways(), Prefs.getInstance().getUseWifiOnly());
+                    new AsyncUploader.UploadSettings(Prefs.getInstance(this).getWifiScanAlways(), Prefs.getInstance(this).getUseWifiOnly());
                 AsyncUploader uploader = new AsyncUploader(settings, null);
-                uploader.setNickname(Prefs.getInstance().getNickname());
+                uploader.setNickname(Prefs.getInstance(this).getNickname());
                 uploader.execute();
                 // we could listen for completion and cancel, instead, cancel on next alarm when db empty
             }
         }
     }
 
     static PendingIntent createIntent(Context c, boolean isRepeating) {
         Intent intent = new Intent(c, UploadAlarmReceiver.class);
--- a/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/utils/AbstractCommunicator.java
+++ b/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/utils/AbstractCommunicator.java
@@ -56,24 +56,26 @@ public abstract class AbstractCommunicat
     }
 
     public abstract NetworkSendResult cleanSend(byte[] data);
 
     public String getNickname() {
         return null;
     }
 
-    public AbstractCommunicator(String userAgent) {
-        mUserAgent = userAgent;
+    public AbstractCommunicator() {
+        Prefs prefs = Prefs.getInstanceWithoutContext();
+        mUserAgent = (prefs != null)? prefs.getUserAgent() : "fennec-stumbler-unset-user-agent";
     }
 
     private void openConnectionAndSetHeaders() {
         try {
-            if (sMozApiKey == null) {
-                sMozApiKey = Prefs.getInstance().getMozApiKey();
+            Prefs prefs = Prefs.getInstanceWithoutContext();
+            if (sMozApiKey == null || prefs != null) {
+                sMozApiKey = prefs.getMozApiKey();
             }
             URL url = new URL(getUrlString() + "?key=" + sMozApiKey);
             mHttpURLConnection = (HttpURLConnection) url.openConnection();
             mHttpURLConnection.setRequestMethod("POST");
         } catch (MalformedURLException e) {
             throw new IllegalArgumentException(e);
         } catch (IOException e) {
             Log.e(LOG_TAG, "Couldn't open a connection: " + e);
--- a/testing/mochitest/browser-test.js
+++ b/testing/mochitest/browser-test.js
@@ -10,19 +10,16 @@ if (Cc === undefined) {
 }
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Services",
   "resource://gre/modules/Services.jsm");
 
-XPCOMUtils.defineLazyModuleGetter(this, "BrowserNewTabPreloader",
-  "resource:///modules/BrowserNewTabPreloader.jsm");
-
 XPCOMUtils.defineLazyModuleGetter(this, "CustomizationTabPreloader",
   "resource:///modules/CustomizationTabPreloader.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "ContentSearch",
   "resource:///modules/ContentSearch.jsm");
 
 const SIMPLETEST_OVERRIDES =
   ["ok", "is", "isnot", "ise", "todo", "todo_is", "todo_isnot", "info", "expectAssertions", "requestCompleteLog"];
@@ -499,17 +496,23 @@ Tester.prototype = {
           socialSidebar.docShell.createAboutBlankContentViewer(null);
           socialSidebar.setAttribute("src", "about:blank");
 
           // Destroy BackgroundPageThumbs resources.
           let {BackgroundPageThumbs} =
             Cu.import("resource://gre/modules/BackgroundPageThumbs.jsm", {});
           BackgroundPageThumbs._destroy();
 
-          BrowserNewTabPreloader.uninit();
+          // Destroy preloaded browsers.
+          if (gBrowser._preloadedBrowser) {
+            let browser = gBrowser._preloadedBrowser;
+            gBrowser._preloadedBrowser = null;
+            gBrowser.getNotificationBox(browser).remove();
+          }
+
           CustomizationTabPreloader.uninit();
           SocialFlyout.unload();
           SocialShare.uninit();
           TabView.uninit();
         }
 
         // Schedule GC and CC runs before finishing in order to detect
         // DOM windows leaked by our tests or the tested code. Note that we
--- a/toolkit/devtools/server/actors/highlighter.js
+++ b/toolkit/devtools/server/actors/highlighter.js
@@ -679,26 +679,25 @@ AutoRefreshHighlighter.prototype = {
   _hide: function() {
     // To be implemented by sub classes
     // When called, sub classes should actually hide the highlighter
   },
 
   _startRefreshLoop: function() {
     let win = this.currentNode.ownerDocument.defaultView;
     this.rafID = win.requestAnimationFrame(this._startRefreshLoop.bind(this));
+    this.rafWin = win;
     this.update();
   },
 
   _stopRefreshLoop: function() {
-    if (!this.rafID) {
-      return;
+    if (this.rafID && !Cu.isDeadWrapper(this.rafWin)) {
+      this.rafWin.cancelAnimationFrame(this.rafID);
     }
-    let win = this.currentNode.ownerDocument.defaultView;
-    win.cancelAnimationFrame(this.rafID);
-    this.rafID = null;
+    this.rafID = this.rafWin = null;
   },
 
   destroy: function() {
     this.hide();
 
     this.tabActor = null;
     this.win = null;
     this.browser = null;