Merge m-c to mozilla-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 25 Apr 2014 13:36:03 +0200
changeset 198637 7711e1bc8d4b5f1d8f9ccea14d8e4c18e5a079e7
parent 198636 3d0c2e8d8f4eabd8f2bd0f6f33003772cdd29dcd (current diff)
parent 198627 09a19b25b9cf0fd8fba893bfa7ebc28aa99fe32a (diff)
child 198638 a14c6bf99d13848a30b4287de0e0d663da5763fc
push id3624
push userasasaki@mozilla.com
push dateMon, 09 Jun 2014 21:49:01 +0000
treeherdermozilla-beta@b1a5da15899a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone31.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 m-c to mozilla-inbound
browser/themes/shared/devtools/images/eyedropper-black.png
content/xul/document/test/test_documentnotification.xul
content/xul/document/test/window_documentnotification.xul
--- a/b2g/components/B2GComponents.manifest
+++ b/b2g/components/B2GComponents.manifest
@@ -81,9 +81,14 @@ contract @mozilla.org/fxaccounts/fxaccou
 # HelperAppDialog.js
 component {710322af-e6ae-4b0c-b2c9-1474a87b077e} HelperAppDialog.js
 contract @mozilla.org/helperapplauncherdialog;1 {710322af-e6ae-4b0c-b2c9-1474a87b077e}
 
 #ifndef MOZ_WIDGET_GONK
 component {c83c02c0-5d43-4e3e-987f-9173b313e880} SimulatorScreen.js
 contract @mozilla.org/simulator-screen;1 {c83c02c0-5d43-4e3e-987f-9173b313e880}
 category profile-after-change SimulatorScreen @mozilla.org/simulator-screen;1
+
+component {e30b0e13-2d12-4cb0-bc4c-4e617a1bf76e} OopCommandLine.js
+contract @mozilla.org/commandlinehandler/general-startup;1?type=b2goop {e30b0e13-2d12-4cb0-bc4c-4e617a1bf76e}
+category command-line-handler m-b2goop @mozilla.org/commandlinehandler/general-startup;1?type=b2goop
 #endif
+
new file mode 100644
--- /dev/null
+++ b/b2g/components/OopCommandLine.js
@@ -0,0 +1,47 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
+
+function oopCommandlineHandler() {
+}
+
+oopCommandlineHandler.prototype = {
+    handle: function(cmdLine) {
+        let oopFlag = cmdLine.handleFlag("oop", false);
+        if (oopFlag) {
+            /**
+             * Manipulate preferences by adding to the *default* branch.  Adding
+             * to the default branch means the changes we make won"t get written
+             * back to user preferences.
+             */
+            let prefs = Services.prefs
+            let branch = prefs.getDefaultBranch("");
+
+            try {
+                // Turn on all OOP services, making desktop run similar to phone
+                // environment
+                branch.setBoolPref("dom.ipc.tabs.disabled", false);
+                branch.setBoolPref("layers.acceleration.disabled", false);
+                branch.setBoolPref("layers.offmainthreadcomposition.enabled", true);
+                branch.setBoolPref("layers.offmainthreadcomposition.async-animations", true);
+                branch.setBoolPref("layers.async-video.enabled", true);
+                branch.setBoolPref("layers.async-pan-zoom.enabled", true);
+                branch.setCharPref("gfx.content.azure.backends", "cairo");
+            } catch (e) { }
+
+        }
+        if (cmdLine.state == Ci.nsICommandLine.STATE_REMOTE_AUTO) {
+            cmdLine.preventDefault = true;
+        }
+    },
+
+    helpInfo: "  -oop         Use out-of-process model in B2G\n",
+    classID: Components.ID("{e30b0e13-2d12-4cb0-bc4c-4e617a1bf76e}"),
+    QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler]),
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([oopCommandlineHandler]);
--- a/b2g/components/moz.build
+++ b/b2g/components/moz.build
@@ -20,16 +20,17 @@ EXTRA_COMPONENTS += [
     'SmsProtocolHandler.js',
     'TelProtocolHandler.js',
     'WebappsUpdateTimer.js',
     'YoutubeProtocolHandler.js',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
     EXTRA_COMPONENTS += [
+      'OopCommandLine.js',
       'SimulatorScreen.js'
     ]
 
 EXTRA_PP_COMPONENTS += [
     'B2GComponents.manifest',
     'DirectoryProvider.js',
     'RecoveryService.js',
 ]
--- 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="2a165bebfa19b11b697837409f9550dd2917c46c">
     <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="73b5d6a8773aa048054119bf5b3ca0d005b5494e"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="facd91d31db983a60c7f1035ca01b727c7a1de65"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="0292e64ef8451df104dcf9ac3b2c6749b81684dd"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="85f9690323b235f4dcf2901ea2240d3c60fc22a0"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
   <!-- 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="e6383e6e785cc3ea237e902beb1092f9aa88e29d">
     <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="73b5d6a8773aa048054119bf5b3ca0d005b5494e"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="facd91d31db983a60c7f1035ca01b727c7a1de65"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
   <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="65fba428f8d76336b33ddd9e15900357953600ba">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="73b5d6a8773aa048054119bf5b3ca0d005b5494e"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="facd91d31db983a60c7f1035ca01b727c7a1de65"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <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="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
   <!-- Stock Android things -->
--- 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="2a165bebfa19b11b697837409f9550dd2917c46c">
     <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="73b5d6a8773aa048054119bf5b3ca0d005b5494e"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="facd91d31db983a60c7f1035ca01b727c7a1de65"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="0292e64ef8451df104dcf9ac3b2c6749b81684dd"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="85f9690323b235f4dcf2901ea2240d3c60fc22a0"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame/releng-flame.tt
+++ b/b2g/config/flame/releng-flame.tt
@@ -1,7 +1,7 @@
 [
-{"size": 169226356,
-"digest": "f9456848fd661b8be05d6a30607fad4787bcecfe676b53f9a4074653fdda6a377c961038c866c5d83355e3afd89f1a3bd947a142aeaf7dd7d81b6c376185badd",
+{"size": 156447892,
+"digest": "02b2e6bcaff4ccadfd85a75cc1dfb526be7937673ed18b2c6fb7fe2256a725bc778d513e3820d86adaec1636e1771bd0c5663e17bf2d3f1d6b445ff1e0a136f2",
 "filename": "backup-flame.tar.xz",
 "algorithm": "sha512"
 }
 ]
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -13,17 +13,17 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e6383e6e785cc3ea237e902beb1092f9aa88e29d">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="73b5d6a8773aa048054119bf5b3ca0d005b5494e"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="facd91d31db983a60c7f1035ca01b727c7a1de65"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
   <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"/>
@@ -115,17 +115,17 @@
   <project name="platform/system/netd" path="system/netd" revision="ea8103eae5642621ca8202e00620f4ca954ed413"/>
   <project name="platform/system/security" path="system/security" revision="360f51f7af191316cd739f229db1c5f7233be063"/>
   <project name="platform/system/vold" path="system/vold" revision="153df4d067a4149c7d78f1c92fed2ce2bd6a272e"/>
   <default remote="caf" revision="jb_3.2" sync-j="4"/>
   <!-- Flame specific things -->
   <project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="e8a318f7690092e639ba88891606f4183e846d3f"/>
   <project name="device/qcom/common" path="device/qcom/common" revision="234ed34543345f58c0d4dcb1aa012de68802b9dc"/>
   <project name="device-flame" path="device/t2m/flame" remote="b2g" revision="9729afa15ae3362db1852eee60422947db614dd6"/>
-  <project name="kernel/msm" path="kernel" revision="b3092c54430df89636fb0670d32058bc63474017"/>
+  <project name="kernel/msm" path="kernel" revision="3f7af9ae7ef30dc1c37972ed0ad957fc64219f31"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="f2914eacee9120680a41463708bb6ee8291749fc"/>
   <project name="platform/external/bluetooth/bluedroid" path="external/bluetooth/bluedroid" revision="fa892235a9bd8983f8b591129fc1a9398f64e514"/>
   <project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="f0689ac1914cdbc59e53bdc9edd9013dc157c299"/>
   <project name="platform/external/bluetooth/glib" path="external/bluetooth/glib" revision="dd925f76e4f149c3d5571b80e12f7e24bbe89c59"/>
   <project name="platform/external/dbus" path="external/dbus" revision="ea87119c843116340f5df1d94eaf8275e1055ae8"/>
   <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="320b05a5761eb2a4816f7529c91ea49422979b55"/>
   <project name="platform/frameworks/av" path="frameworks/av" revision="1df6dac11d7370a2fffca8e31d65b80f537faec5"/>
   <project name="platform/frameworks/base" path="frameworks/base" revision="807d87d5ff66cb5e0664f6924f612fcdb5e2c453"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "git_revision": "", 
         "remote": "", 
         "branch": ""
     }, 
-    "revision": "d3ee2aca1f8e4d7e1c721f445d6956d73066126b", 
+    "revision": "2fccee502f455ba2ca7178efa5cf247d90df8afb", 
     "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="2a165bebfa19b11b697837409f9550dd2917c46c">
     <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="73b5d6a8773aa048054119bf5b3ca0d005b5494e"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="facd91d31db983a60c7f1035ca01b727c7a1de65"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- 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="2a165bebfa19b11b697837409f9550dd2917c46c">
     <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="73b5d6a8773aa048054119bf5b3ca0d005b5494e"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="facd91d31db983a60c7f1035ca01b727c7a1de65"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <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/inari/sources.xml
+++ b/b2g/config/inari/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="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="2a165bebfa19b11b697837409f9550dd2917c46c">
     <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="73b5d6a8773aa048054119bf5b3ca0d005b5494e"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="facd91d31db983a60c7f1035ca01b727c7a1de65"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="cd5dfce80bc3f0139a56b58aca633202ccaee7f8"/>
--- a/b2g/config/leo/sources.xml
+++ b/b2g/config/leo/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="2a165bebfa19b11b697837409f9550dd2917c46c">
     <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="73b5d6a8773aa048054119bf5b3ca0d005b5494e"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="facd91d31db983a60c7f1035ca01b727c7a1de65"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/b2g/config/mako/sources.xml
+++ b/b2g/config/mako/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="e6383e6e785cc3ea237e902beb1092f9aa88e29d">
     <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="73b5d6a8773aa048054119bf5b3ca0d005b5494e"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="facd91d31db983a60c7f1035ca01b727c7a1de65"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
   <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="2a165bebfa19b11b697837409f9550dd2917c46c">
     <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="73b5d6a8773aa048054119bf5b3ca0d005b5494e"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="facd91d31db983a60c7f1035ca01b727c7a1de65"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -426,16 +426,17 @@
 #endif // MOZ_WIDGET_GONK && MOZ_B2G_RIL
 
 #ifndef MOZ_WIDGET_GONK
 @BINPATH@/components/extensions.manifest
 @BINPATH@/components/addonManager.js
 @BINPATH@/components/amContentHandler.js
 @BINPATH@/components/amWebInstallListener.js
 @BINPATH@/components/nsBlocklistService.js
+@BINPATH@/components/OopCommandLine.js
 #endif
 
 #ifdef MOZ_UPDATER
 @BINPATH@/components/nsUpdateService.manifest
 @BINPATH@/components/nsUpdateService.js
 @BINPATH@/components/nsUpdateServiceStub.js
 #endif
 @BINPATH@/components/nsUpdateTimerManager.manifest
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1223,27 +1223,28 @@ pref("devtools.appmanager.enabled", true
 pref("devtools.appmanager.lastTab", "help");
 pref("devtools.appmanager.manifestEditor.enabled", true);
 
 // Toolbox preferences
 pref("devtools.toolbox.footer.height", 250);
 pref("devtools.toolbox.sidebar.width", 500);
 pref("devtools.toolbox.host", "bottom");
 pref("devtools.toolbox.selectedTool", "webconsole");
-pref("devtools.toolbox.toolbarSpec", '["splitconsole", "paintflashing toggle","tilt toggle","scratchpad","resize toggle"]');
+pref("devtools.toolbox.toolbarSpec", '["splitconsole", "paintflashing toggle","tilt toggle","scratchpad","resize toggle","eyedropper"]');
 pref("devtools.toolbox.sideEnabled", true);
 pref("devtools.toolbox.zoomValue", "1");
 
 // Toolbox Button preferences
 pref("devtools.command-button-pick.enabled", true);
 pref("devtools.command-button-splitconsole.enabled", true);
 pref("devtools.command-button-paintflashing.enabled", false);
 pref("devtools.command-button-tilt.enabled", false);
 pref("devtools.command-button-scratchpad.enabled", false);
 pref("devtools.command-button-responsive.enabled", true);
+pref("devtools.command-button-eyedropper.enabled", false);
 
 // Inspector preferences
 // Enable the Inspector
 pref("devtools.inspector.enabled", true);
 // What was the last active sidebar in the inspector
 pref("devtools.inspector.activeSidebar", "ruleview");
 // Enable the markup preview
 pref("devtools.inspector.markupPreview", false);
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -186,16 +186,70 @@ let AboutHomeListener = {
       case "settings":
         sendAsyncMessage("AboutHome:Settings");
         break;
     }
   },
 };
 AboutHomeListener.init(this);
 
+
+let ContentSearchMediator = {
+
+  whitelist: new Set([
+    "about:newtab",
+  ]),
+
+  init: function (chromeGlobal) {
+    chromeGlobal.addEventListener("ContentSearchClient", this, true, true);
+    addMessageListener("ContentSearch", this);
+  },
+
+  handleEvent: function (event) {
+    if (this._contentWhitelisted) {
+      this._sendMsg(event.detail.type, event.detail.data);
+    }
+  },
+
+  receiveMessage: function (msg) {
+    if (msg.data.type == "AddToWhitelist") {
+      for (let uri of msg.data.data) {
+        this.whitelist.add(uri);
+      }
+      this._sendMsg("AddToWhitelistAck");
+      return;
+    }
+    if (this._contentWhitelisted) {
+      this._fireEvent(msg.data.type, msg.data.data);
+    }
+  },
+
+  get _contentWhitelisted() {
+    return this.whitelist.has(content.document.documentURI.toLowerCase());
+  },
+
+  _sendMsg: function (type, data=null) {
+    sendAsyncMessage("ContentSearch", {
+      type: type,
+      data: data,
+    });
+  },
+
+  _fireEvent: function (type, data=null) {
+    content.dispatchEvent(new content.CustomEvent("ContentSearchService", {
+      detail: {
+        type: type,
+        data: data,
+      },
+    }));
+  },
+};
+ContentSearchMediator.init(this);
+
+
 var global = this;
 
 // Lazily load the finder code
 addMessageListener("Finder:Initialize", function () {
   let {RemoteFinderListener} = Cu.import("resource://gre/modules/RemoteFinder.jsm", {});
   new RemoteFinderListener(global);
 });
 
--- a/browser/base/content/newtab/grid.js
+++ b/browser/base/content/newtab/grid.js
@@ -192,21 +192,27 @@ let gGrid = {
     if (this._cellMargin === undefined) {
       let refCell = document.querySelector(".newtab-cell");
       this._cellMargin = parseFloat(getComputedStyle(refCell).marginTop) * 2;
       this._cellHeight = refCell.offsetHeight + this._cellMargin;
       this._cellWidth = refCell.offsetWidth + this._cellMargin;
     }
 
     let availSpace = document.documentElement.clientHeight - this._cellMargin -
-                     document.querySelector("#newtab-margin-undo-container").offsetHeight;
+                     document.querySelector("#newtab-margin-undo-container").offsetHeight -
+                     document.querySelector("#newtab-search-form").offsetHeight;
     let visibleRows = Math.floor(availSpace / this._cellHeight);
     this._node.style.height = this._computeHeight() + "px";
     this._node.style.maxHeight = this._computeHeight(visibleRows) + "px";
     this._node.style.maxWidth = gGridPrefs.gridColumns * this._cellWidth +
                                 GRID_WIDTH_EXTRA + "px";
+
+    // Resize the search bar.
+    let width = parseFloat(window.getComputedStyle(this._node).width);
+    let visibleCols = Math.floor(width / this._cellWidth);
+    gSearch.setWidth(visibleCols * this._cellWidth - this._cellMargin);
   },
 
   _shouldRenderGrid : function Grid_shouldRenderGrid() {
     let cellsLength = this._node.querySelectorAll(".newtab-cell").length;
     return cellsLength != (gGridPrefs.gridRows * gGridPrefs.gridColumns);
   }
 };
--- a/browser/base/content/newtab/newTab.css
+++ b/browser/base/content/newtab/newTab.css
@@ -1,22 +1,28 @@
 /* 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/. */
 
+input {
+  font: message-box !important;
+  font-size: 16px !important;
+}
+
 input[type=button] {
   cursor: pointer;
 }
 
 /* SCROLLBOX */
 #newtab-scrollbox {
   display: -moz-box;
   position: relative;
   -moz-box-flex: 1;
   -moz-user-focus: normal;
+  -moz-box-orient: vertical;
 }
 
 #newtab-scrollbox:not([page-disabled]) {
   overflow: auto;
 }
 
 /* UNDO */
 #newtab-undo-container {
@@ -49,28 +55,36 @@ input[type=button] {
   position: relative;
   -moz-box-flex: 1;
   -moz-box-orient: vertical;
 }
 
 #newtab-margin-undo-container {
   display: -moz-box;
   -moz-box-pack: center;
+  margin-bottom: 26px; /* 32 - 6 search form top "padding" */
 }
 
 #newtab-horizontal-margin {
   display: -moz-box;
   -moz-box-flex: 1;
 }
 
 #newtab-margin-top,
 #newtab-margin-bottom {
   display: -moz-box;
+  position: relative;
+}
+
+#newtab-margin-top {
   -moz-box-flex: 1;
-  position: relative;
+}
+
+#newtab-margin-bottom {
+  -moz-box-flex: 2;
 }
 
 .newtab-side-margin {
   min-width: 16px;
   -moz-box-flex: 1;
 }
 
 /* GRID */
@@ -208,20 +222,173 @@ input[type=button] {
  */
 .newtab-drag {
   width: 1px;
   height: 1px;
   background-color: #fff;
   opacity: 0.01;
 }
 
-/* PANEL */
+/* SPONSORED PANEL */
 #sponsored-panel {
   width: 330px;
 }
 
 #sponsored-panel description {
   margin: 0;
 }
 
 #sponsored-panel .text-link {
   margin: 12px 0 0;
 }
+
+/* SEARCH */
+#newtab-search-container {
+  display: -moz-box;
+  position: relative;
+  -moz-box-align: center;
+  -moz-box-pack: center;
+}
+
+#newtab-search-container[page-disabled] {
+  opacity: 0;
+  pointer-events: none;
+}
+
+#newtab-search-form {
+  display: -moz-box;
+  -moz-box-orient: horizontal;
+  -moz-box-align: center;
+  height: 44px; /* 32 + 6 logo top "padding" + 6 logo bottom "padding" */
+  margin-bottom: 10px; /* 32 - 16 tiles top margin - 6 logo bottom "padding" */
+}
+
+#newtab-search-logo {
+  display: -moz-box;
+  width: 77px; /* 65 image width + 6 left "padding" + 6 right "padding" */
+  height: 38px; /* 26 image height + 6 top "padding" + 6 bottom "padding" */
+  border: 1px solid transparent;
+  -moz-margin-end: 8px;
+  background-repeat: no-repeat;
+  background-position: center;
+  background-size: 65px 26px;
+}
+
+#newtab-search-logo[hidden] {
+  display: none;
+}
+
+#newtab-search-logo[active],
+#newtab-search-logo:hover {
+  background-color: #e9e9e9;
+  border: 1px solid rgb(226, 227, 229);
+  border-radius: 2.5px;
+}
+
+#newtab-search-text {
+  height: 32px;
+  -moz-box-flex: 1;
+
+  padding: 0 8px;
+  background: hsla(0,0%,100%,.9) padding-box;
+  border: 1px solid;
+  border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2);
+  box-shadow: 0 1px 0 hsla(210,65%,9%,.02) inset,
+              0 0 2px hsla(210,65%,9%,.1) inset,
+              0 1px 0 hsla(0,0%,100%,.2);
+  border-radius: 2.5px 0 0 2.5px;
+}
+
+#newtab-search-text:-moz-dir(rtl) {
+  border-radius: 0 2.5px 2.5px 0;
+}
+
+#newtab-search-text:focus,
+#newtab-search-text[autofocus] {
+  border-color: hsla(206,100%,60%,.6) hsla(206,76%,52%,.6) hsla(204,100%,40%,.6);
+}
+
+#newtab-search-submit {
+  height: 32px;
+
+  -moz-margin-start: -1px;
+  background: linear-gradient(hsla(0,0%,100%,.8), hsla(0,0%,100%,.1)) padding-box;
+  padding: 0 9px;
+  border: 1px solid;
+  border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2);
+  -moz-border-start: 1px solid transparent;
+  border-radius: 0 2.5px 2.5px 0;
+  box-shadow: 0 0 2px hsla(0,0%,100%,.5) inset,
+              0 1px 0 hsla(0,0%,100%,.2);
+  cursor: pointer;
+  transition-property: background-color, border-color, box-shadow;
+  transition-duration: 150ms;
+}
+
+#newtab-search-submit:-moz-dir(rtl) {
+  border-radius: 2.5px 0 0 2.5px;
+}
+
+#newtab-search-text:focus + #newtab-search-submit,
+#newtab-search-text + #newtab-search-submit:hover,
+#newtab-search-text[autofocus] + #newtab-search-submit {
+  border-color: #59b5fc #45a3e7 #3294d5;
+  color: white;
+}
+
+#newtab-search-text:focus + #newtab-search-submit,
+#newtab-search-text[autofocus] + #newtab-search-submit {
+  background-image: linear-gradient(#4cb1ff, #1793e5);
+  box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset,
+              0 0 0 1px hsla(0,0%,100%,.1) inset,
+              0 1px 0 hsla(210,54%,20%,.03);
+}
+
+#newtab-search-text + #newtab-search-submit:hover {
+  background-image: linear-gradient(#66bdff, #0d9eff);
+  box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset,
+              0 0 0 1px hsla(0,0%,100%,.1) inset,
+              0 1px 0 hsla(210,54%,20%,.03),
+              0 0 4px hsla(206,100%,20%,.2);
+}
+
+#newtab-search-text + #newtab-search-submit:hover:active {
+  box-shadow: 0 1px 1px hsla(211,79%,6%,.1) inset,
+              0 0 1px hsla(211,79%,6%,.2) inset;
+  transition-duration: 0ms;
+}
+
+#newtab-search-panel .panel-arrowcontent {
+  -moz-padding-start: 0;
+  -moz-padding-end: 0;
+  padding-top: 0;
+  padding-bottom: 0;
+  background: rgb(248, 250, 251);
+}
+
+.newtab-search-panel-engine {
+  -moz-box-align: center;
+  padding-top: 4px;
+  padding-bottom: 4px;
+  -moz-padding-start: 24px;
+  -moz-padding-end: 24px;
+}
+
+.newtab-search-panel-engine:not(:last-child) {
+  border-bottom: 1px solid #ccc;
+}
+
+.newtab-search-panel-engine > image {
+  -moz-margin-end: 8px;
+  width: 16px;
+  height: 16px;
+  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
+}
+
+.newtab-search-panel-engine > label {
+  -moz-padding-start: 0;
+  -moz-margin-start: 0;
+  color: rgb(130, 132, 133);
+}
+
+.newtab-search-panel-engine[selected] {
+  background: url("chrome://global/skin/menu/shared-menu-check.png") center left 4px no-repeat transparent;
+}
--- a/browser/base/content/newtab/newTab.js
+++ b/browser/base/content/newtab/newTab.js
@@ -36,24 +36,26 @@ XPCOMUtils.defineLazyGetter(this, "gStri
 
 function newTabString(name) gStringBundle.GetStringFromName('newtab.' + name);
 
 function inPrivateBrowsingMode() {
   return PrivateBrowsingUtils.isWindowPrivate(window);
 }
 
 const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
+const XUL_NAMESPACE = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 #include transformations.js
 #include page.js
 #include grid.js
 #include cells.js
 #include sites.js
 #include drag.js
 #include dragDataHelper.js
 #include drop.js
 #include dropTargetShim.js
 #include dropPreview.js
 #include updater.js
 #include undo.js
+#include search.js
 
 // Everything is loaded. Initialize the New Tab Page.
 gPage.init();
--- a/browser/base/content/newtab/newTab.xul
+++ b/browser/base/content/newtab/newTab.xul
@@ -6,29 +6,38 @@
 
 <?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/content/newtab/newTab.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/newtab/newTab.css" type="text/css"?>
 
 <!DOCTYPE window [
   <!ENTITY % newTabDTD SYSTEM "chrome://browser/locale/newTab.dtd">
   %newTabDTD;
+  <!ENTITY % searchBarDTD SYSTEM "chrome://browser/locale/searchbar.dtd">
+  %searchBarDTD;
 ]>
 
 <xul:window id="newtab-window" xmlns="http://www.w3.org/1999/xhtml"
             xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
             title="&newtab.pageTitle;">
 
   <xul:panel id="sponsored-panel" orient="vertical" type="arrow">
     <xul:description>&newtab.panel.message;</xul:description>
     <xul:label class="text-link"
                href="https://support.mozilla.org/kb/how-do-sponsored-tiles-work"
                value="&newtab.panel.link.text;" />
   </xul:panel>
 
+  <xul:panel id="newtab-search-panel" orient="vertical" type="arrow"
+             noautohide="true">
+    <xul:hbox id="newtab-search-manage" class="newtab-search-panel-engine">
+      <xul:label>&cmd_engineManager.label;</xul:label>
+    </xul:hbox>
+  </xul:panel>
+
   <div id="newtab-scrollbox">
 
     <div id="newtab-vertical-margin">
 
       <div id="newtab-margin-top"/>
 
       <div id="newtab-margin-undo-container">
         <div id="newtab-undo-container" undo-disabled="true">
@@ -41,16 +50,26 @@
                       label="&newtab.undo.restoreButton;"
                       class="newtab-undo-button" />
           <xul:toolbarbutton id="newtab-undo-close-button" tabindex="-1"
                              class="close-icon tabbable"
                              tooltiptext="&newtab.undo.closeTooltip;" />
         </div>
       </div>
 
+      <div id="newtab-search-container">
+        <form id="newtab-search-form" name="searchForm">
+          <div id="newtab-search-logo"/>
+          <input type="text" name="q" value="" id="newtab-search-text"
+                 maxlength="256" dir="auto"/>
+          <input id="newtab-search-submit" type="submit"
+                 value="&searchEndCap.label;"/>
+        </form>
+      </div>
+
       <div id="newtab-horizontal-margin">
         <div class="newtab-side-margin"/>
 
         <div id="newtab-grid">
         </div>
 
         <div class="newtab-side-margin"/>
       </div>
--- a/browser/base/content/newtab/page.js
+++ b/browser/base/content/newtab/page.js
@@ -106,16 +106,18 @@ let gPage = {
    * is/gets enabled.
    */
   _init: function Page_init() {
     if (this._initialized)
       return;
 
     this._initialized = true;
 
+    gSearch.init();
+
     this._mutationObserver = new MutationObserver(() => {
       if (this.allowBackgroundCaptures) {
         Services.telemetry.getHistogramById("NEWTAB_PAGE_SHOWN").add(true);
 
         // Initialize type counting with the types we want to count
         let directoryCount = {};
         for (let type of DirectoryLinksProvider.linkTypes) {
           directoryCount[type] = 0;
@@ -133,16 +135,20 @@ let gPage = {
 
         // Record how many directory sites were shown, but place counts over the
         // default 9 in the same bucket
         for (let [type, count] of Iterator(directoryCount)) {
           let shownId = "NEWTAB_PAGE_DIRECTORY_" + type.toUpperCase() + "_SHOWN";
           let shownCount = Math.min(10, count);
           Services.telemetry.getHistogramById(shownId).add(shownCount);
         }
+
+        // content.js isn't loaded for the page while it's in the preloader,
+        // which is why this is necessary.
+        gSearch.setUpInitialState();
       }
     });
     this._mutationObserver.observe(document.documentElement, {
       attributes: true,
       attributeFilter: ["allow-background-captures"],
     });
 
     // Initialize and render the grid.
@@ -159,17 +165,17 @@ let gPage = {
   },
 
   /**
    * Updates the 'page-disabled' attributes of the respective DOM nodes.
    * @param aValue Whether the New Tab Page is enabled or not.
    */
   _updateAttributes: function Page_updateAttributes(aValue) {
     // Set the nodes' states.
-    let nodeSelector = "#newtab-scrollbox, #newtab-toggle, #newtab-grid";
+    let nodeSelector = "#newtab-scrollbox, #newtab-toggle, #newtab-grid, #newtab-search-container";
     for (let node of document.querySelectorAll(nodeSelector)) {
       if (aValue)
         node.removeAttribute("page-disabled");
       else
         node.setAttribute("page-disabled", "true");
     }
 
     // Enables/disables the control and link elements.
new file mode 100644
--- /dev/null
+++ b/browser/base/content/newtab/search.js
@@ -0,0 +1,170 @@
+#ifdef 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/. */
+#endif
+
+let gSearch = {
+
+  currentEngineName: null,
+
+  init: function () {
+    for (let idSuffix of this._nodeIDSuffixes) {
+      this._nodes[idSuffix] =
+        document.getElementById("newtab-search-" + idSuffix);
+    }
+
+    window.addEventListener("ContentSearchService", this);
+    this.setUpInitialState();
+  },
+
+  setUpInitialState: function () {
+    this._send("GetState");
+  },
+
+  showPanel: function () {
+    let panel = this._nodes.panel;
+    let logo = this._nodes.logo;
+    panel.openPopup(logo);
+    logo.setAttribute("active", "true");
+    panel.addEventListener("popuphidden", function onHidden() {
+      panel.removeEventListener("popuphidden", onHidden);
+      logo.removeAttribute("active");
+    });
+  },
+
+  search: function (event) {
+    event.preventDefault();
+    let searchStr = this._nodes.text.value;
+    if (this.currentEngineName && searchStr.length) {
+      this._send("Search", {
+        engineName: this.currentEngineName,
+        searchString: searchStr,
+        whence: "newtab",
+      });
+    }
+  },
+
+  manageEngines: function () {
+    this._nodes.panel.hidePopup();
+    this._send("ManageEngines");
+  },
+
+  setWidth: function (width) {
+    this._nodes.form.style.width = width + "px";
+    this._nodes.form.style.maxWidth = width + "px";
+  },
+
+  handleEvent: function (event) {
+    this["on" + event.detail.type](event.detail.data);
+  },
+
+  onState: function (data) {
+    this._makePanel(data.engines);
+    this._setCurrentEngine(data.currentEngine);
+    this._initWhenInitalStateReceived();
+  },
+
+  onCurrentEngine: function (engineName) {
+    this._setCurrentEngine(engineName);
+  },
+
+  _nodeIDSuffixes: [
+    "form",
+    "logo",
+    "manage",
+    "panel",
+    "text",
+  ],
+
+  _nodes: {},
+
+  _initWhenInitalStateReceived: function () {
+    this._nodes.form.addEventListener("submit", e => this.search(e));
+    this._nodes.logo.addEventListener("click", e => this.showPanel());
+    this._nodes.manage.addEventListener("click", e => this.manageEngines());
+    this._initWhenInitalStateReceived = function () {};
+  },
+
+  _send: function (type, data=null) {
+    window.dispatchEvent(new CustomEvent("ContentSearchClient", {
+      detail: {
+        type: type,
+        data: data,
+      },
+    }));
+  },
+
+  _makePanel: function (engines) {
+    let panel = this._nodes.panel;
+
+    // Empty the panel except for the Manage Engines row.
+    let i = 0;
+    while (i < panel.childNodes.length) {
+      let node = panel.childNodes[i];
+      if (node != this._nodes.manage) {
+        panel.removeChild(node);
+      }
+      else {
+        i++;
+      }
+    }
+
+    // Add all the engines.
+    for (let engine of engines) {
+      panel.insertBefore(this._makePanelEngine(panel, engine),
+                         this._nodes.manage);
+    }
+  },
+
+  _makePanelEngine: function (panel, engine) {
+    let box = document.createElementNS(XUL_NAMESPACE, "hbox");
+    box.className = "newtab-search-panel-engine";
+    box.setAttribute("engine", engine.name);
+
+    box.addEventListener("click", () => {
+      this._send("SetCurrentEngine", engine.name);
+      panel.hidePopup();
+      this._nodes.text.focus();
+    });
+
+    let image = document.createElementNS(XUL_NAMESPACE, "image");
+    if (engine.iconURI) {
+      image.setAttribute("src", engine.iconURI);
+    }
+    box.appendChild(image);
+
+    let label = document.createElementNS(XUL_NAMESPACE, "label");
+    label.setAttribute("value", engine.name);
+    box.appendChild(label);
+
+    return box;
+  },
+
+  _setCurrentEngine: function (engine) {
+    this.currentEngineName = engine.name;
+
+    // Set the logo.
+    let logoURI = window.devicePixelRatio == 2 ? engine.logo2xURI :
+                  engine.logoURI;
+    if (logoURI) {
+      this._nodes.logo.hidden = false;
+      this._nodes.logo.style.backgroundImage = "url(" + logoURI + ")";
+      this._nodes.text.placeholder = "";
+    }
+    else {
+      this._nodes.logo.hidden = true;
+      this._nodes.text.placeholder = engine.name;
+    }
+
+    // Set the selected state of all the engines in the panel.
+    for (let box of this._nodes.panel.childNodes) {
+      if (box.getAttribute("engine") == engine.name) {
+        box.setAttribute("selected", "true");
+      }
+      else {
+        box.removeAttribute("selected");
+      }
+    }
+  },
+};
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -394,17 +394,16 @@ skip-if = e10s # Bug ?????? - obscure no
 [browser_urlbarRevert.js]
 skip-if = e10s # Bug ?????? - ESC reverted the location bar value - Got foobar, expected example.com
 [browser_urlbarStop.js]
 skip-if = e10s # Bug ????? - test calls gBrowser.contentWindow.stop
 [browser_urlbarTrimURLs.js]
 [browser_urlbar_search_healthreport.js]
 skip-if = e10s # Bug ?????? - FHR tests failing (either with "no data for today" or "2 records for today")
 [browser_utilityOverlay.js]
-skip-if = e10s # Bug 921947 - openNewTabWith failed with window.content.document being null
 [browser_visibleFindSelection.js]
 skip-if = e10s # Bug ?????? - test directly manipulates content
 [browser_visibleLabel.js]
 [browser_visibleTabs.js]
 skip-if = e10s # Bug 921905 - pinTab/unpinTab fail in e10s
 [browser_visibleTabs_bookmarkAllPages.js]
 skip-if = e10s # Bug ?????? - bizarre problem with hidden tab having _mouseenter called, via _setPositionalAttributes, and tab not being found resulting in 'candidate is undefined'
 [browser_visibleTabs_bookmarkAllTabs.js]
--- a/browser/base/content/test/newtab/browser.ini
+++ b/browser/base/content/test/newtab/browser.ini
@@ -1,11 +1,14 @@
 [DEFAULT]
-support-files = head.js
 skip-if = e10s # Bug ?????? - about:newtab tests don't work in e10s
+support-files =
+  head.js
+  searchEngineLogo.xml
+  searchEngineNoLogo.xml
 
 [browser_newtab_background_captures.js]
 [browser_newtab_block.js]
 [browser_newtab_bug721442.js]
 [browser_newtab_bug722273.js]
 [browser_newtab_bug723102.js]
 [browser_newtab_bug723121.js]
 [browser_newtab_bug725996.js]
@@ -20,13 +23,14 @@ skip-if = os == "mac" # Intermittent fai
 [browser_newtab_bug998387.js]
 [browser_newtab_disable.js]
 [browser_newtab_drag_drop.js]
 [browser_newtab_drag_drop_ext.js]
 [browser_newtab_drop_preview.js]
 [browser_newtab_focus.js]
 [browser_newtab_perwindow_private_browsing.js]
 [browser_newtab_reset.js]
+[browser_newtab_search.js]
 [browser_newtab_sponsored_icon_click.js]
 [browser_newtab_tabsync.js]
 [browser_newtab_undo.js]
 [browser_newtab_unpin.js]
 [browser_newtab_update.js]
--- a/browser/base/content/test/newtab/browser_newtab_focus.js
+++ b/browser/base/content/test/newtab/browser_newtab_focus.js
@@ -4,19 +4,19 @@
 /*
  * These tests make sure that focusing the 'New Tage Page' works as expected.
  */
 function runTests() {
   // Handle the OSX full keyboard access setting
   Services.prefs.setIntPref("accessibility.tabfocus", 7);
 
   // Focus count in new tab page.
-  // 28 = 9 * 3 + 1 = 9 sites and 1 toggle button, each site has a link, a pin
-  // and a remove button.
-  let FOCUS_COUNT = 28;
+  // 30 = 9 * 3 + 3 = 9 sites, each with link, pin and remove buttons; search
+  // bar; search button; and toggle button.
+  let FOCUS_COUNT = 30;
 
   // Create a new tab page.
   yield setLinks("0,1,2,3,4,5,6,7,8");
   setPinnedLinks("");
 
   yield addNewTabPageTab();
   gURLBar.focus();
 
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/newtab/browser_newtab_search.js
@@ -0,0 +1,295 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// See browser/components/search/test/browser_*_behavior.js for tests of actual
+// searches.
+
+const ENGINE_LOGO = "searchEngineLogo.xml";
+const ENGINE_NO_LOGO = "searchEngineNoLogo.xml";
+
+const SERVICE_EVENT_NAME = "ContentSearchService";
+
+const LOGO_LOW_DPI_SIZE = [65, 26];
+const LOGO_HIGH_DPI_SIZE = [130, 52];
+
+// The test has an expected search event queue and a search event listener.
+// Search events that are expected to happen are added to the queue, and the
+// listener consumes the queue and ensures that each event it receives is at
+// the head of the queue.
+//
+// Each item in the queue is an object { type, deferred }.  type is the
+// expected search event type.  deferred is a Promise.defer() value that is
+// resolved when the event is consumed.
+var gExpectedSearchEventQueue = [];
+
+var gNewEngines = [];
+
+function runTests() {
+  let oldCurrentEngine = Services.search.currentEngine;
+
+  yield addNewTabPageTab();
+  yield whenSearchInitDone();
+
+  // The tab is removed at the end of the test, so there's no need to remove
+  // this listener at the end of the test.
+  info("Adding search event listener");
+  getContentWindow().addEventListener(SERVICE_EVENT_NAME, searchEventListener);
+
+  let panel = searchPanel();
+  is(panel.state, "closed", "Search panel should be closed initially");
+
+  // The panel's animation often is not finished when the test clicks on panel
+  // children, which makes the test click the wrong children, so disable it.
+  panel.setAttribute("animate", "false");
+
+  // Add the two test engines.
+  let logoEngine = null;
+  yield promiseNewSearchEngine(true).then(engine => {
+    logoEngine = engine;
+    TestRunner.next();
+  });
+  ok(!!logoEngine.getIconURLBySize(...LOGO_LOW_DPI_SIZE),
+     "Sanity check: engine should have 1x logo");
+  ok(!!logoEngine.getIconURLBySize(...LOGO_HIGH_DPI_SIZE),
+     "Sanity check: engine should have 2x logo");
+
+  let noLogoEngine = null;
+  yield promiseNewSearchEngine(false).then(engine => {
+    noLogoEngine = engine;
+    TestRunner.next();
+  });
+  ok(!noLogoEngine.getIconURLBySize(...LOGO_LOW_DPI_SIZE),
+     "Sanity check: engine should not have 1x logo");
+  ok(!noLogoEngine.getIconURLBySize(...LOGO_HIGH_DPI_SIZE),
+     "Sanity check: engine should not have 2x logo");
+
+  // Use the search service to change the current engine to the logo engine.
+  Services.search.currentEngine = logoEngine;
+  yield promiseSearchEvents(["CurrentEngine"]).then(TestRunner.next);
+  checkCurrentEngine(ENGINE_LOGO);
+
+  // Click the logo to open the search panel.
+  yield Promise.all([
+    promisePanelShown(panel),
+    promiseClick(logoImg()),
+  ]).then(TestRunner.next);
+
+  // In the search panel, click the no-logo engine.  It should become the
+  // current engine.
+  let noLogoBox = null;
+  for (let box of panel.childNodes) {
+    if (box.getAttribute("engine") == noLogoEngine.name) {
+      noLogoBox = box;
+      break;
+    }
+  }
+  ok(noLogoBox, "Search panel should contain the no-logo engine");
+  yield Promise.all([
+    promiseSearchEvents(["CurrentEngine"]),
+    promiseClick(noLogoBox),
+  ]).then(TestRunner.next);
+
+  checkCurrentEngine(ENGINE_NO_LOGO);
+
+  // Switch back to the logo engine.
+  Services.search.currentEngine = logoEngine;
+  yield promiseSearchEvents(["CurrentEngine"]).then(TestRunner.next);
+  checkCurrentEngine(ENGINE_LOGO);
+
+  // Open the panel again.
+  yield Promise.all([
+    promisePanelShown(panel),
+    promiseClick(logoImg()),
+  ]).then(TestRunner.next);
+
+  // In the search panel, click the Manage Engines box.
+  let manageBox = $("manage");
+  ok(!!manageBox, "The Manage Engines box should be present in the document");
+  yield Promise.all([
+    promiseManagerOpen(),
+    promiseClick(manageBox),
+  ]).then(TestRunner.next);
+
+  // Done.  Revert the current engine and remove the new engines.
+  Services.search.currentEngine = oldCurrentEngine;
+  yield promiseSearchEvents(["CurrentEngine"]).then(TestRunner.next);
+
+  let events = [];
+  for (let engine of gNewEngines) {
+    Services.search.removeEngine(engine);
+    events.push("State");
+  }
+  yield promiseSearchEvents(events).then(TestRunner.next);
+}
+
+function searchEventListener(event) {
+  info("Got search event " + event.detail.type);
+  let passed = false;
+  let nonempty = gExpectedSearchEventQueue.length > 0;
+  ok(nonempty, "Expected search event queue should be nonempty");
+  if (nonempty) {
+    let { type, deferred } = gExpectedSearchEventQueue.shift();
+    is(event.detail.type, type, "Got expected search event " + type);
+    if (event.detail.type == type) {
+      passed = true;
+      // Let gSearch respond to the event before continuing.
+      executeSoon(() => deferred.resolve());
+    }
+  }
+  if (!passed) {
+    info("Didn't get expected event, stopping the test");
+    getContentWindow().removeEventListener(SERVICE_EVENT_NAME,
+                                           searchEventListener);
+    // Set next() to a no-op so the test really does stop.
+    TestRunner.next = function () {};
+    TestRunner.finish();
+  }
+}
+
+function $(idSuffix) {
+  return getContentDocument().getElementById("newtab-search-" + idSuffix);
+}
+
+function promiseSearchEvents(events) {
+  info("Expecting search events: " + events);
+  events = events.map(e => ({ type: e, deferred: Promise.defer() }));
+  gExpectedSearchEventQueue.push(...events);
+  return Promise.all(events.map(e => e.deferred.promise));
+}
+
+function promiseNewSearchEngine(withLogo) {
+  let basename = withLogo ? ENGINE_LOGO : ENGINE_NO_LOGO;
+  info("Waiting for engine to be added: " + basename);
+
+  // Wait for the search events triggered by adding the new engine.
+  // engine-added engine-loaded
+  let expectedSearchEvents = ["State", "State"];
+  if (withLogo) {
+    // an engine-changed for each of the two logos
+    expectedSearchEvents.push("State", "State");
+  }
+  let eventPromise = promiseSearchEvents(expectedSearchEvents);
+
+  // Wait for addEngine().
+  let addDeferred = Promise.defer();
+  let url = getRootDirectory(gTestPath) + basename;
+  Services.search.addEngine(url, Ci.nsISearchEngine.TYPE_MOZSEARCH, "", false, {
+    onSuccess: function (engine) {
+      info("Search engine added: " + basename);
+      gNewEngines.push(engine);
+      addDeferred.resolve(engine);
+    },
+    onError: function (errCode) {
+      ok(false, "addEngine failed with error code " + errCode);
+      addDeferred.reject();
+    },
+  });
+
+  // Make a new promise that wraps the previous promises.  The only point of
+  // this is to pass the new engine to the yielder via deferred.resolve(),
+  // which is a little nicer than passing an array whose first element is the
+  // new engine.
+  let deferred = Promise.defer();
+  Promise.all([addDeferred.promise, eventPromise]).then(values => {
+    let newEngine = values[0];
+    deferred.resolve(newEngine);
+  }, () => deferred.reject());
+  return deferred.promise;
+}
+
+function checkCurrentEngine(basename) {
+  let engine = Services.search.currentEngine;
+  ok(engine.name.contains(basename),
+     "Sanity check: current engine: engine.name=" + engine.name +
+     " basename=" + basename);
+
+  // gSearch.currentEngineName
+  is(gSearch().currentEngineName, engine.name,
+     "currentEngineName: " + engine.name);
+
+  // search bar logo
+  let logoSize = [px * window.devicePixelRatio for (px of LOGO_LOW_DPI_SIZE)];
+  let logoURI = engine.getIconURLBySize(...logoSize);
+  let logo = logoImg();
+  is(logo.hidden, !logoURI,
+     "Logo should be visible iff engine has a logo: " + engine.name);
+  if (logoURI) {
+    is(logo.style.backgroundImage, 'url("' + logoURI + '")', "Logo URI");
+  }
+
+  // "selected" attributes of engines in the panel
+  let panel = searchPanel();
+  for (let engineBox of panel.childNodes) {
+    let engineName = engineBox.getAttribute("engine");
+    if (engineName == engine.name) {
+      is(engineBox.getAttribute("selected"), "true",
+         "Engine box's selected attribute should be true for " +
+         "selected engine: " + engineName);
+    }
+    else {
+      ok(!engineBox.hasAttribute("selected"),
+         "Engine box's selected attribute should be absent for " +
+         "non-selected engine: " + engineName);
+    }
+  }
+}
+
+function promisePanelShown(panel) {
+  let deferred = Promise.defer();
+  info("Waiting for popupshown");
+  panel.addEventListener("popupshown", function onEvent() {
+    panel.removeEventListener("popupshown", onEvent);
+    is(panel.state, "open", "Panel state");
+    executeSoon(() => deferred.resolve());
+  });
+  return deferred.promise;
+}
+
+function promiseClick(node) {
+  let deferred = Promise.defer();
+  let win = getContentWindow();
+  SimpleTest.waitForFocus(() => {
+    EventUtils.synthesizeMouseAtCenter(node, {}, win);
+    deferred.resolve();
+  }, win);
+  return deferred.promise;
+}
+
+function promiseManagerOpen() {
+  info("Waiting for the search manager window to open...");
+  let deferred = Promise.defer();
+  let winWatcher = Cc["@mozilla.org/embedcomp/window-watcher;1"].
+                   getService(Ci.nsIWindowWatcher);
+  winWatcher.registerNotification(function onWin(subj, topic, data) {
+    if (topic == "domwindowopened" && subj instanceof Ci.nsIDOMWindow) {
+      subj.addEventListener("load", function onLoad() {
+        subj.removeEventListener("load", onLoad);
+        if (subj.document.documentURI ==
+            "chrome://browser/content/search/engineManager.xul") {
+          winWatcher.unregisterNotification(onWin);
+          ok(true, "Observed search manager window opened");
+          is(subj.opener, gWindow,
+             "Search engine manager opener should be the chrome browser " +
+             "window containing the newtab page");
+          executeSoon(() => {
+            subj.close();
+            deferred.resolve();
+          });
+        }
+      });
+    }
+  });
+  return deferred.promise;
+}
+
+function searchPanel() {
+  return $("panel");
+}
+
+function logoImg() {
+  return $("logo");
+}
+
+function gSearch() {
+  return getContentWindow().gSearch;
+}
--- a/browser/base/content/test/newtab/browser_newtab_sponsored_icon_click.js
+++ b/browser/base/content/test/newtab/browser_newtab_sponsored_icon_click.js
@@ -1,15 +1,19 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function runTests() {
   yield setLinks("0");
   yield addNewTabPageTab();
 
+  // When gSearch modifies the DOM as it sets itself up, it can prevent the
+  // popup from opening, depending on the timing.  Wait until that's done.
+  yield whenSearchInitDone();
+
   let site = getCell(0).node.querySelector(".newtab-site");
   site.setAttribute("type", "sponsored");
 
   let sponsoredPanel = getContentDocument().getElementById("sponsored-panel");
   is(sponsoredPanel.state, "closed", "Sponsored panel must be closed");
 
   function continueOnceOn(event) {
     sponsoredPanel.addEventListener(event, function listener() {
--- a/browser/base/content/test/newtab/head.js
+++ b/browser/base/content/test/newtab/head.js
@@ -20,20 +20,51 @@ let {Promise, NewTabUtils, Sanitizer, cl
 let uri = Services.io.newURI("about:newtab", null, null);
 let principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
 
 let isMac = ("nsILocalFileMac" in Ci);
 let isLinux = ("@mozilla.org/gnome-gconf-service;1" in Cc);
 let isWindows = ("@mozilla.org/windows-registry-key;1" in Cc);
 let gWindow = window;
 
+// The tests assume all three rows of sites are shown, but the window may be too
+// short to actually show three rows.  Resize it if necessary.
+let requiredInnerHeight =
+  40 + 32 + // undo container + bottom margin
+  44 + 32 + // search bar + bottom margin
+  (3 * (150 + 32)) + // 3 rows * (tile height + title and bottom margin)
+  100; // breathing room
+
+let oldInnerHeight = null;
+if (gBrowser.contentWindow.innerHeight < requiredInnerHeight) {
+  oldInnerHeight = gBrowser.contentWindow.innerHeight;
+  info("Changing browser inner height from " + oldInnerHeight + " to " +
+       requiredInnerHeight);
+  gBrowser.contentWindow.innerHeight = requiredInnerHeight;
+  let screenHeight = {};
+  Cc["@mozilla.org/gfx/screenmanager;1"].
+    getService(Ci.nsIScreenManager).
+    primaryScreen.
+    GetAvailRectDisplayPix({}, {}, {}, screenHeight);
+  screenHeight = screenHeight.value;
+  if (screenHeight < gBrowser.contentWindow.outerHeight) {
+    info("Warning: Browser outer height is now " +
+         gBrowser.contentWindow.outerHeight + ", which is larger than the " +
+         "available screen height, " + screenHeight +
+         ". That may cause problems.");
+  }
+}
+
 registerCleanupFunction(function () {
   while (gWindow.gBrowser.tabs.length > 1)
     gWindow.gBrowser.removeTab(gWindow.gBrowser.tabs[1]);
 
+  if (oldInnerHeight)
+    gBrowser.contentWindow.innerHeight = oldInnerHeight;
+
   Services.prefs.clearUserPref(PREF_NEWTAB_ENABLED);
   Services.prefs.clearUserPref(PREF_NEWTAB_DIRECTORYSOURCE);
 
   // Stop any update timers to prevent unexpected updates in later tests
   let timer = NewTabUtils.allPages._scheduleUpdateTimeout;
   if (timer) {
     clearTimeout(timer);
     delete NewTabUtils.allPages._scheduleUpdateTimeout;
@@ -544,8 +575,40 @@ function whenPagesUpdated(aCallback, aOn
     }
   };
 
   NewTabUtils.allPages.register(page);
   registerCleanupFunction(function () {
     NewTabUtils.allPages.unregister(page);
   });
 }
+
+/**
+ * Waits a small amount of time for search events to stop occurring in the
+ * newtab page.
+ *
+ * newtab pages receive some search events around load time that are difficult
+ * to predict.  There are two categories of such events: (1) "State" events
+ * triggered by engine notifications like engine-changed, due to the search
+ * service initializing itself on app startup.  This can happen when a test is
+ * the first test to run.  (2) "State" events triggered by the newtab page
+ * itself when gSearch first sets itself up.  newtab preloading makes these a
+ * pain to predict.
+ */
+function whenSearchInitDone() {
+  info("Waiting for initial search events...");
+  let numTicks = 0;
+  function reset(event) {
+    info("Got initial search event " + event.detail.type +
+         ", waiting for more...");
+    numTicks = 0;
+  }
+  let eventName = "ContentSearchService";
+  getContentWindow().addEventListener(eventName, reset);
+  let interval = window.setInterval(() => {
+    if (++numTicks >= 100) {
+      info("Done waiting for initial search events");
+      window.clearInterval(interval);
+      getContentWindow().removeEventListener(eventName, reset);
+      TestRunner.next();
+    }
+  }, 0);
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/newtab/searchEngineLogo.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>browser_newtab_search searchEngineLogo.xml</ShortName>
+<Url type="text/html" method="GET" template="http://browser-newtab-search.com/logo" rel="searchform"/>
+<Image width="65" height="26">data:image/png;base64,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</Image>
+<Image width="130" height="52">data:image/png;base64,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</Image>
+</SearchPlugin>
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/newtab/searchEngineNoLogo.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>browser_newtab_search searchEngineNoLogo.xml</ShortName>
+<Url type="text/html" method="GET" template="http://browser-newtab-search.com/nologo" rel="searchform"/>
+</SearchPlugin>
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -594,77 +594,68 @@ function isElementVisible(aElement)
 }
 
 function makeURLAbsolute(aBase, aUrl)
 {
   // Note:  makeURI() will throw if aUri is not a valid URI
   return makeURI(aUrl, null, makeURI(aBase)).spec;
 }
 
-
 /**
  * openNewTabWith: opens a new tab with the given URL.
  *
  * @param aURL
  *        The URL to open (as a string).
  * @param aDocument
- *        The document from which the URL came, or null. This is used to set the
- *        referrer header and to do a security check of whether the document is
- *        allowed to reference the URL. If null, there will be no referrer
- *        header and no security check.
+ *        Note this parameter is now ignored. There is no security check & no
+ *        referrer header derived from aDocument (null case).
  * @param aPostData
  *        Form POST data, or null.
  * @param aEvent
  *        The triggering event (for the purpose of determining whether to open
  *        in the background), or null.
  * @param aAllowThirdPartyFixup
  *        If true, then we allow the URL text to be sent to third party services
  *        (e.g., Google's I Feel Lucky) for interpretation. This parameter may
  *        be undefined in which case it is treated as false.
  * @param [optional] aReferrer
- *        If aDocument is null, then this will be used as the referrer.
- *        There will be no security check.
+ *        This will be used as the referrer. There will be no security check.
  */ 
 function openNewTabWith(aURL, aDocument, aPostData, aEvent,
                         aAllowThirdPartyFixup, aReferrer) {
-  if (aDocument)
-    urlSecurityCheck(aURL, aDocument.nodePrincipal);
 
   // As in openNewWindowWith(), we want to pass the charset of the
   // current document over to a new tab.
-  var originCharset = aDocument && aDocument.characterSet;
-  if (!originCharset &&
-      document.documentElement.getAttribute("windowtype") == "navigator:browser")
-    originCharset = window.content.document.characterSet;
+  let originCharset = null;
+  if (document.documentElement.getAttribute("windowtype") == "navigator:browser")
+    originCharset = gBrowser.selectedBrowser.characterSet;
 
   openLinkIn(aURL, aEvent && aEvent.shiftKey ? "tabshifted" : "tab",
              { charset: originCharset,
                postData: aPostData,
                allowThirdPartyFixup: aAllowThirdPartyFixup,
-               referrerURI: aDocument ? aDocument.documentURIObject : aReferrer });
+               referrerURI: aReferrer });
 }
 
+/**
+ * @param aDocument
+ *        Note this parameter is ignored. See openNewTabWith()
+ */
 function openNewWindowWith(aURL, aDocument, aPostData, aAllowThirdPartyFixup, aReferrer) {
-  if (aDocument)
-    urlSecurityCheck(aURL, aDocument.nodePrincipal);
-
-  // if and only if the current window is a browser window and it has a
-  // document with a character set, then extract the current charset menu
-  // setting from the current document and use it to initialize the new browser
-  // window...
-  var originCharset = aDocument && aDocument.characterSet;
-  if (!originCharset &&
-      document.documentElement.getAttribute("windowtype") == "navigator:browser")
-    originCharset = window.content.document.characterSet;
+  // Extract the current charset menu setting from the current document and
+  // use it to initialize the new browser window...
+  let originCharset = null;
+  if (document.documentElement.getAttribute("windowtype") == "navigator:browser")
+    originCharset = gBrowser.selectedBrowser.characterSet;
 
   openLinkIn(aURL, "window",
              { charset: originCharset,
                postData: aPostData,
                allowThirdPartyFixup: aAllowThirdPartyFixup,
-               referrerURI: aDocument ? aDocument.documentURIObject : aReferrer });
+               referrerURI: aReferrer });
 }
 
 // aCalledFromModal is optional
 function openHelpLink(aHelpTopic, aCalledFromModal, aWhere) {
   var url = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
                       .getService(Components.interfaces.nsIURLFormatter)
                       .formatURLPref("app.support.baseURL");
   url += aHelpTopic;
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -93,16 +93,19 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "AsyncShutdown",
                                   "resource://gre/modules/AsyncShutdown.jsm");
 
 #ifdef NIGHTLY_BUILD
 XPCOMUtils.defineLazyModuleGetter(this, "SignInToWebsiteUX",
                                   "resource:///modules/SignInToWebsite.jsm");
 #endif
 
+XPCOMUtils.defineLazyModuleGetter(this, "ContentSearch",
+                                  "resource:///modules/ContentSearch.jsm");
+
 const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser";
 const PREF_PLUGINS_UPDATEURL  = "plugins.update.url";
 
 // Seconds of idle before trying to create a bookmarks backup.
 const BOOKMARKS_BACKUP_IDLE_TIME_SEC = 10 * 60;
 // Minimum interval between backups.  We try to not create more than one backup
 // per interval.
 const BOOKMARKS_BACKUP_MIN_INTERVAL_DAYS = 1;
@@ -492,16 +495,17 @@ BrowserGlue.prototype = {
     PdfJs.init();
 #ifdef NIGHTLY_BUILD
     ShumwayUtils.init();
 #endif
     webrtcUI.init();
     AboutHome.init();
     SessionStore.init();
     BrowserUITelemetry.init();
+    ContentSearch.init();
 
     if (Services.appinfo.browserTabsRemote) {
       ContentClick.init();
       RemotePrompt.init();
     }
 
     Services.obs.notifyObservers(null, "browser-ui-startup-complete", "");
   },
--- a/browser/components/search/test/browser_bing_behavior.js
+++ b/browser/components/search/test/browser_bing_behavior.js
@@ -61,16 +61,60 @@ function test() {
         sb.value = "foo";
         registerCleanupFunction(function () {
           sb.value = "";
         });
         EventUtils.synthesizeKey("VK_RETURN", {});
       }
     },
     {
+      name: "new tab search",
+      searchURL: base,
+      run: function () {
+        function doSearch(doc) {
+          // Re-add the listener, and perform a search
+          gBrowser.addProgressListener(listener);
+          doc.getElementById("newtab-search-text").value = "foo";
+          doc.getElementById("newtab-search-submit").click();
+        }
+
+        // load about:newtab, but remove the listener first so it doesn't
+        // get in the way
+        gBrowser.removeProgressListener(listener);
+        gBrowser.loadURI("about:newtab");
+        info("Waiting for about:newtab load");
+        tab.linkedBrowser.addEventListener("load", function load(event) {
+          if (event.originalTarget != tab.linkedBrowser.contentDocument ||
+              event.target.location.href == "about:blank") {
+            info("skipping spurious load event");
+            return;
+          }
+          tab.linkedBrowser.removeEventListener("load", load, true);
+
+          // Observe page setup
+          let win = gBrowser.contentWindow;
+          if (win.gSearch.currentEngineName ==
+              Services.search.currentEngine.name) {
+            doSearch(win.document);
+          }
+          else {
+            info("Waiting for newtab search init");
+            win.addEventListener("ContentSearchService", function done(event) {
+              info("Got newtab search event " + event.detail.type);
+              if (event.detail.type == "State") {
+                win.removeEventListener("ContentSearchService", done);
+                // Let gSearch respond to the event before continuing.
+                executeSoon(() => doSearch(win.document));
+              }
+            });
+          }
+        }, true);
+      }
+    },
+    {
       name: "home page search",
       searchURL: base + "&form=MOZSPG",
       run: function () {
         // Bug 992270: Ignore uncaught about:home exceptions (related to snippets from IndexedDB)
         ignoreAllUncaughtExceptions(true);
 
         // load about:home, but remove the listener first so it doesn't
         // get in the way
--- a/browser/components/search/test/browser_google_behavior.js
+++ b/browser/components/search/test/browser_google_behavior.js
@@ -91,16 +91,60 @@ function test() {
         sb.value = "foo";
         registerCleanupFunction(function () {
           sb.value = "";
         });
         EventUtils.synthesizeKey("VK_RETURN", {});
       }
     },
     {
+      name: "new tab search",
+      searchURL: base,
+      run: function () {
+        function doSearch(doc) {
+          // Re-add the listener, and perform a search
+          gBrowser.addProgressListener(listener);
+          doc.getElementById("newtab-search-text").value = "foo";
+          doc.getElementById("newtab-search-submit").click();
+        }
+
+        // load about:newtab, but remove the listener first so it doesn't
+        // get in the way
+        gBrowser.removeProgressListener(listener);
+        gBrowser.loadURI("about:newtab");
+        info("Waiting for about:newtab load");
+        tab.linkedBrowser.addEventListener("load", function load(event) {
+          if (event.originalTarget != tab.linkedBrowser.contentDocument ||
+              event.target.location.href == "about:blank") {
+            info("skipping spurious load event");
+            return;
+          }
+          tab.linkedBrowser.removeEventListener("load", load, true);
+
+          // Observe page setup
+          let win = gBrowser.contentWindow;
+          if (win.gSearch.currentEngineName ==
+              Services.search.currentEngine.name) {
+            doSearch(win.document);
+          }
+          else {
+            info("Waiting for newtab search init");
+            win.addEventListener("ContentSearchService", function done(event) {
+              info("Got newtab search event " + event.detail.type);
+              if (event.detail.type == "State") {
+                win.removeEventListener("ContentSearchService", done);
+                // Let gSearch respond to the event before continuing.
+                executeSoon(() => doSearch(win.document));
+              }
+            });
+          }
+        }, true);
+      }
+    },
+    {
       name: "home page search",
       searchURL: base + "&channel=np&source=hp",
       run: function () {
         // Bug 992270: Ignore uncaught about:home exceptions (related to snippets from IndexedDB)
         ignoreAllUncaughtExceptions(true);
 
         // load about:home, but remove the listener first so it doesn't
         // get in the way
--- a/browser/devtools/debugger/test/browser_dbg_breakpoints-break-on-last-line-of-script-on-reload.js
+++ b/browser/devtools/debugger/test/browser_dbg_breakpoints-break-on-last-line-of-script-on-reload.js
@@ -4,18 +4,16 @@
 /**
  * Bug 978019: Setting a breakpoint on the last line of a Debugger.Script and
  * reloading should still hit the breakpoint.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_breakpoints-break-on-last-line-of-script-on-reload.html";
 const CODE_URL = EXAMPLE_URL + "code_breakpoints-break-on-last-line-of-script-on-reload.js";
 
-const { promiseInvoke } = require("devtools/async-utils");
-
 function test() {
   let gPanel, gDebugger, gThreadClient, gEvents;
 
   initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gThreadClient = gDebugger.gThreadClient;
     gEvents = gDebugger.EVENTS;
@@ -54,25 +52,25 @@ function test() {
         // Refresh and hit the debugger statement again.
         yield promise.all([
           reloadActiveTab(gPanel, gEvents.SOURCE_SHOWN),
           waitForCaretAndScopes(gPanel, 1)
         ]);
 
         // And we should hit the breakpoints as we resume.
         yield promise.all([
-          doResume(),
+          doResume(gPanel),
           waitForCaretAndScopes(gPanel, 3)
         ]);
         yield promise.all([
-          doResume(),
+          doResume(gPanel),
           waitForCaretAndScopes(gPanel, 4)
         ]);
         yield promise.all([
-          doResume(),
+          doResume(gPanel),
           waitForCaretAndScopes(gPanel, 5)
         ]);
 
         // Clean up the breakpoints.
         yield promise.all([
           rdpInvoke(bp1, bp1.remove),
           rdpInvoke(bp2, bp1.remove),
           rdpInvoke(bp3, bp1.remove),
@@ -85,33 +83,16 @@ function test() {
           "browser_dbg_breakpoints-break-on-last-line-of-script-on-reload.js",
           e
         );
         ok(false);
       }
     });
   });
 
-  function rdpInvoke(obj, method) {
-    return promiseInvoke(obj, method)
-      .then(({error, message }) => {
-        if (error) {
-          throw new Error(error + ": " + message);
-        }
-      });
-  }
-
-  function doResume() {
-    return rdpInvoke(gThreadClient, gThreadClient.resume);
-  }
-
-  function doInterrupt() {
-    return rdpInvoke(gThreadClient, gThreadClient.interrupt);
-  }
-
   function setBreakpoint(location) {
     let deferred = promise.defer();
     gThreadClient.setBreakpoint(location, ({ error, message }, bpClient) => {
       if (error) {
         deferred.reject(error + ": " + message);
       }
       deferred.resolve(bpClient);
     });
--- a/browser/devtools/debugger/test/browser_dbg_pretty-print-on-paused.js
+++ b/browser/devtools/debugger/test/browser_dbg_pretty-print-on-paused.js
@@ -1,81 +1,67 @@
 /* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
- * Test that pretty printing when the debugger is paused
- * does not switch away from the selected source.
+ * Test that pretty printing when the debugger is paused does not switch away
+ * from the selected source.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_pretty-print-on-paused.html";
 
-let gTab, gDebuggee, gPanel, gDebugger;
-let gSources;
+let gTab, gDebuggee, gPanel, gDebugger, gThreadClient, gSources;
 
-let gSecondSourceLabel = "code_ugly-2.js";
+const SECOND_SOURCE_VALUE = EXAMPLE_URL + "code_ugly-2.js";
 
 function test(){
   initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
+    gThreadClient = gDebugger.gThreadClient;
     gSources = gDebugger.DebuggerView.Sources;
 
-    gPanel.addBreakpoint({ url: gSources.values[0], line: 6 });
+    Task.spawn(function* () {
+      try {
+        yield ensureSourceIs(gPanel, "code_script-switching-02.js", true);
+
+        yield doInterrupt(gPanel);
+        yield rdpInvoke(gThreadClient, gThreadClient.setBreakpoint, {
+          url: gSources.selectedValue,
+          line: 6
+        });
+        yield doResume(gPanel);
+
+        const bpHit = waitForCaretAndScopes(gPanel, 6);
+        // Get the debuggee call off this tick so that we aren't accidentally
+        // blocking the yielding of bpHit which causes a deadlock.
+        executeSoon(() => gDebuggee.secondCall());
+        yield bpHit;
 
-    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 6)
-      .then(testPaused)
-      .then(() => {
-        // Switch to the second source.
-        let finished = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.SOURCE_SHOWN);
-        gSources.selectedIndex = 1;
-        return finished;
-      })
-      .then(testSecondSourceIsSelected)
-      .then(() => {
-        const finished = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.SOURCE_SHOWN);
-        clickPrettyPrintButton();
-        testProgressBarShown();
-        return finished;
-      })
-      .then(testSecondSourceIsStillSelected)
-      .then(() => closeDebuggerAndFinish(gPanel))
-      .then(null, aError => {
-        ok(false, "Got an error: " + DevToolsUtils.safeErrorString(aError));
-      })
+        info("Switch to the second source.");
+        const sourceShown = waitForSourceShown(gPanel, SECOND_SOURCE_VALUE);
+        gSources.selectedValue = SECOND_SOURCE_VALUE;
+        yield sourceShown;
 
-    gDebuggee.secondCall();
+        info("Pretty print the source.");
+        const prettyPrinted = waitForSourceShown(gPanel, SECOND_SOURCE_VALUE);
+        gDebugger.document.getElementById("pretty-print").click();
+        yield prettyPrinted;
+
+        yield resumeDebuggerThenCloseAndFinish(gPanel);
+      } catch (e) {
+        DevToolsUtils.reportException("browser_dbg_pretty-print-on-paused.js", e);
+        ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+      }
+    });
   });
 }
 
-function testPaused() {
-  is(gDebugger.gThreadClient.paused, true,
-    "The thread should be paused");
-}
-
-function testSecondSourceIsSelected() {
-  ok(gSources.containsValue(EXAMPLE_URL + gSecondSourceLabel),
-    "The second source should be selected.");
-}
-
-function clickPrettyPrintButton() {
-  gDebugger.document.getElementById("pretty-print").click();
-}
-
-function testProgressBarShown() {
-  const deck = gDebugger.document.getElementById("editor-deck");
-  is(deck.selectedIndex, 2, "The progress bar should be shown");
-}
-
-function testSecondSourceIsStillSelected() {
-  ok(gSources.containsValue(EXAMPLE_URL + gSecondSourceLabel),
-    "The second source should still be selected.");
-}
-
 registerCleanupFunction(function() {
   gTab = null;
   gDebuggee = null;
   gPanel = null;
   gDebugger = null;
+  gThreadClient = null;
   gSources = null;
 });
--- a/browser/devtools/debugger/test/head.js
+++ b/browser/devtools/debugger/test/head.js
@@ -17,16 +17,17 @@ let { Promise: promise } = Cu.import("re
 let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
 let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 let { require } = devtools;
 let { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
 let { BrowserToolboxProcess } = Cu.import("resource:///modules/devtools/ToolboxProcess.jsm", {});
 let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
 let { DebuggerClient } = Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {});
 let { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {});
+const { promiseInvoke } = require("devtools/async-utils");
 let TargetFactory = devtools.TargetFactory;
 let Toolbox = devtools.Toolbox;
 
 const EXAMPLE_URL = "http://example.com/browser/browser/devtools/debugger/test/";
 
 gDevTools.testing = true;
 SimpleTest.registerCleanupFunction(() => {
   gDevTools.testing = false;
@@ -851,8 +852,28 @@ function attachAddonActorForUrl(aClient,
   getAddonActorForUrl(aClient, aUrl).then(aGrip => {
     aClient.attachAddon(aGrip.actor, aResponse => {
       deferred.resolve([aGrip, aResponse]);
     });
   });
 
   return deferred.promise;
 }
+
+function rdpInvoke(aClient, aMethod, ...args) {
+  return promiseInvoke(aClient, aMethod, ...args)
+    .then(({error, message }) => {
+      if (error) {
+        throw new Error(error + ": " + message);
+      }
+    });
+}
+
+function doResume(aPanel) {
+  const threadClient = aPanel.panelWin.gThreadClient;
+  return rdpInvoke(threadClient, threadClient.resume);
+}
+
+function doInterrupt(aPanel) {
+  const threadClient = aPanel.panelWin.gThreadClient;
+  return rdpInvoke(threadClient, threadClient.interrupt);
+}
+
new file mode 100644
--- /dev/null
+++ b/browser/devtools/eyedropper/commands.js
@@ -0,0 +1,50 @@
+/* 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/. */
+
+const gcli = require("gcli/index");
+const EventEmitter = require("devtools/toolkit/event-emitter");
+const eventEmitter = new EventEmitter();
+
+let { Eyedropper, EyedropperManager } = require("devtools/eyedropper/eyedropper");
+
+/**
+ * 'eyedropper' command
+ */
+exports.items = [{
+  name: "eyedropper",
+  description: gcli.lookup("eyedropperDesc"),
+  manual: gcli.lookup("eyedropperManual"),
+  buttonId: "command-button-eyedropper",
+  buttonClass: "command-button command-button-invertable",
+  tooltipText: gcli.lookup("eyedropperTooltip"),
+  state: {
+    isChecked: function(target) {
+      let chromeWindow = target.tab.ownerDocument.defaultView;
+      let dropper = EyedropperManager.getInstance(chromeWindow);
+      if (dropper) {
+        return true;
+      }
+      return false;
+    },
+    onChange: function(target, changeHandler) {
+      eventEmitter.on("changed", changeHandler);
+    },
+    offChange: function(target, changeHandler) {
+      eventEmitter.off("changed", changeHandler);
+    },
+  },
+  exec: function(args, context) {
+    let chromeWindow = context.environment.chromeWindow;
+    let target = context.environment.target;
+
+    let dropper = EyedropperManager.createInstance(chromeWindow);
+    dropper.open();
+
+    eventEmitter.emit("changed", target.tab);
+
+    dropper.once("destroy", () => {
+      eventEmitter.emit("changed", target.tab);
+    });
+  }
+}];
--- a/browser/devtools/eyedropper/eyedropper.js
+++ b/browser/devtools/eyedropper/eyedropper.js
@@ -44,16 +44,50 @@ const FORMAT_PREF = "devtools.defaultCol
 const CANVAS_WIDTH = 96;
 const CANVAS_OFFSET = 3; // equals the border width of the canvas.
 const CLOSE_DELAY = 750;
 
 const HEX_BOX_WIDTH = CANVAS_WIDTH + CANVAS_OFFSET * 2;
 const HSL_BOX_WIDTH = 158;
 
 /**
+ * Manage instances of eyedroppers for windows. Registering here isn't
+ * necessary for creating an eyedropper, but can be used for testing.
+ */
+let EyedropperManager = {
+  _instances: new WeakMap(),
+
+  getInstance: function(chromeWindow) {
+    return this._instances.get(chromeWindow);
+  },
+
+  createInstance: function(chromeWindow) {
+    let dropper = this.getInstance(chromeWindow);
+    if (dropper) {
+      return dropper;
+    }
+
+    dropper = new Eyedropper(chromeWindow);
+    this._instances.set(chromeWindow, dropper);
+
+    dropper.on("destroy", () => {
+      this.deleteInstance(chromeWindow);
+    });
+
+    return dropper;
+  },
+
+  deleteInstance: function(chromeWindow) {
+    this._instances.delete(chromeWindow);
+  }
+}
+
+exports.EyedropperManager = EyedropperManager;
+
+/**
  * Eyedropper widget. Once opened, shows zoomed area above current pixel and
  * displays the color value of the center pixel. Clicking on the window will
  * close the widget and fire a 'select' event. If 'copyOnSelect' is true, the color
  * will also be copied to the clipboard.
  *
  * let eyedropper = new Eyedropper(window);
  * eyedropper.open();
  *
--- a/browser/devtools/eyedropper/moz.build
+++ b/browser/devtools/eyedropper/moz.build
@@ -2,12 +2,13 @@
 # vim: set filetype=python:
 # 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/.
 
 JS_MODULES_PATH = 'modules/devtools/eyedropper'
 
 EXTRA_JS_MODULES += [
+    'commands.js',
     'eyedropper.js'
 ]
 
 BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
--- a/browser/devtools/eyedropper/test/browser.ini
+++ b/browser/devtools/eyedropper/test/browser.ini
@@ -2,8 +2,9 @@
 skip-if = e10s # Bug ?????? - devtools tests disabled with e10s
 subsuite = devtools
 support-files =
   color-block.html
   head.js
 
 [browser_eyedropper_basic.js]
 skip-if = os == "win" && debug # bug 963492
+[browser_eyedropper_cmd.js]
--- a/browser/devtools/eyedropper/test/browser_eyedropper_basic.js
+++ b/browser/devtools/eyedropper/test/browser_eyedropper_basic.js
@@ -57,19 +57,8 @@ function inspectPage(dropper, click=true
       EventUtils.synthesizeMouse(target, 30, 30, {}, win);
     }
   });
 }
 
 function pressESC() {
   EventUtils.synthesizeKey("VK_ESCAPE", { });
 }
-
-function dropperLoaded(dropper) {
-  if (dropper.loaded) {
-    return promise.resolve();
-  }
-
-  let deferred = promise.defer();
-  dropper.once("load", deferred.resolve);
-
-  return deferred.promise;
-}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/eyedropper/test/browser_eyedropper_cmd.js
@@ -0,0 +1,56 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the eyedropper command works
+
+const TESTCASE_URI = TEST_BASE + "color-block.html";
+const DIV_COLOR = "#0000FF";
+
+function test() {
+  return Task.spawn(spawnTest).then(finish, helpers.handleError);
+}
+
+function spawnTest() {
+  let options = yield helpers.openTab(TESTCASE_URI);
+  yield helpers.openToolbar(options);
+
+  yield helpers.audit(options, [
+    {
+      setup: "eyedropper",
+      check: {
+        input: "eyedropper"
+      },
+      exec: { output: "" }
+    },
+  ]);
+
+  yield inspectAndWaitForCopy();
+
+  yield helpers.closeToolbar(options);
+  yield helpers.closeTab(options);
+}
+
+function inspectAndWaitForCopy() {
+  let deferred = promise.defer();
+
+  waitForClipboard(DIV_COLOR, () => {
+    inspectPage(); // setup: inspect the page
+  }, deferred.resolve, deferred.reject);
+
+  return deferred.promise;
+}
+
+function inspectPage() {
+  let target = content.document.getElementById("test");
+  let win = content.window;
+
+  EventUtils.synthesizeMouse(target, 20, 20, { type: "mousemove" }, win);
+
+  let dropper = EyedropperManager.getInstance(window);
+
+  return dropperLoaded(dropper).then(() => {
+    EventUtils.synthesizeMouse(target, 30, 30, { type: "mousemove" }, win);
+
+    EventUtils.synthesizeMouse(target, 30, 30, {}, win);
+  });
+}
--- a/browser/devtools/eyedropper/test/head.js
+++ b/browser/devtools/eyedropper/test/head.js
@@ -1,18 +1,19 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const TEST_BASE = "chrome://mochitests/content/browser/browser/devtools/eyedropper/test/";
 const TEST_HOST = 'mochi.test:8888';
 
-const promise = Cu.import("resource://gre/modules/devtools/deprecated-sync-thenables.js").Promise;
-const require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
-const { Eyedropper } = require("devtools/eyedropper/eyedropper");
+let { devtools } = Components.utils.import("resource://gre/modules/devtools/Loader.jsm", {});
+const { Eyedropper, EyedropperManager } = devtools.require("devtools/eyedropper/eyedropper");
 
+let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
+Services.scriptloader.loadSubScript(testDir + "../../../commandline/test/helpers.js", this);
 
 waitForExplicitFinish();
 
 function cleanup()
 {
   while (gBrowser.tabs.length > 1) {
     gBrowser.removeCurrentTab();
   }
@@ -30,8 +31,15 @@ function addTab(uri) {
     gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
     deferred.resolve(tab);
   }, true);
 
   content.location = uri;
 
   return deferred.promise;
 }
+
+function dropperLoaded(dropper) {
+  if (dropper.loaded) {
+    return promise.resolve();
+  }
+  return dropper.once("load");
+}
--- a/browser/devtools/framework/toolbox.js
+++ b/browser/devtools/framework/toolbox.js
@@ -568,17 +568,18 @@ Toolbox.prototype = {
     // White-list buttons that can be toggled to prevent adding prefs for
     // addons that have manually inserted toolbarbuttons into DOM.
     return [
       "command-button-pick",
       "command-button-splitconsole",
       "command-button-responsive",
       "command-button-paintflashing",
       "command-button-tilt",
-      "command-button-scratchpad"
+      "command-button-scratchpad",
+      "command-button-eyedropper"
     ].map(id => {
       let button = this.doc.getElementById(id);
       // Some buttons may not exist inside of Browser Toolbox
       if (!button) {
         return false;
       }
       return {
         id: id,
--- a/browser/devtools/main.js
+++ b/browser/devtools/main.js
@@ -123,16 +123,17 @@ Tools.inspector = {
   invertIconForLightTheme: true,
   url: "chrome://browser/content/devtools/inspector/inspector.xul",
   label: l10n("inspector.label", inspectorStrings),
   tooltip: l10n("inspector.tooltip", inspectorStrings),
   inMenu: true,
   commands: [
     "devtools/resize-commands",
     "devtools/inspector/inspector-commands",
+    "devtools/eyedropper/commands.js"
   ],
 
   preventClosingOnKey: true,
   onkey: function(panel) {
     panel.toolbox.highlighterUtils.togglePicker();
   },
 
   isTargetSupported: function(target) {
--- a/browser/devtools/shared/widgets/Tooltip.js
+++ b/browser/devtools/shared/widgets/Tooltip.js
@@ -688,17 +688,17 @@ Tooltip.prototype = {
    */
   setColorPickerContent: function(color) {
     let def = promise.defer();
 
     // Create an iframe to contain spectrum
     let iframe = this.doc.createElementNS(XHTML_NS, "iframe");
     iframe.setAttribute("transparent", true);
     iframe.setAttribute("width", "210");
-    iframe.setAttribute("height", "220");
+    iframe.setAttribute("height", "216");
     iframe.setAttribute("flex", "1");
     iframe.setAttribute("class", "devtools-tooltip-iframe");
 
     let panel = this.panel;
     let xulWin = this.doc.ownerGlobal;
 
     // Wait for the load to initialize spectrum
     function onLoad() {
--- a/browser/devtools/shared/widgets/spectrum-frame.xhtml
+++ b/browser/devtools/shared/widgets/spectrum-frame.xhtml
@@ -14,11 +14,11 @@
     body {
       margin: 0;
       padding: 0;
     }
   </style>
 </head>
 <body role="application">
   <div id="spectrum"></div>
-  <button id="eyedropper-button"></button>
+  <div id="eyedropper-button"></div>
 </body>
 </html>
\ No newline at end of file
--- a/browser/devtools/shared/widgets/spectrum.css
+++ b/browser/devtools/shared/widgets/spectrum.css
@@ -1,24 +1,47 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #eyedropper-button {
-  background: url("chrome://browser/skin/devtools/eyedropper-black.png") no-repeat center;
-  width: 20px;
-  height: 20px;
-  -moz-margin-start: 6px;
-  border: 1px solid #ccc;
+  background-image: url("chrome://browser/skin/devtools/command-eyedropper.png");
+  width: 16px;
+  height: 16px;
+  background-size: 64px 16px;
+  background-position: 0 center;
+  background-repeat: no-repeat;
+  -moz-margin-start: 5px;
   border-radius: 2px;
   cursor: pointer;
 }
 
+.theme-light #eyedropper-button {
+  filter: url(chrome://browser/skin/devtools/filters.svg#invert);
+  border: 1px solid #AAA;
+}
+
 .theme-dark #eyedropper-button {
-  filter: url(chrome://browser/skin/devtools/filters.svg#colorpicker-invert);
+  border: 1px solid #444;
+}
+
+#eyedropper-button:hover {
+  background-position: -16px center;
+}
+#eyedropper-button:hover:active {
+  background-position: -32px center;
+}
+#eyedropper-button[checked=true] {
+  background-position: -48px center;
+}
+
+@media (min-resolution: 2dppx) {
+  #eyedropper-button {
+    background-image: url("chrome://browser/skin/devtools/command-eyedropper@2x.png");
+  }
 }
 
 /* Mix-in classes */
 
 .spectrum-checker {
   background-color: #eee;
   background-image: linear-gradient(45deg, #ccc 25%, transparent 25%, transparent 75%, #ccc 75%, #ccc),
     linear-gradient(45deg, #ccc 25%, transparent 25%, transparent 75%, #ccc 75%, #ccc);
--- a/browser/installer/windows/nsis/shared.nsh
+++ b/browser/installer/windows/nsis/shared.nsh
@@ -425,16 +425,40 @@ FunctionEnd
     WriteRegStr SHCTX "$0\.xht"   "" "FirefoxHTML"
   ${EndIf}
 
   ReadRegStr $6 SHCTX "$0\.xhtml" ""
   ${If} "$6" != "FirefoxHTML"
     WriteRegStr SHCTX "$0\.xhtml" "" "FirefoxHTML"
   ${EndIf}
 
+  ; Only add .oga if it's not present
+  ${CheckIfRegistryKeyExists} "$0" ".oga" $7
+  ${If} $7 == "false"
+    WriteRegStr SHCTX "$0\.oga"  "" "FirefoxHTML"
+  ${EndIf}
+
+  ; Only add .ogg if it's not present
+  ${CheckIfRegistryKeyExists} "$0" ".ogg" $7
+  ${If} $7 == "false"
+    WriteRegStr SHCTX "$0\.ogg"  "" "FirefoxHTML"
+  ${EndIf}
+
+  ; Only add .ogv if it's not present
+  ${CheckIfRegistryKeyExists} "$0" ".ogv" $7
+  ${If} $7 == "false"
+    WriteRegStr SHCTX "$0\.ogv"  "" "FirefoxHTML"
+  ${EndIf}
+
+  ; Only add .pdf if it's not present
+  ${CheckIfRegistryKeyExists} "$0" ".pdf" $7
+  ${If} $7 == "false"
+    WriteRegStr SHCTX "$0\.pdf"  "" "FirefoxHTML"
+  ${EndIf}
+
   ; Only add webm if it's not present
   ${CheckIfRegistryKeyExists} "$0" ".webm" $7
   ${If} $7 == "false"
     WriteRegStr SHCTX "$0\.webm"  "" "FirefoxHTML"
   ${EndIf}
 
   ; An empty string is used for the 5th param because FirefoxHTML is not a
   ; protocol handler
--- a/browser/installer/windows/nsis/uninstaller.nsi
+++ b/browser/installer/windows/nsis/uninstaller.nsi
@@ -319,16 +319,20 @@ Section "Uninstall"
   ; Don't clean up the file handlers if the FirefoxHTML key still exists since
   ; there should be a second installation that may be the default file handler
   ${If} ${Errors}
     ${un.RegCleanFileHandler}  ".htm"   "FirefoxHTML"
     ${un.RegCleanFileHandler}  ".html"  "FirefoxHTML"
     ${un.RegCleanFileHandler}  ".shtml" "FirefoxHTML"
     ${un.RegCleanFileHandler}  ".xht"   "FirefoxHTML"
     ${un.RegCleanFileHandler}  ".xhtml" "FirefoxHTML"
+    ${un.RegCleanFileHandler}  ".oga"  "FirefoxHTML"
+    ${un.RegCleanFileHandler}  ".ogg"  "FirefoxHTML"
+    ${un.RegCleanFileHandler}  ".ogv"  "FirefoxHTML"
+    ${un.RegCleanFileHandler}  ".pdf"  "FirefoxHTML"
     ${un.RegCleanFileHandler}  ".webm"  "FirefoxHTML"
   ${EndIf}
 
   SetShellVarContext all  ; Set SHCTX to HKLM
   ${un.GetSecondInstallPath} "Software\Mozilla" $R9
   ${If} $R9 == "false"
     SetShellVarContext current  ; Set SHCTX to HKCU
     ${un.GetSecondInstallPath} "Software\Mozilla" $R9
--- a/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties
@@ -160,16 +160,30 @@ inspectManual=Investigate the dimensions
 # when the user is using this command.
 inspectNodeDesc=CSS selector
 
 # LOCALIZATION NOTE (inspectNodeManual) A fuller description of the 'node'
 # parameter to the 'inspect' command, displayed when the user asks for help
 # on what it does.
 inspectNodeManual=A CSS selector for use with document.querySelector which identifies a single element
 
+# LOCALIZATION NOTE (eyedropperDesc) A very short description of the 'eyedropper'
+# command. See eyedropperManual for a fuller description of what it does. This
+# string is designed to be shown in a menu alongside the command name, which
+# is why it should be as short as possible.
+eyedropperDesc=Grab a color from the page
+
+# LOCALIZATION NOTE (eyedropperManual) A fuller description of the 'eyedropper'
+# command, displayed when the user asks for help on what it does.
+eyedropperManual=Open a panel that magnifies an area of page to inspect pixels and copy color values
+
+# LOCALIZATION NOTE (eyedropperTooltip) A string displayed as the
+# tooltip of button in devtools toolbox which toggles the Eyedropper tool.
+eyedropperTooltip=Grab a color from the page
+
 # LOCALIZATION NOTE (tiltDesc) A very short description of the 'tilt'
 # command. See tiltManual for a fuller description of what it does. This
 # string is designed to be shown in a menu alongside the command name, which
 # is why it should be as short as possible.
 tiltDesc=Visualize the webpage in 3D
 
 # LOCALIZATION NOTE (tiltManual) A fuller description of the 'tilt'
 # command, displayed when the user asks for help on what it does.
new file mode 100644
--- /dev/null
+++ b/browser/modules/ContentSearch.jsm
@@ -0,0 +1,149 @@
+/* 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 = [
+  "ContentSearch",
+];
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+const INBOUND_MESSAGE = "ContentSearch";
+const OUTBOUND_MESSAGE = INBOUND_MESSAGE;
+
+/**
+ * ContentSearch receives messages named INBOUND_MESSAGE and sends messages
+ * named OUTBOUND_MESSAGE.  The data of each message is expected to look like
+ * { type, data }.  type is the message's type (or subtype if you consider the
+ * type of the message itself to be INBOUND_MESSAGE), and data is data that is
+ * specific to the type.
+ *
+ * Inbound messages have the following types:
+ *
+ *   GetState
+ *      Retrieves the current search engine state.
+ *      data: null
+ *   ManageEngines
+ *      Opens the search engine management window.
+ *      data: null
+ *   Search
+ *      Performs a search.
+ *      data: an object { engineName, searchString, whence }
+ *   SetCurrentEngine
+ *      Sets the current engine.
+ *      data: the name of the engine
+ *
+ * Outbound messages have the following types:
+ *
+ *   CurrentEngine
+ *     Sent when the current engine changes.
+ *     data: see _currentEngineObj
+ *   State
+ *     Sent in reply to GetState and when the state changes.
+ *     data: see _currentStateObj
+ */
+
+this.ContentSearch = {
+
+  init: function () {
+    Cc["@mozilla.org/globalmessagemanager;1"].
+      getService(Ci.nsIMessageListenerManager).
+      addMessageListener(INBOUND_MESSAGE, this);
+    Services.obs.addObserver(this, "browser-search-engine-modified", false);
+  },
+
+  receiveMessage: function (msg) {
+    let methodName = "on" + msg.data.type;
+    if (methodName in this) {
+      this[methodName](msg, msg.data.data);
+    }
+  },
+
+  onGetState: function (msg, data) {
+    this._reply(msg, "State", this._currentStateObj());
+  },
+
+  onSearch: function (msg, data) {
+    let expectedDataProps = [
+      "engineName",
+      "searchString",
+      "whence",
+    ];
+    for (let prop of expectedDataProps) {
+      if (!(prop in data)) {
+        Cu.reportError("Message data missing required property: " + prop);
+        return;
+      }
+    }
+    let browserWin = msg.target.ownerDocument.defaultView;
+    let engine = Services.search.getEngineByName(data.engineName);
+    browserWin.BrowserSearch.recordSearchInHealthReport(engine, data.whence);
+    let submission = engine.getSubmission(data.searchString, "", data.whence);
+    browserWin.loadURI(submission.uri.spec, null, submission.postData);
+  },
+
+  onSetCurrentEngine: function (msg, data) {
+    Services.search.currentEngine = Services.search.getEngineByName(data);
+  },
+
+  onManageEngines: function (msg, data) {
+    let browserWin = msg.target.ownerDocument.defaultView;
+    browserWin.BrowserSearch.searchBar.openManager(null);
+  },
+
+  observe: function (subj, topic, data) {
+    switch (topic) {
+    case "browser-search-engine-modified":
+      if (data == "engine-current") {
+        this._broadcast("CurrentEngine", this._currentEngineObj());
+      }
+      else if (data != "engine-default") {
+        // engine-default is always sent with engine-current and isn't otherwise
+        // relevant to content searches.
+        this._broadcast("State", this._currentStateObj());
+      }
+      break;
+    }
+  },
+
+  _reply: function (msg, type, data) {
+    msg.target.messageManager.sendAsyncMessage(...this._msgArgs(type, data));
+  },
+
+  _broadcast: function (type, data) {
+    Cc["@mozilla.org/globalmessagemanager;1"].
+      getService(Ci.nsIMessageListenerManager).
+      broadcastAsyncMessage(...this._msgArgs(type, data));
+  },
+
+  _msgArgs: function (type, data) {
+    return [OUTBOUND_MESSAGE, {
+      type: type,
+      data: data,
+    }];
+  },
+
+  _currentStateObj: function () {
+    return {
+      engines: Services.search.getVisibleEngines().map(engine => {
+        return {
+          name: engine.name,
+          iconURI: engine.getIconURLBySize(16, 16),
+        };
+      }),
+      currentEngine: this._currentEngineObj(),
+    };
+  },
+
+  _currentEngineObj: function () {
+    return {
+      name: Services.search.currentEngine.name,
+      logoURI: Services.search.currentEngine.getIconURLBySize(65, 26),
+      logo2xURI: Services.search.currentEngine.getIconURLBySize(130, 52),
+    };
+  },
+};
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -6,16 +6,17 @@
 
 TEST_DIRS += ['test']
 
 EXTRA_JS_MODULES += [
     'BrowserNewTabPreloader.jsm',
     'BrowserUITelemetry.jsm',
     'ContentClick.jsm',
     'ContentLinkHandler.jsm',
+    'ContentSearch.jsm',
     'CustomizationTabPreloader.jsm',
     'Feeds.jsm',
     'NetworkPrioritizer.jsm',
     'offlineAppCache.jsm',
     'RemotePrompt.jsm',
     'SharedFrame.jsm',
     'SitePermissions.jsm',
     'Social.jsm',
--- a/browser/modules/test/browser.ini
+++ b/browser/modules/test/browser.ini
@@ -1,15 +1,17 @@
 [DEFAULT]
 support-files =
   head.js
+  contentSearch.js
+  image.png
   uitour.*
-  image.png
 
 [browser_BrowserUITelemetry_buckets.js]
+[browser_ContentSearch.js]
 [browser_NetworkPrioritizer.js]
 skip-if = e10s # Bug 666804 - Support NetworkPrioritizer in e10s
 [browser_SignInToWebsite.js]
 skip-if = e10s # Bug 941426 - SignIntoWebsite.jsm not e10s friendly
 [browser_UITour.js]
 skip-if = os == "linux" || e10s # Intermittent failures, bug 951965
 [browser_UITour2.js]
 skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
new file mode 100644
--- /dev/null
+++ b/browser/modules/test/browser_ContentSearch.js
@@ -0,0 +1,240 @@
+/* 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/. */
+
+const TEST_MSG = "ContentSearchTest";
+const CONTENT_SEARCH_MSG = "ContentSearch";
+const TEST_CONTENT_SCRIPT_BASENAME = "contentSearch.js";
+
+function generatorTest() {
+  // nextStep() drives the iterator returned by this function.  This function's
+  // iterator in turn drives the iterator of each test below.
+  let currentTestIter = yield startNextTest();
+  let arg = undefined;
+  while (currentTestIter) {
+    try {
+      currentTestIter.send(arg);
+      arg = yield null;
+    }
+    catch (err if err instanceof StopIteration) {
+      currentTestIter = yield startNextTest();
+      arg = undefined;
+    }
+  }
+}
+
+function startNextTest() {
+  if (!gTests.length) {
+    setTimeout(() => nextStep(null), 0);
+    return;
+  }
+  let nextTestGen = gTests.shift();
+  let nextTestIter = nextTestGen();
+  addTab(() => {
+    info("Starting test " + nextTestGen.name);
+    nextStep(nextTestIter);
+  });
+}
+
+function addTest(testGen) {
+  gTests.push(testGen);
+}
+
+var gTests = [];
+var gMsgMan;
+
+addTest(function GetState() {
+  gMsgMan.sendAsyncMessage(TEST_MSG, {
+    type: "GetState",
+  });
+  let msg = yield waitForTestMsg("State");
+  checkMsg(msg, {
+    type: "State",
+    data: currentStateObj(),
+  });
+});
+
+addTest(function SetCurrentEngine() {
+  let newCurrentEngine = null;
+  let oldCurrentEngine = Services.search.currentEngine;
+  let engines = Services.search.getVisibleEngines();
+  for (let engine of engines) {
+    if (engine != oldCurrentEngine) {
+      newCurrentEngine = engine;
+      break;
+    }
+  }
+  if (!newCurrentEngine) {
+    info("Couldn't find a non-selected search engine, " +
+         "skipping this part of the test");
+    return;
+  }
+  gMsgMan.sendAsyncMessage(TEST_MSG, {
+    type: "SetCurrentEngine",
+    data: newCurrentEngine.name,
+  });
+  Services.obs.addObserver(function obs(subj, topic, data) {
+    info("Test observed " + data);
+    if (data == "engine-current") {
+      ok(true, "Test observed engine-current");
+      Services.obs.removeObserver(obs, "browser-search-engine-modified", false);
+      nextStep();
+    }
+  }, "browser-search-engine-modified", false);
+  info("Waiting for test to observe engine-current...");
+  waitForTestMsg("CurrentEngine");
+  let maybeMsg1 = yield null;
+  let maybeMsg2 = yield null;
+  let msg = maybeMsg1 || maybeMsg2;
+  ok(!!msg,
+     "Sanity check: One of the yields is for waitForTestMsg and should have " +
+     "therefore produced a message object");
+  checkMsg(msg, {
+    type: "CurrentEngine",
+    data: currentEngineObj(newCurrentEngine),
+  });
+
+  Services.search.currentEngine = oldCurrentEngine;
+  let msg = yield waitForTestMsg("CurrentEngine");
+  checkMsg(msg, {
+    type: "CurrentEngine",
+    data: currentEngineObj(oldCurrentEngine),
+  });
+});
+
+addTest(function ManageEngines() {
+  gMsgMan.sendAsyncMessage(TEST_MSG, {
+    type: "ManageEngines",
+  });
+  let winWatcher = Cc["@mozilla.org/embedcomp/window-watcher;1"].
+                   getService(Ci.nsIWindowWatcher);
+  winWatcher.registerNotification(function onOpen(subj, topic, data) {
+    if (topic == "domwindowopened" && subj instanceof Ci.nsIDOMWindow) {
+      subj.addEventListener("load", function onLoad() {
+        subj.removeEventListener("load", onLoad);
+        if (subj.document.documentURI ==
+            "chrome://browser/content/search/engineManager.xul") {
+          winWatcher.unregisterNotification(onOpen);
+          ok(true, "Observed search manager window open");
+          is(subj.opener, window,
+             "Search engine manager opener should be this chrome window");
+          subj.close();
+          nextStep();
+        }
+      });
+    }
+  });
+  info("Waiting for search engine manager window to open...");
+  yield null;
+});
+
+addTest(function modifyEngine() {
+  let engine = Services.search.currentEngine;
+  let oldAlias = engine.alias;
+  engine.alias = "ContentSearchTest";
+  let msg = yield waitForTestMsg("State");
+  checkMsg(msg, {
+    type: "State",
+    data: currentStateObj(),
+  });
+  engine.alias = oldAlias;
+  msg = yield waitForTestMsg("State");
+  checkMsg(msg, {
+    type: "State",
+    data: currentStateObj(),
+  });
+});
+
+addTest(function search() {
+  let engine = Services.search.currentEngine;
+  let data = {
+    engineName: engine.name,
+    searchString: "ContentSearchTest",
+    whence: "ContentSearchTest",
+  };
+  gMsgMan.sendAsyncMessage(TEST_MSG, {
+    type: "Search",
+    data: data,
+  });
+  let submissionURL =
+    engine.getSubmission(data.searchString, "", data.whence).uri.spec;
+  let listener = {
+    onStateChange: function (webProg, req, flags, status) {
+      let url = req.originalURI.spec;
+      info("onStateChange " + url);
+      let docStart = Ci.nsIWebProgressListener.STATE_IS_DOCUMENT |
+                     Ci.nsIWebProgressListener.STATE_START;
+      if ((flags & docStart) && webProg.isTopLevel && url == submissionURL) {
+        gBrowser.removeProgressListener(listener);
+        ok(true, "Search URL loaded");
+        req.cancel(Components.results.NS_ERROR_FAILURE);
+        nextStep();
+      }
+    }
+  };
+  gBrowser.addProgressListener(listener);
+  info("Waiting for search URL to load: " + submissionURL);
+  yield null;
+});
+
+function checkMsg(actualMsg, expectedMsgData) {
+  SimpleTest.isDeeply(actualMsg.data, expectedMsgData, "Checking message");
+}
+
+function waitForMsg(name, type, callback) {
+  info("Waiting for " + name + " message " + type + "...");
+  gMsgMan.addMessageListener(name, function onMsg(msg) {
+    info("Received " + name + " message " + msg.data.type + "\n");
+    if (msg.data.type == type) {
+      gMsgMan.removeMessageListener(name, onMsg);
+      (callback || nextStep)(msg);
+    }
+  });
+}
+
+function waitForTestMsg(type, callback) {
+  waitForMsg(TEST_MSG, type, callback);
+}
+
+function addTab(onLoad) {
+  let tab = gBrowser.addTab();
+  gBrowser.selectedTab = tab;
+  tab.linkedBrowser.addEventListener("load", function load() {
+    tab.removeEventListener("load", load, true);
+    let url = getRootDirectory(gTestPath) + TEST_CONTENT_SCRIPT_BASENAME;
+    gMsgMan = tab.linkedBrowser.messageManager;
+    gMsgMan.sendAsyncMessage(CONTENT_SEARCH_MSG, {
+      type: "AddToWhitelist",
+      data: ["about:blank"],
+    });
+    waitForMsg(CONTENT_SEARCH_MSG, "AddToWhitelistAck", () => {
+      gMsgMan.loadFrameScript(url, false);
+      onLoad();
+    });
+  }, true);
+  registerCleanupFunction(() => gBrowser.removeTab(tab));
+}
+
+function currentStateObj() {
+  return {
+    engines: Services.search.getVisibleEngines().map(engine => {
+      return {
+        name: engine.name,
+        iconURI: engine.getIconURLBySize(16, 16),
+      };
+    }),
+    currentEngine: currentEngineObj(),
+  };
+}
+
+function currentEngineObj(expectedCurrentEngine) {
+  if (expectedCurrentEngine) {
+    is(Services.search.currentEngine.name, expectedCurrentEngine.name,
+       "Sanity check: expected current engine");
+  }
+  return {
+    name: Services.search.currentEngine.name,
+    logoURI: Services.search.currentEngine.getIconURLBySize(65, 26),
+    logo2xURI: Services.search.currentEngine.getIconURLBySize(130, 52),
+  };
+}
new file mode 100644
--- /dev/null
+++ b/browser/modules/test/contentSearch.js
@@ -0,0 +1,21 @@
+/* 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/. */
+
+const TEST_MSG = "ContentSearchTest";
+const SERVICE_EVENT_TYPE = "ContentSearchService";
+const CLIENT_EVENT_TYPE = "ContentSearchClient";
+
+// Forward events from the in-content service to the test.
+content.addEventListener(SERVICE_EVENT_TYPE, event => {
+  sendAsyncMessage(TEST_MSG, event.detail);
+});
+
+// Forward messages from the test to the in-content service.
+addMessageListener(TEST_MSG, msg => {
+  content.dispatchEvent(
+    new content.CustomEvent(CLIENT_EVENT_TYPE, {
+      detail: msg.data,
+    })
+  );
+});
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -194,16 +194,18 @@ browser.jar:
   skin/classic/browser/devtools/command-scratchpad.png        (../shared/devtools/images/command-scratchpad.png)
   skin/classic/browser/devtools/command-scratchpad@2x.png     (../shared/devtools/images/command-scratchpad@2x.png)
   skin/classic/browser/devtools/command-tilt.png              (../shared/devtools/images/command-tilt.png)
   skin/classic/browser/devtools/command-tilt@2x.png           (../shared/devtools/images/command-tilt@2x.png)
   skin/classic/browser/devtools/command-pick.png              (../shared/devtools/images/command-pick.png)
   skin/classic/browser/devtools/command-pick@2x.png           (../shared/devtools/images/command-pick@2x.png)
   skin/classic/browser/devtools/command-console.png           (../shared/devtools/images/command-console.png)
   skin/classic/browser/devtools/command-console@2x.png        (../shared/devtools/images/command-console@2x.png)
+  skin/classic/browser/devtools/command-eyedropper.png        (../shared/devtools/images/command-eyedropper.png)
+  skin/classic/browser/devtools/command-eyedropper@2x.png     (../shared/devtools/images/command-eyedropper@2x.png)
   skin/classic/browser/devtools/alerticon-warning.png (devtools/alerticon-warning.png)
 * skin/classic/browser/devtools/ruleview.css          (../shared/devtools/ruleview.css)
 * skin/classic/browser/devtools/webconsole.css                  (devtools/webconsole.css)
   skin/classic/browser/devtools/webconsole_networkpanel.css     (devtools/webconsole_networkpanel.css)
   skin/classic/browser/devtools/webconsole.png                  (devtools/webconsole.png)
   skin/classic/browser/devtools/commandline.css              (devtools/commandline.css)
   skin/classic/browser/devtools/markup-view.css       (../shared/devtools/markup-view.css)
   skin/classic/browser/devtools/editor-error.png       (devtools/editor-error.png)
@@ -292,17 +294,16 @@ browser.jar:
   skin/classic/browser/devtools/app-manager/error.svg                 (../shared/devtools/app-manager/images/error.svg)
   skin/classic/browser/devtools/app-manager/plus.svg                  (../shared/devtools/app-manager/images/plus.svg)
   skin/classic/browser/devtools/app-manager/remove.svg                (../shared/devtools/app-manager/images/remove.svg)
   skin/classic/browser/devtools/app-manager/add.svg                   (../shared/devtools/app-manager/images/add.svg)
   skin/classic/browser/devtools/app-manager/index-icons.svg           (../shared/devtools/app-manager/images/index-icons.svg)
   skin/classic/browser/devtools/app-manager/rocket.svg                (../shared/devtools/app-manager/images/rocket.svg)
   skin/classic/browser/devtools/app-manager/noise.png                 (../shared/devtools/app-manager/images/noise.png)
   skin/classic/browser/devtools/app-manager/default-app-icon.png      (../shared/devtools/app-manager/images/default-app-icon.png)
-  skin/classic/browser/devtools/eyedropper-black.png        (../shared/devtools/images/eyedropper-black.png)
 #ifdef MOZ_SERVICES_SYNC
   skin/classic/browser/sync-16.png
   skin/classic/browser/sync-32.png
   skin/classic/browser/sync-bg.png
   skin/classic/browser/sync-128.png
   skin/classic/browser/sync-desktopIcon.png
   skin/classic/browser/sync-horizontalbar.png
   skin/classic/browser/sync-mobileIcon.png
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -315,16 +315,18 @@ browser.jar:
   skin/classic/browser/devtools/command-scratchpad.png        (../shared/devtools/images/command-scratchpad.png)
   skin/classic/browser/devtools/command-scratchpad@2x.png     (../shared/devtools/images/command-scratchpad@2x.png)
   skin/classic/browser/devtools/command-tilt.png              (../shared/devtools/images/command-tilt.png)
   skin/classic/browser/devtools/command-tilt@2x.png           (../shared/devtools/images/command-tilt@2x.png)
   skin/classic/browser/devtools/command-pick.png              (../shared/devtools/images/command-pick.png)
   skin/classic/browser/devtools/command-pick@2x.png           (../shared/devtools/images/command-pick@2x.png)
   skin/classic/browser/devtools/command-console.png           (../shared/devtools/images/command-console.png)
   skin/classic/browser/devtools/command-console@2x.png        (../shared/devtools/images/command-console@2x.png)
+  skin/classic/browser/devtools/command-eyedropper.png        (../shared/devtools/images/command-eyedropper.png)
+  skin/classic/browser/devtools/command-eyedropper@2x.png     (../shared/devtools/images/command-eyedropper@2x.png)
   skin/classic/browser/devtools/alerticon-warning.png       (devtools/alerticon-warning.png)
 * skin/classic/browser/devtools/ruleview.css                (../shared/devtools/ruleview.css)
   skin/classic/browser/devtools/commandline.css             (devtools/commandline.css)
   skin/classic/browser/devtools/markup-view.css             (../shared/devtools/markup-view.css)
   skin/classic/browser/devtools/editor-error.png             (devtools/editor-error.png)
   skin/classic/browser/devtools/editor-breakpoint.png        (devtools/editor-breakpoint.png)
   skin/classic/browser/devtools/editor-debug-location.png    (devtools/editor-debug-location.png)
 * skin/classic/browser/devtools/webconsole.css                  (devtools/webconsole.css)
@@ -413,17 +415,16 @@ browser.jar:
   skin/classic/browser/devtools/app-manager/error.svg                 (../shared/devtools/app-manager/images/error.svg)
   skin/classic/browser/devtools/app-manager/plus.svg                  (../shared/devtools/app-manager/images/plus.svg)
   skin/classic/browser/devtools/app-manager/remove.svg                (../shared/devtools/app-manager/images/remove.svg)
   skin/classic/browser/devtools/app-manager/add.svg                   (../shared/devtools/app-manager/images/add.svg)
   skin/classic/browser/devtools/app-manager/index-icons.svg           (../shared/devtools/app-manager/images/index-icons.svg)
   skin/classic/browser/devtools/app-manager/rocket.svg                (../shared/devtools/app-manager/images/rocket.svg)
   skin/classic/browser/devtools/app-manager/noise.png                 (../shared/devtools/app-manager/images/noise.png)
   skin/classic/browser/devtools/app-manager/default-app-icon.png      (../shared/devtools/app-manager/images/default-app-icon.png)
-  skin/classic/browser/devtools/eyedropper-black.png        (../shared/devtools/images/eyedropper-black.png)
 
 #ifdef MOZ_SERVICES_SYNC
   skin/classic/browser/sync-16.png
   skin/classic/browser/sync-32.png
   skin/classic/browser/sync-bg.png
   skin/classic/browser/sync-128.png
   skin/classic/browser/sync-desktopIcon.png
   skin/classic/browser/sync-horizontalbar.png
--- a/browser/themes/shared/devtools/filters.svg
+++ b/browser/themes/shared/devtools/filters.svg
@@ -1,16 +1,9 @@
 <svg height="0" xmlns="http://www.w3.org/2000/svg">
 <filter id="invert" x="0%" y="0%" width="100%" height="100%" >
   <feComponentTransfer>
     <feFuncR type="table" tableValues=".1 0"/>
     <feFuncG type="table" tableValues=".1 0"/>
     <feFuncB type="table" tableValues=".1 0"/>
   </feComponentTransfer>
 </filter>
-<filter id="colorpicker-invert" x="0%" y="0%" width="100%" height="100%" >
-  <feComponentTransfer>
-    <feFuncR type="table" tableValues=".6 0"/>
-    <feFuncG type="table" tableValues=".6 0"/>
-    <feFuncB type="table" tableValues=".6 0"/>
-  </feComponentTransfer>
-</filter>
 </svg>
\ No newline at end of file
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a96b8eb8070221d28536098888cd3e0092a544d3
GIT binary patch
literal 1049
zc$@(l1m^pRP)<h;3K|Lk000e1NJLTq002M$000mO1^@s6rssJn000BvNkl<ZSPAWw
z&udgy6vywm^JeBvCNTt)+9sx;T^0WT2O=eP)udE6siYKJ3Ki-f;HJ_af^pSlid_hb
zGz*h7LM@aM5f?4#(v3(rjs+8SQf%nt{eJJBe(#KL#)-*$FSFGPGw<E|&gY){J?Gqe
zg?QCo?g&s8`OHl9dVQ}n%&!D-Z`mxIA)=>QSZ$+u%d+;7QM`!veh%}~eSK8h4gb^;
zBN3~gG1~x%+}Kc=*0S<g03BXh9g$4n#emF5V}Bffv%#kZz_$Ivio_QH3&gyq;S}P_
zrNXCK7T`F+Q5NEB7Hr}<pcyz_2Y)0Bzix%cz|F$8gx}yZ=Fw|qV>k<6`Dyi-Wbh<P
z%rQ@oR;Gp<ypc2svSr$i|0S`ITkO0o{cXG8du%V5h2va@0g>I&-=R}u?qxe#!skw2
z2x|wVV|aYN*6VSwgaGCQ{@RUO{8nLYoA`}rRY=6}S!058mH#I0r$4C*CoLY?|Ig>}
zR65tRK;`^}w-%504Ux-DWaVM^BCk}7-!T9>`<4@&SAyo5)Y*0r$m{75Ptq2kv+uaU
zWfc4mi2o<g1;JguVLnXbb@qiFEo0waAjL;v9|yr6DvD|vue0yUU>V#00<&uKEbRSa
ze~aHX0I_%``lYXwzfB>JvSJNkNM6GMsFD^Ci)WHQmQCY^VEGondy<5bf-jypK~WMP
zi^tZR9qKjal$38Hkaxf+7BmC@iF_;`tDXJx!{IqW1YR_FRN%})l7B6J)AqlsJHfbK
zq-D92sDzojce&75m>x`MT0G8;YnB^+pv`PA{AknhuVTE2U-J-6;I)sM6|Ssrh97IQ
zCsuU2W{0<`Sl7k|HxAJRTxju?#nqL{!dio`EIvG4SzNm{ez|HMnfv<%zT+0a0vly-
zNcyc<G=8_e=v`bPV-M~d=MEhrKgqHF2EJMLQZAGVxnEt^KOPBsuWaT|A#14zXPkLV
zb&2fUyy{UWiFnE?<t7ghxoQ66%oB-A(kRTc@1ImeQ)b<py#3jMZTVIJw0Or0&vV38
zmi(_I8pPP^5AGMf%`Dyvui(VLheiLRB7Or`&nT|D&$EgrJGzOzg;M3mBxn@;L5QEo
zD&CXfO)z(GW<17WJ&J8}uvqsdb`q}x+PVg9H$00S)|GfYk?jWG!(mtAH4pLEyA`i_
zPX^bq*LNjebL~TrcyBkE*ON3G?dvA<F^%hPq?^pgJf0)F$$ZSU0)WyjqwL<nwXr8_
z*~gPKpWKHn^FB!$$XfQDJoRJmPUEf{%v$yxJd1vm{WNZl!L+jP;9hlJ@(BD7yK5*4
T>WA9`00000NkvXXu0mjfj^_vF
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..edfe4c175398676e59bd27738f80e059a33db185
GIT binary patch
literal 2066
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
deleted file mode 100644
index 907a135e4d1f4580e893347478dc205d3aeaffde..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/shared/devtools/toolbars.inc.css
+++ b/browser/themes/shared/devtools/toolbars.inc.css
@@ -575,16 +575,19 @@
 #command-button-pick > image {
   background-image: url("chrome://browser/skin/devtools/command-pick.png");
 }
 
 #command-button-splitconsole > image {
   background-image: url("chrome://browser/skin/devtools/command-console.png");
 }
 
+#command-button-eyedropper > image {
+  background-image: url("chrome://browser/skin/devtools/command-eyedropper.png");
+}
 
 @media (min-resolution: 2dppx) {
   #command-button-paintflashing > image {
     background-image: url("chrome://browser/skin/devtools/command-paintflashing@2x.png");
   }
 
   #command-button-responsive > image {
     background-image: url("chrome://browser/skin/devtools/command-responsivemode@2x.png");
@@ -600,16 +603,20 @@
 
   #command-button-pick > image {
     background-image: url("chrome://browser/skin/devtools/command-pick@2x.png");
   }
 
   #command-button-splitconsole > image {
     background-image: url("chrome://browser/skin/devtools/command-console@2x.png");
   }
+
+  #command-button-eyedropper > image {
+    background-image: url("chrome://browser/skin/devtools/command-eyedropper@2x.png");
+  }
 }
 
 /* Tabs */
 
 .devtools-tabbar {
   -moz-appearance: none;
   min-height: 28px;
   border: 0px solid;
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -234,16 +234,18 @@ browser.jar:
         skin/classic/browser/devtools/command-scratchpad.png        (../shared/devtools/images/command-scratchpad.png)
         skin/classic/browser/devtools/command-scratchpad@2x.png     (../shared/devtools/images/command-scratchpad@2x.png)
         skin/classic/browser/devtools/command-tilt.png              (../shared/devtools/images/command-tilt.png)
         skin/classic/browser/devtools/command-tilt@2x.png           (../shared/devtools/images/command-tilt@2x.png)
         skin/classic/browser/devtools/command-pick.png              (../shared/devtools/images/command-pick.png)
         skin/classic/browser/devtools/command-pick@2x.png           (../shared/devtools/images/command-pick@2x.png)
         skin/classic/browser/devtools/command-console.png           (../shared/devtools/images/command-console.png)
         skin/classic/browser/devtools/command-console@2x.png        (../shared/devtools/images/command-console@2x.png)
+        skin/classic/browser/devtools/command-eyedropper.png        (../shared/devtools/images/command-eyedropper.png)
+        skin/classic/browser/devtools/command-eyedropper@2x.png     (../shared/devtools/images/command-eyedropper@2x.png)
         skin/classic/browser/devtools/markup-view.css               (../shared/devtools/markup-view.css)
         skin/classic/browser/devtools/editor-error.png              (devtools/editor-error.png)
         skin/classic/browser/devtools/editor-breakpoint.png         (devtools/editor-breakpoint.png)
         skin/classic/browser/devtools/editor-debug-location.png     (devtools/editor-debug-location.png)
 *       skin/classic/browser/devtools/webconsole.css                (devtools/webconsole.css)
         skin/classic/browser/devtools/webconsole_networkpanel.css   (devtools/webconsole_networkpanel.css)
         skin/classic/browser/devtools/webconsole.png                (devtools/webconsole.png)
         skin/classic/browser/devtools/breadcrumbs-divider@2x.png    (../shared/devtools/images/breadcrumbs-divider@2x.png)
@@ -328,17 +330,16 @@ browser.jar:
         skin/classic/browser/devtools/app-manager/error.svg                 (../shared/devtools/app-manager/images/error.svg)
         skin/classic/browser/devtools/app-manager/plus.svg                  (../shared/devtools/app-manager/images/plus.svg)
         skin/classic/browser/devtools/app-manager/remove.svg                (../shared/devtools/app-manager/images/remove.svg)
         skin/classic/browser/devtools/app-manager/add.svg                   (../shared/devtools/app-manager/images/add.svg)
         skin/classic/browser/devtools/app-manager/index-icons.svg           (../shared/devtools/app-manager/images/index-icons.svg)
         skin/classic/browser/devtools/app-manager/rocket.svg                (../shared/devtools/app-manager/images/rocket.svg)
         skin/classic/browser/devtools/app-manager/noise.png                 (../shared/devtools/app-manager/images/noise.png)
         skin/classic/browser/devtools/app-manager/default-app-icon.png      (../shared/devtools/app-manager/images/default-app-icon.png)
-        skin/classic/browser/devtools/eyedropper-black.png                  (../shared/devtools/images/eyedropper-black.png)
 
 #ifdef MOZ_SERVICES_SYNC
         skin/classic/browser/sync-16.png
         skin/classic/browser/sync-32.png
         skin/classic/browser/sync-128.png
         skin/classic/browser/sync-bg.png
         skin/classic/browser/sync-desktopIcon.png
         skin/classic/browser/sync-horizontalbar.png
@@ -594,16 +595,18 @@ browser.jar:
         skin/classic/aero/browser/devtools/command-scratchpad.png    (../shared/devtools/images/command-scratchpad.png)
         skin/classic/aero/browser/devtools/command-scratchpad@2x.png (../shared/devtools/images/command-scratchpad@2x.png)
         skin/classic/aero/browser/devtools/command-tilt.png          (../shared/devtools/images/command-tilt.png)
         skin/classic/aero/browser/devtools/command-tilt@2x.png       (../shared/devtools/images/command-tilt@2x.png)
         skin/classic/aero/browser/devtools/command-pick.png          (../shared/devtools/images/command-pick.png)
         skin/classic/aero/browser/devtools/command-pick@2x.png       (../shared/devtools/images/command-pick@2x.png)
         skin/classic/aero/browser/devtools/command-console.png       (../shared/devtools/images/command-console.png)
         skin/classic/aero/browser/devtools/command-console@2x.png    (../shared/devtools/images/command-console@2x.png)
+        skin/classic/aero/browser/devtools/command-eyedropper.png        (../shared/devtools/images/command-eyedropper.png)
+        skin/classic/aero/browser/devtools/command-eyedropper@2x.png     (../shared/devtools/images/command-eyedropper@2x.png)
         skin/classic/aero/browser/devtools/alerticon-warning.png     (devtools/alerticon-warning.png)
 *       skin/classic/aero/browser/devtools/ruleview.css              (../shared/devtools/ruleview.css)
         skin/classic/aero/browser/devtools/commandline.css           (devtools/commandline.css)
         skin/classic/aero/browser/devtools/markup-view.css           (../shared/devtools/markup-view.css)
         skin/classic/aero/browser/devtools/editor-error.png           (devtools/editor-error.png)
         skin/classic/aero/browser/devtools/editor-breakpoint.png      (devtools/editor-breakpoint.png)
         skin/classic/aero/browser/devtools/editor-debug-location.png  (devtools/editor-debug-location.png)
 *       skin/classic/aero/browser/devtools/webconsole.css                  (devtools/webconsole.css)
@@ -690,18 +693,16 @@ browser.jar:
         skin/classic/aero/browser/devtools/app-manager/error.svg                 (../shared/devtools/app-manager/images/error.svg)
         skin/classic/aero/browser/devtools/app-manager/plus.svg                  (../shared/devtools/app-manager/images/plus.svg)
         skin/classic/aero/browser/devtools/app-manager/remove.svg                (../shared/devtools/app-manager/images/remove.svg)
         skin/classic/aero/browser/devtools/app-manager/add.svg                   (../shared/devtools/app-manager/images/add.svg)
         skin/classic/aero/browser/devtools/app-manager/index-icons.svg           (../shared/devtools/app-manager/images/index-icons.svg)
         skin/classic/aero/browser/devtools/app-manager/rocket.svg                (../shared/devtools/app-manager/images/rocket.svg)
         skin/classic/aero/browser/devtools/app-manager/noise.png                 (../shared/devtools/app-manager/images/noise.png)
         skin/classic/aero/browser/devtools/app-manager/default-app-icon.png      (../shared/devtools/app-manager/images/default-app-icon.png)
-        skin/classic/aero/browser/devtools/eyedropper-black.png        (../shared/devtools/images/eyedropper-black.png)
-
 #ifdef MOZ_SERVICES_SYNC
         skin/classic/aero/browser/sync-16.png
         skin/classic/aero/browser/sync-32.png
         skin/classic/aero/browser/sync-128.png
         skin/classic/aero/browser/sync-bg.png
         skin/classic/aero/browser/sync-desktopIcon.png
         skin/classic/aero/browser/sync-horizontalbar.png
         skin/classic/aero/browser/sync-horizontalbar-XPVista7.png
--- a/content/xul/document/src/XULDocument.cpp
+++ b/content/xul/document/src/XULDocument.cpp
@@ -2480,18 +2480,16 @@ XULDocument::PrepareToWalk()
         if (NS_FAILED(rv)) return rv;
         
         rv = AddElementToRefMap(root);
         if (NS_FAILED(rv)) return rv;
 
         // Block onload until we've finished building the complete
         // document content model.
         BlockOnload();
-
-        nsContentSink::NotifyDocElementCreated(this);
     }
 
     // There'd better not be anything on the context stack at this
     // point! This is the basis case for our "induction" in
     // ResumeWalk(), below, which'll assume that there's always a
     // content element on the context stack if either 1) we're in the
     // "master" document, or 2) we're in an overlay, and we've got
     // more than one prototype element (the single, root "overlay"
--- a/content/xul/document/test/chrome.ini
+++ b/content/xul/document/test/chrome.ini
@@ -1,24 +1,22 @@
 [DEFAULT]
 support-files =
   bug497875-iframe.xul
   overlay1_bug335375.xul
   overlay2_bug335375.xul
   window_bug583948.xul
   window_bug757137.xul
-  window_documentnotification.xul
 
 [test_bug199692.xul]
 [test_bug311681.xul]
 [test_bug335375.xul]
 [test_bug391002.xul]
 [test_bug403868.xul]
 [test_bug414907.xul]
 [test_bug418216.xul]
 [test_bug445177.xul]
 [test_bug449457.xul]
 [test_bug468176.xul]
 [test_bug497875.xul]
 [test_bug583948.xul]
 [test_bug640158_overlay_persist.xul]
 [test_bug757137.xul]
-[test_documentnotification.xul]
deleted file mode 100644
--- a/content/xul/document/test/test_documentnotification.xul
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
-<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
-                 type="text/css"?>
-
-<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-
-<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
-
-<body xmlns="http://www.w3.org/1999/xhtml">
-  <div id="content" style="display: none"/>
-</body>
-
-<script>
-SimpleTest.waitForExplicitFinish();
-
-var seenNotification = false;
-function notify(subject, topic, data) {
-  seenNotification = true;
-  is(topic, "document-element-inserted", "Should be the right notification");
-  is(subject, otherWindow.document, "Should have been notified about the right window");
-  ok(subject.documentElement, "documentElement should be defined");
-}
-
-var obs = Components.classes["@mozilla.org/observer-service;1"].
-          getService(Components.interfaces.nsIObserverService)
-obs.addObserver(notify, "document-element-inserted", false);
-
-var otherWindow = window.open("window_documentnotification.xul", "_new", "chrome");
-otherWindow.addEventListener("load", function() {
-  ok(seenNotification, "Should have seen the document-element-inserted")
-  obs.removeObserver(notify, "document-element-inserted");
-  window.close();
-  SimpleTest.waitForFocus(function() {
-    SimpleTest.finish();
-  });
-});
-</script>
-
-</window>
deleted file mode 100644
--- a/content/xul/document/test/window_documentnotification.xul
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
-
-<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-  <label value="window_documentnotification.xul"/>
-</window>
--- a/dom/bluetooth/bluez/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/bluez/BluetoothHfpManager.cpp
@@ -1373,16 +1373,47 @@ BluetoothHfpManager::GetNumberOfCalls(ui
     if (mCurrentCallArray[i].mState == aState) {
       ++num;
     }
   }
 
   return num;
 }
 
+uint32_t
+BluetoothHfpManager::GetNumberOfConCalls()
+{
+  uint32_t num = 0;
+  uint32_t callLength = mCurrentCallArray.Length();
+
+  for (uint32_t i = 1; i < callLength; ++i) {
+    if (mCurrentCallArray[i].mIsConference) {
+      ++num;
+    }
+  }
+
+  return num;
+}
+
+uint32_t
+BluetoothHfpManager::GetNumberOfConCalls(uint16_t aState)
+{
+  uint32_t num = 0;
+  uint32_t callLength = mCurrentCallArray.Length();
+
+  for (uint32_t i = 1; i < callLength; ++i) {
+    if (mCurrentCallArray[i].mIsConference
+        && mCurrentCallArray[i].mState == aState) {
+      ++num;
+    }
+  }
+
+  return num;
+}
+
 void
 BluetoothHfpManager::HandleCallStateChanged(uint32_t aCallIndex,
                                             uint16_t aCallState,
                                             const nsAString& aError,
                                             const nsAString& aNumber,
                                             const bool aIsOutgoing,
                                             const bool aIsConference,
                                             bool aSend)
@@ -1417,25 +1448,69 @@ BluetoothHfpManager::HandleCallStateChan
   }
   mCurrentCallArray[aCallIndex].mNumber = aNumber;
 
   nsRefPtr<nsRunnable> sendRingTask;
   nsString address;
 
   switch (aCallState) {
     case nsITelephonyProvider::CALL_STATE_HELD:
-      if (prevCallState == nsITelephonyProvider::CALL_STATE_CONNECTED) {
-        if (mCurrentCallArray.Length() == 1) {
-          // A single active call is put on hold (+CIEV, callheld=2)
-          sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_NOACTIVE;
-        } else {
-          // Releases all active calls and accepts the other (+CIEV, callheld=1)
-          sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_ACTIVE;
+      switch (prevCallState) {
+        case nsITelephonyProvider::CALL_STATE_CONNECTED: {
+          uint32_t numActive = GetNumberOfCalls(nsITelephonyProvider::CALL_STATE_CONNECTED);
+          uint32_t numHeld = GetNumberOfCalls(nsITelephonyProvider::CALL_STATE_HELD);
+          uint32_t numConCalls = GetNumberOfConCalls();
+
+          /**
+           * An active call becomes a held call.
+           *
+           * If this call is not a conference call,
+           * - callheld state = ONHOLD_NOACTIVE if no active call remains;
+           * - callheld state = ONHOLD_ACTIVE otherwise.
+           * If this call belongs to a conference call and all other members of
+           * the conference call have become held calls,
+           * - callheld state = ONHOLD_NOACTIVE if no active call remains;
+           * - callheld state = ONHOLD_ACTIVE otherwise.
+           *
+           * Note number of active calls may be 0 in-between state transition
+           * (c1 has become held but c2 has not become active yet), so we regard
+           * no active call remains if there is no other active/held call
+           * besides this changed call/group of conference call.
+           */
+          if (!aIsConference) {
+            if (numActive + numHeld == 1) {
+              // A single active call is put on hold.
+              sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_NOACTIVE;
+            } else {
+              // An active call is placed on hold or active/held calls swapped.
+              sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_ACTIVE;
+            }
+            SendCommand(RESPONSE_CIEV, CINDType::CALLHELD);
+          } else if (GetNumberOfConCalls(nsITelephonyProvider::CALL_STATE_HELD)
+                     == numConCalls) {
+            if (numActive + numHeld == numConCalls) {
+              // An active conference call is put on hold.
+              sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_NOACTIVE;
+            } else {
+              // Active calls are placed on hold or active/held calls swapped.
+              sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_ACTIVE;
+            }
+            SendCommand(RESPONSE_CIEV, CINDType::CALLHELD);
+          }
+          break;
         }
-        SendCommand(RESPONSE_CIEV, CINDType::CALLHELD);
+        case nsITelephonyProvider::CALL_STATE_DISCONNECTED:
+          // The call state changed from DISCONNECTED to HELD. It could happen
+          // when user held a call before Bluetooth got connected.
+          if (FindFirstCall(nsITelephonyProvider::CALL_STATE_CONNECTED)) {
+            // callheld = ONHOLD_ACTIVE if an active call already exists.
+            sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_ACTIVE;
+            SendCommand(RESPONSE_CIEV, CINDType::CALLHELD);
+          }
+          break;
       }
       break;
     case nsITelephonyProvider::CALL_STATE_INCOMING:
       if (FindFirstCall(nsITelephonyProvider::CALL_STATE_CONNECTED)) {
         SendCCWA(aNumber, mCurrentCallArray[aCallIndex].mType);
         UpdateCIND(CINDType::CALLSETUP, CallSetupState::INCOMING, aSend);
       } else {
         // Start sending RING indicator to HF
@@ -1471,40 +1546,51 @@ BluetoothHfpManager::HandleCallStateChan
     case nsITelephonyProvider::CALL_STATE_ALERTING:
       UpdateCIND(CINDType::CALLSETUP, CallSetupState::OUTGOING_ALERTING, aSend);
 
       // If there's an ongoing call when the headset is just connected, we have
       // to open a sco socket here.
       ConnectSco();
       break;
     case nsITelephonyProvider::CALL_STATE_CONNECTED:
+      /**
+       * A call becomes active because:
+       * - user answers an incoming call,
+       * - user dials a outgoing call and it is answered, or
+       * - SLC is connected when a call is active.
+       */
       switch (prevCallState) {
         case nsITelephonyProvider::CALL_STATE_INCOMING:
         case nsITelephonyProvider::CALL_STATE_DISCONNECTED:
           // Incoming call, no break
           sStopSendingRingFlag = true;
           ConnectSco();
           // NO BREAK HERE. continue to next statement
         case nsITelephonyProvider::CALL_STATE_DIALING:
         case nsITelephonyProvider::CALL_STATE_ALERTING:
           // Outgoing call
           UpdateCIND(CINDType::CALL, CallState::IN_PROGRESS, aSend);
           UpdateCIND(CINDType::CALLSETUP, CallSetupState::NO_CALLSETUP, aSend);
+
+          if (FindFirstCall(nsITelephonyProvider::CALL_STATE_HELD)) {
+            // callheld state = ONHOLD_ACTIVE if a held call already exists.
+            UpdateCIND(CINDType::CALLHELD, CallHeldState::ONHOLD_ACTIVE, aSend);
+          }
           break;
-        // User wants to add a held call to the conversation.
-        // The original connected call become a conference call here.
         case nsITelephonyProvider::CALL_STATE_CONNECTED:
+          // User wants to add a held call to the conversation.
+          // The original connected call becomes a conference call here.
           if (aIsConference) {
             UpdateCIND(CINDType::CALLHELD, CallHeldState::NO_CALLHELD, aSend);
           }
           break;
         case nsITelephonyProvider::CALL_STATE_HELD:
           if (!FindFirstCall(nsITelephonyProvider::CALL_STATE_HELD)) {
             if (aIsConference && !prevCallIsConference) {
-              // The held call was merged and become a conference call.
+              // The held call was merged and becomes a conference call.
               UpdateCIND(CINDType::CALLHELD, CallHeldState::NO_CALLHELD, aSend);
             } else if (sCINDItems[CINDType::CALLHELD].value ==
                        CallHeldState::ONHOLD_NOACTIVE) {
               // The held call(s) become connected call(s).
               UpdateCIND(CINDType::CALLHELD, CallHeldState::NO_CALLHELD, aSend);
             }
           }
           break;
--- a/dom/bluetooth/bluez/BluetoothHfpManager.h
+++ b/dom/bluetooth/bluez/BluetoothHfpManager.h
@@ -152,16 +152,18 @@ private:
   void HandleVolumeChanged(const nsAString& aData);
 
   bool Init();
   void Notify(const hal::BatteryInformation& aBatteryInfo);
 #ifdef MOZ_B2G_RIL
   void ResetCallArray();
   uint32_t FindFirstCall(uint16_t aState);
   uint32_t GetNumberOfCalls(uint16_t aState);
+  uint32_t GetNumberOfConCalls();
+  uint32_t GetNumberOfConCalls(uint16_t aState);
   PhoneType GetPhoneType(const nsAString& aType);
 #endif
 
   void NotifyConnectionStatusChanged(const nsAString& aType);
   void NotifyDialer(const nsAString& aCommand);
 
 #ifdef MOZ_B2G_RIL
   void SendCCWA(const nsAString& aNumber, int aType);
--- a/mobile/android/base/CrashReporter.java
+++ b/mobile/android/base/CrashReporter.java
@@ -131,16 +131,17 @@ public class CrashReporter extends Activ
         mExtrasStringMap = new HashMap<String, String>();
         readStringsFromFile(mPendingExtrasFile.getPath(), mExtrasStringMap);
 
         // Set the flag that indicates we were stopped as expected, as
         // we will send a crash report, so it is not a silent OOM crash.
         SharedPreferences prefs = GeckoSharedPrefs.forApp(this);
         SharedPreferences.Editor editor = prefs.edit();
         editor.putBoolean(GeckoApp.PREFS_WAS_STOPPED, true);
+        editor.putBoolean(GeckoApp.PREFS_CRASHED, true);
         editor.commit();
 
         final CheckBox allowContactCheckBox = (CheckBox) findViewById(R.id.allow_contact);
         final CheckBox includeUrlCheckBox = (CheckBox) findViewById(R.id.include_url);
         final CheckBox sendReportCheckBox = (CheckBox) findViewById(R.id.send_report);
         final EditText commentsEditText = (EditText) findViewById(R.id.comment);
         final EditText emailEditText = (EditText) findViewById(R.id.email);
 
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -152,16 +152,17 @@ public abstract class GeckoApp
     public static final String ACTION_WEBAPP_PREFIX        = "org.mozilla.gecko.WEBAPP";
 
     public static final String EXTRA_STATE_BUNDLE          = "stateBundle";
 
     public static final String PREFS_ALLOW_STATE_BUNDLE    = "allowStateBundle";
     public static final String PREFS_OOM_EXCEPTION         = "OOMException";
     public static final String PREFS_VERSION_CODE          = "versionCode";
     public static final String PREFS_WAS_STOPPED           = "wasStopped";
+    public static final String PREFS_CRASHED               = "crashed";
     public static final String PREFS_CLEANUP_TEMP_FILES    = "cleanupTempFiles";
 
     public static final String SAVED_STATE_IN_BACKGROUND   = "inBackground";
     public static final String SAVED_STATE_PRIVATE_SESSION = "privateSession";
 
     static private final String LOCATION_URL = "https://location.services.mozilla.com/v1/submit";
 
     // Delay before running one-time "cleanup" tasks that may be needed
@@ -1767,20 +1768,27 @@ public abstract class GeckoApp
                          .putInt(PREFS_VERSION_CODE, versionCode)
                          .commit();
                 }
             });
 
             shouldRestore = true;
         } else if (savedInstanceState != null ||
                    getSessionRestorePreference().equals("always") ||
-                   getRestartFromIntent() ||
-                   prefs.getBoolean(GeckoApp.PREFS_WAS_STOPPED, false)) {
+                   getRestartFromIntent()) {
             // We're coming back from a background kill by the OS, the user
-            // has chosen to always restore, we restarted, or we crashed.
+            // has chosen to always restore, or we restarted.
+            shouldRestore = true;
+        } else if (prefs.getBoolean(GeckoApp.PREFS_CRASHED, false)) {
+            ThreadUtils.postToBackgroundThread(new Runnable() {
+                @Override
+                public void run() {
+                    prefs.edit().putBoolean(PREFS_CRASHED, false).commit();
+                }
+            });
             shouldRestore = true;
         }
 
         return shouldRestore;
     }
 
     private String getSessionRestorePreference() {
         return getSharedPreferences().getString(GeckoPreferences.PREFS_RESTORE_SESSION, "quit");
--- a/mobile/android/base/TabsAccessor.java
+++ b/mobile/android/base/TabsAccessor.java
@@ -16,16 +16,17 @@ import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
 import android.net.Uri;
 import android.util.Log;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.regex.Pattern;
 
 public final class TabsAccessor {
     private static final String LOGTAG = "GeckoTabsAccessor";
 
     private static final String[] CLIENTS_AVAILABILITY_PROJECTION = new String[] {
                                                                         BrowserContract.Clients.GUID
                                                                     };
 
@@ -44,16 +45,17 @@ public final class TabsAccessor {
         NAME
     };
 
     private static final String CLIENTS_SELECTION = BrowserContract.Clients.GUID + " IS NOT NULL";
     private static final String TABS_SELECTION = BrowserContract.Tabs.CLIENT_GUID + " IS NOT NULL";
 
     private static final String LOCAL_CLIENT_SELECTION = BrowserContract.Clients.GUID + " IS NULL";
     private static final String LOCAL_TABS_SELECTION = BrowserContract.Tabs.CLIENT_GUID + " IS NULL";
+    private static final Pattern FILTERED_URL_PATTERN = Pattern.compile("^(about|chrome|wyciwyg|file):.*$");
 
     public static class RemoteTab {
         public String title;
         public String url;
         public String guid;
         public String name;
     }
 
@@ -144,19 +146,19 @@ public final class TabsAccessor {
      */
     private static void insertLocalTabs(final ContentResolver cr, final Iterable<Tab> tabs) {
         // Reuse this for serializing individual history URLs as JSON.
         JSONArray history = new JSONArray();
         ArrayList<ContentValues> valuesToInsert = new ArrayList<ContentValues>();
 
         int position = 0;
         for (Tab tab : tabs) {
-            // Skip this tab if it has a null URL or is in private browsing mode
+            // Skip this tab if it has a null URL or is in private browsing mode, or is a filtered URL.
             String url = tab.getURL();
-            if (url == null || tab.isPrivate())
+            if (url == null || tab.isPrivate() || isFilteredURL(url))
                 continue;
 
             ContentValues values = new ContentValues();
             values.put(BrowserContract.Tabs.URL, url);
             values.put(BrowserContract.Tabs.TITLE, tab.getTitle());
             values.put(BrowserContract.Tabs.LAST_USED, tab.getLastUsed());
 
             String favicon = tab.getFaviconURL();
@@ -187,9 +189,18 @@ public final class TabsAccessor {
     }
 
     // Deletes all local tabs and replaces them with a new list of tabs.
     public static synchronized void persistLocalTabs(final ContentResolver cr, final Iterable<Tab> tabs) {
         deleteLocalTabs(cr);
         insertLocalTabs(cr, tabs);
         updateLocalClient(cr);
     }
+
+    /**
+     * Matches the supplied URL string against the set of URLs to filter.
+     *
+     * @return true if the supplied URL should be skipped; false otherwise.
+     */
+    private static boolean isFilteredURL(String url) {
+        return FILTERED_URL_PATTERN.matcher(url).matches();
+    }
 }
--- a/mobile/android/base/home/PanelBackItemView.java
+++ b/mobile/android/base/home/PanelBackItemView.java
@@ -24,20 +24,25 @@ class PanelBackItemView extends LinearLa
         super(context);
 
         LayoutInflater.from(context).inflate(R.layout.panel_back_item, this);
         setOrientation(HORIZONTAL);
 
         title = (TextView) findViewById(R.id.title);
 
         final ImageView image = (ImageView) findViewById(R.id.image);
-        Picasso.with(getContext())
-               .load(backImageUrl)
-               .placeholder(R.drawable.folder_up)
-               .into(image);
+
+        if (TextUtils.isEmpty(backImageUrl)) {
+            image.setImageResource(R.drawable.folder_up);
+        } else {
+            Picasso.with(getContext())
+                   .load(backImageUrl)
+                   .placeholder(R.drawable.folder_up)
+                   .into(image);
+        }
     }
 
     public void updateFromFilter(FilterDetail filter) {
         final String backText = getResources()
             .getString(R.string.home_move_up_to_filter, filter.title);
         title.setText(backText);
     }
 }
--- a/mobile/android/base/home/PanelLayout.java
+++ b/mobile/android/base/home/PanelLayout.java
@@ -432,20 +432,18 @@ abstract class PanelLayout extends Frame
                 textView.setText(R.string.home_default_empty);
             } else {
                 textView.setText(text);
             }
 
             final String imageUrl = (emptyViewConfig == null) ? null : emptyViewConfig.getImageUrl();
             final ImageView imageView = (ImageView) view.findViewById(R.id.home_empty_image);
 
-            if (imageUrl == null) {
-                Picasso.with(getContext())
-                       .load(R.drawable.icon_home_empty_firefox)
-                       .into(imageView);
+            if (TextUtils.isEmpty(imageUrl)) {
+                imageView.setImageResource(R.drawable.icon_home_empty_firefox);
             } else {
                 Picasso.with(getContext())
                        .load(imageUrl)
                        .error(R.drawable.icon_home_empty_firefox)
                        .into(imageView);
             }
 
             viewState.setEmptyView(view);
--- a/mobile/android/base/tests/JavascriptTest.java
+++ b/mobile/android/base/tests/JavascriptTest.java
@@ -32,17 +32,18 @@ public class JavascriptTest extends Base
             mActions.expectGeckoEvent(EVENT_TYPE);
         mAsserter.dumpLog("Registered listener for " + EVENT_TYPE);
 
         final String url = getAbsoluteUrl(StringHelper.ROBOCOP_JS_HARNESS_URL +
                                           "?path=" + javascriptUrl);
         mAsserter.dumpLog("Loading JavaScript test from " + url);
         loadUrl(url);
 
-        final JavascriptMessageParser testMessageParser = new JavascriptMessageParser(mAsserter);
+        final JavascriptMessageParser testMessageParser =
+                new JavascriptMessageParser(mAsserter, false);
         try {
             while (!testMessageParser.isTestFinished()) {
                 if (Log.isLoggable(LOGTAG, Log.VERBOSE)) {
                     Log.v(LOGTAG, "Waiting for " + EVENT_TYPE);
                 }
                 String data = expecter.blockForEventData();
                 if (Log.isLoggable(LOGTAG, Log.VERBOSE)) {
                     Log.v(LOGTAG, "Got event with data '" + data + "'");
--- a/mobile/android/base/tests/helpers/JavascriptBridge.java
+++ b/mobile/android/base/tests/helpers/JavascriptBridge.java
@@ -105,18 +105,20 @@ public final class JavascriptBridge {
     /* package */ static void init(final UITestContext context) {
         sActions = context.getActions();
         sAsserter = context.getAsserter();
     }
 
     public JavascriptBridge(final Object target) {
         mTarget = target;
         mMethods = target.getClass().getMethods();
-        mLogParser = new JavascriptMessageParser(sAsserter);
         mExpecter = sActions.expectGeckoEvent(EVENT_TYPE);
+        // The JS here is unrelated to a test harness, so we
+        // have our message parser end on assertion failure.
+        mLogParser = new JavascriptMessageParser(sAsserter, true);
     }
 
     /**
      * Synchronously calls a method in Javascript.
      *
      * @param method Name of the method to call
      * @param args Arguments to pass to the Javascript method; must be a list of
      *             values allowed by JSONObject.
--- a/mobile/android/base/tests/helpers/JavascriptMessageParser.java
+++ b/mobile/android/base/tests/helpers/JavascriptMessageParser.java
@@ -29,19 +29,32 @@ public final class JavascriptMessagePars
     private static final Pattern testMessagePattern =
         Pattern.compile("TEST-([A-Z\\-]+) \\| (.*?) \\| (.*)", Pattern.DOTALL);
 
     private final Assert asserter;
     // Used to help print stack traces neatly.
     private String lastTestName = "";
     // Have we seen a message saying the test is finished?
     private boolean testFinishedMessageSeen = false;
+    private final boolean endOnAssertionFailure;
 
-    public JavascriptMessageParser(final Assert asserter) {
+    /**
+     * Constructs a message parser for test result messages sent from JavaScript. When seeing an
+     * assertion failure, the message parser can use the given {@link org.mozilla.gecko.Assert}
+     * instance to immediately end the test (typically if the underlying JS framework is not able
+     * to end the test itself) or to swallow the Errors - this functionality is determined by the
+     * <code>endOnAssertionFailure</code> parameter.
+     *
+     * @param asserter The Assert instance to which test results should be passed.
+     * @param endOnAssertionFailure
+     *        true if the test should end if we see a JS assertion failure, false otherwise.
+     */
+    public JavascriptMessageParser(final Assert asserter, final boolean endOnAssertionFailure) {
         this.asserter = asserter;
+        this.endOnAssertionFailure = endOnAssertionFailure;
     }
 
     public boolean isTestFinished() {
         return testFinishedMessageSeen;
     }
 
     public void logMessage(final String str) {
         final Matcher m = testMessagePattern.matcher(str.trim());
@@ -56,18 +69,25 @@ public final class JavascriptMessagePars
                 testFinishedMessageSeen = testFinishedMessageSeen ||
                                           "exiting test".equals(message);
             } else if ("PASS".equals(type)) {
                 asserter.ok(true, name, message);
             } else if ("UNEXPECTED-FAIL".equals(type)) {
                 try {
                     asserter.ok(false, name, message);
                 } catch (AssertionFailedError e) {
-                    // Swallow this exception.  We want to see all the
-                    // Javascript failures, not die on the very first one!
+                    // Above, we call the assert, allowing it to log.
+                    // Now we can end the test, if applicable.
+                    if (this.endOnAssertionFailure) {
+                        throw e;
+                    }
+                    // Otherwise, swallow the Error. The JS framework we're
+                    // logging messages from is likely capable of ending tests
+                    // when it needs to, and we want to see all of its failures,
+                    // not just the first one!
                 }
             } else if ("KNOWN-FAIL".equals(type)) {
                 asserter.todo(false, name, message);
             } else if ("UNEXPECTED-PASS".equals(type)) {
                 asserter.todo(true, name, message);
             }
 
             lastTestName = name;
--- a/mobile/android/base/tests/robocop.ini
+++ b/mobile/android/base/tests/robocop.ini
@@ -27,16 +27,17 @@ skip-if = android_version == "10"
 # disabled on 2.3; bug 979603
 skip-if = android_version == "10"
 [testBrowserSearchVisibility]
 [testClearPrivateData]
 # disabled on x86 and 2.3; bug 948591
 skip-if = android_version == "10" || processor == "x86"
 [testDistribution]
 [testDoorHanger]
+[testFilterOpenTab]
 # disabled on 2.3; bug 986172
 skip-if = android_version == "10"
 [testFindInPage]
 # disabled on Android 2.3; bug 975155
 skip-if = android_version == "10"
 [testFlingCorrectness]
 # disabled on x86 only; bug 927476
 skip-if = processor == "x86"
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/tests/testFilterOpenTab.java
@@ -0,0 +1,125 @@
+package org.mozilla.gecko.tests;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+import org.mozilla.gecko.PrivateTab;
+import org.mozilla.gecko.Tab;
+import org.mozilla.gecko.TabsAccessor;
+import org.mozilla.gecko.db.BrowserContract;
+import org.mozilla.gecko.db.TabsProvider;
+
+import android.content.ContentProvider;
+import android.content.Context;
+import android.database.Cursor;
+
+/**
+ * Tests that local tabs are filtered prior to upload.
+ * - create a set of tabs and perists them through TabsAccessor.
+ * - verifies that tabs are filtered by querying.
+ */
+public class testFilterOpenTab extends ContentProviderTest {
+    private static final String[] TABS_PROJECTION_COLUMNS = new String[] {
+                                                                BrowserContract.Tabs.TITLE,
+                                                                BrowserContract.Tabs.URL,
+                                                                BrowserContract.Clients.GUID,
+                                                                BrowserContract.Clients.NAME
+                                                            };
+
+    private static final String LOCAL_TABS_SELECTION = BrowserContract.Tabs.CLIENT_GUID + " IS NULL";
+
+    /**
+     * Factory function that makes new ContentProvider instances.
+     * <p>
+     * We want a fresh provider each test, so this should be invoked in
+     * <code>setUp</code> before each individual test.
+     */
+    protected static Callable<ContentProvider> sTabProviderCallable = new Callable<ContentProvider>() {
+        @Override
+        public ContentProvider call() {
+            return new TabsProvider();
+        }
+    };
+
+    private Cursor getTabsFromLocalClient() throws Exception {
+        return mProvider.query(BrowserContract.Tabs.CONTENT_URI,
+                               TABS_PROJECTION_COLUMNS,
+                               LOCAL_TABS_SELECTION,
+                               null,
+                               null);
+    }
+
+    private Tab createTab(int id, String url, boolean external, int parentId, String title) {
+        return new Tab((Context) getActivity(), id, url, external, parentId, title);
+    }
+
+    private Tab createPrivateTab(int id, String url, boolean external, int parentId, String title) {
+        return new PrivateTab((Context) getActivity(), id, url, external, parentId, title);
+    }
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp(sTabProviderCallable, BrowserContract.TABS_AUTHORITY, "tabs.db");
+        mTests.add(new TestInsertLocalTabs());
+    }
+
+    public void testFilterOpenTab() throws Exception {
+        for (int i = 0; i < mTests.size(); i++) {
+            Runnable test = mTests.get(i);
+
+            setTestName(test.getClass().getSimpleName());
+            test.run();
+        }
+    }
+
+    private class TestInsertLocalTabs extends TestCase  {
+        @Override
+        public void test() throws Exception {
+            final String TITLE1 = "Google";
+            final String URL1 = "http://www.google.com/";
+            final String TITLE2 = "Mozilla Start Page";
+            final String URL2 = "about:home";
+            final String TITLE3 = "Chrome Weave URL";
+            final String URL3 = "chrome://weave/";
+            final String TITLE4 = "What You Cache Is What You Get";
+            final String URL4 = "wyciwyg://1/test.com";
+            final String TITLE5 = "Root Folder";
+            final String URL5 = "file:///";
+
+            // Create a list of local tabs.
+            List<Tab> tabs = new ArrayList<Tab>(6);
+            Tab tab1 = createTab(1, URL1, false, 0, TITLE1);
+            Tab tab2 = createTab(2, URL2, false, 0, TITLE2);
+            Tab tab3 = createTab(3, URL3, false, 0, TITLE3);
+            Tab tab4 = createTab(4, URL4, false, 0, TITLE4);
+            Tab tab5 = createTab(5, URL5, false, 0, TITLE5);
+            Tab tab6 = createPrivateTab(6, URL1, false, 0, TITLE1);
+            tabs.add(tab1);
+            tabs.add(tab2);
+            tabs.add(tab3);
+            tabs.add(tab4);
+            tabs.add(tab5);
+            tabs.add(tab6);
+
+            // Persist the created tabs.
+            TabsAccessor.persistLocalTabs(mResolver, tabs);
+
+            // Get the persisted tab and check if urls are filtered.
+            Cursor c = getTabsFromLocalClient();
+            assertCountIsAndClose(c, 1, 1 + " tabs entries found");
+        }
+    }
+
+    /**
+     * Assert that the provided cursor has the expected number of rows,
+     * closing the cursor afterwards.
+     */
+    private void assertCountIsAndClose(Cursor c, int expectedCount, String message) {
+        try {
+            mAsserter.is(c.getCount(), expectedCount, message);
+        } finally {
+            c.close();
+        }
+    }
+}
--- a/mobile/android/themes/core/about.css
+++ b/mobile/android/themes/core/about.css
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 html {
   background: #f0f0f0;
   padding: 0 1em;
-  font-family: "Nokia Sans", Tahoma, sans-serif !important;
+  font-family: "Clear Sans", sans-serif !important;
   font-size: 100% !important;
 }
 
 body {
   color: black;
   position: relative;
   min-width: 330px;
   max-width: 50em;
--- a/mobile/android/themes/core/aboutBase.css
+++ b/mobile/android/themes/core/aboutBase.css
@@ -1,17 +1,17 @@
 /* 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/. */
 
 %filter substitution
 %include defines.inc
 
 html {
-  font-family: Roboto,"Droid Sans",helvetica,arial,clean,sans-serif;
+  font-family: "Clear Sans",sans-serif;
   font-size: 14px;
   background-color: @color_about_background@;
   -moz-text-size-adjust: none;
 }
 
 body {
   margin: 0;
 }
--- a/mobile/android/themes/core/aboutFeedback.css
+++ b/mobile/android/themes/core/aboutFeedback.css
@@ -121,17 +121,17 @@ section:not([active]) {
 
 #sumo-message {
   color: #444;
   -moz-padding-end: 30px;
 }
 
 .description,
 #last-url {
-  font-family: Roboto,"Droid Sans",helvetica,arial,clean,sans-serif;
+  font-family: "Clear Sans",sans-serif;
   font-size: 14px;
   margin-bottom: 10px;
   padding: 5px;
   width: -moz-calc(100% - 10px);
 }
 
 .send-feedback {
   margin-top: 10px;
--- a/mobile/android/themes/core/aboutPage.css
+++ b/mobile/android/themes/core/aboutPage.css
@@ -1,15 +1,15 @@
 /* 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/. */
 
 body {
   -moz-text-size-adjust: none;
-  font-family: Roboto,"Droid Sans",helvetica,arial,clean,sans-serif;
+  font-family: "Clear Sans",sans-serif;
   font-size: 23px;
   color: #222222;
   background-color: #ced7de;
 }
 
 #header {
   height: 80px;
 }
--- a/mobile/android/themes/core/aboutPrivateBrowsing.css
+++ b/mobile/android/themes/core/aboutPrivateBrowsing.css
@@ -1,14 +1,14 @@
 /* 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/. */
 
 body {
-  font-family: Roboto,"Droid Sans",helvetica,arial,clean,sans-serif;
+  font-family: "Clear Sans",sans-serif;
   font-size: 14px;
 }
 
 body.normal  .showPrivate,
 body.private .showNormal {
   display: none;
 }
 
--- a/mobile/android/themes/core/aboutReader.css
+++ b/mobile/android/themes/core/aboutReader.css
@@ -316,17 +316,17 @@ body {
 }
 
 .font-size5-sample,
 .font-size5 > .content {
   font-size: 22px !important;
 }
 
 .toolbar {
-  font-family: "Droid Sans",helvetica,arial,clean,sans-serif;
+  font-family: "Clear Sans",sans-serif;
   transition-property: visibility, opacity;
   transition-duration: 0.7s;
   visibility: visible;
   opacity: 1.0;
   position: fixed;
   width: 100%;
   bottom: 0px;
   left: 0px;
@@ -460,17 +460,17 @@ body {
   line-height: 50px;
   margin-bottom: 5px;
   border-bottom: 3px solid transparent;
 }
 
 .segmented-button > li > a {
   display: block;
   padding: 5px 0;
-  font-family: "Roboto",sans-serif;
+  font-family: "Clear Sans",sans-serif;
   font-weight: lighter;
 }
 
 #font-type-buttons > li > a:active,
 #font-type-buttons > li.selected > a {
   border-color: #ff9400;
 }
 
--- a/mobile/android/themes/core/config.css
+++ b/mobile/android/themes/core/config.css
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 html,
 body {
     margin: 0;
     padding: 0;
     background-color: #ced7de;
     -moz-user-select: none;
-    font-family: "Open Sans", sans-serif;
+    font-family: "Clear Sans",sans-serif;
     -moz-text-size-adjust: none;
 }
 
 .toolbar {
     width: 100%;
     height: 3em;
     position: fixed;
     top: 0;
--- a/services/healthreport/providers.jsm
+++ b/services/healthreport/providers.jsm
@@ -1217,16 +1217,17 @@ SearchCountMeasurementBase.prototype = O
 
     // Default to a counter.
     return Metrics.Storage.FIELD_DAILY_COUNTER;
   },
 
   SOURCES: [
     "abouthome",
     "contextmenu",
+    "newtab",
     "searchbar",
     "urlbar",
   ],
 });
 
 function SearchCountMeasurement2() {
   SearchCountMeasurementBase.call(this);
 }
--- a/services/healthreport/tests/xpcshell/test_provider_searches.js
+++ b/services/healthreport/tests/xpcshell/test_provider_searches.js
@@ -52,16 +52,17 @@ add_task(function test_record() {
   // Record searches for all but one of our defaults, and one engine that's
   // not a default.
   for (let engine of DEFAULT_ENGINES.concat([{name: "Not Default", identifier: "notdef"}])) {
     if (engine.identifier == "yahoo") {
       continue;
     }
     yield provider.recordSearch(engine, "abouthome");
     yield provider.recordSearch(engine, "contextmenu");
+    yield provider.recordSearch(engine, "newtab");
     yield provider.recordSearch(engine, "searchbar");
     yield provider.recordSearch(engine, "urlbar");
   }
 
   // Invalid sources should throw.
   let errored = false;
   try {
     yield provider.recordSearch(DEFAULT_ENGINES[0], "bad source");
--- a/toolkit/devtools/server/actors/script.js
+++ b/toolkit/devtools/server/actors/script.js
@@ -4776,35 +4776,37 @@ AddonThreadActor.prototype = Object.crea
 update(AddonThreadActor.prototype, {
   constructor: AddonThreadActor,
 
   // A constant prefix that will be used to form the actor ID by the server.
   actorPrefix: "addonThread",
 
   onAttach: function(aRequest) {
     if (!this.attached) {
-      Services.obs.addObserver(this, "document-element-inserted", false);
+      Services.obs.addObserver(this, "chrome-document-global-created", false);
+      Services.obs.addObserver(this, "content-document-global-created", false);
     }
     return ThreadActor.prototype.onAttach.call(this, aRequest);
   },
 
   disconnect: function() {
     if (this.attached) {
-      Services.obs.removeObserver(this, "document-element-inserted");
+      Services.obs.removeObserver(this, "content-document-global-created");
+      Services.obs.removeObserver(this, "chrome-document-global-created");
     }
     return ThreadActor.prototype.disconnect.call(this);
   },
 
   /**
-   * Called when a new DOM document element is created. Check if the DOM was
-   * laoded from an add-on and if so make the window a debuggee.
+   * Called when a new DOM document global is created. Check if the DOM was
+   * loaded from an add-on and if so make the window a debuggee.
    */
   observe: function(aSubject, aTopic, aData) {
     let id = {};
-    if (mapURIToAddonID(aSubject.documentURIObject, id) && id.value === this.addonID) {
+    if (mapURIToAddonID(aSubject.location, id) && id.value === this.addonID) {
       this.dbg.addDebuggee(aSubject.defaultView);
     }
   },
 
   /**
    * Override the eligibility check for scripts and sources to make
    * sure every script and source with a URL is stored when debugging
    * add-ons.