Merge mozilla-central to mozilla-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 07 Sep 2016 17:21:27 +0200
changeset 313026 80dccdd8c94ae0f47a9c037bc56e4c4ed79ebfbb
parent 313025 2356dac937b763c3b4918a0f82d65741c1a28905 (current diff)
parent 312971 95acb9299fafdc69463c49860caf367e4fbcc8e3 (diff)
child 313027 9b1425c4b569f20b3c9a50ac04e3afee4f13f61c
push id20479
push userkwierso@gmail.com
push dateThu, 08 Sep 2016 01:08:46 +0000
treeherderfx-team@fb7c6b034329 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone51.0a1
Merge mozilla-central to mozilla-inbound
docshell/base/nsDocShell.cpp
--- a/.gitignore
+++ b/.gitignore
@@ -114,8 +114,11 @@ testing/talos/bin/
 testing/talos/include/
 testing/talos/lib/
 testing/talos/talos/tests/tp5n.zip
 testing/talos/talos/tests/tp5n
 testing/talos/talos/tests/devtools/damp.manifest.develop
 
 # Ignore files created when running a reftest.
 lextab.py
+
+# tup database
+/.tup
--- a/.hgignore
+++ b/.hgignore
@@ -124,8 +124,11 @@ GPATH
 ^testing/talos/include/
 ^testing/talos/lib/
 ^testing/talos/talos/tests/tp5n.zip
 ^testing/talos/talos/tests/tp5n
 ^testing/talos/talos/tests/devtools/damp.manifest.develop
 
 # Ignore files created when running a reftest.
 ^lextab.py$
+
+# tup database
+^\.tup
--- a/Makefile.in
+++ b/Makefile.in
@@ -166,16 +166,20 @@ install-manifests: $(addprefix install-,
 # config/faster/rules.mk)
 ifneq (,$(filter FasterMake+RecursiveMake,$(BUILD_BACKENDS)))
 install-manifests: faster
 .PHONY: faster
 faster: install-dist/idl
 	$(MAKE) -C faster FASTER_RECURSIVE_MAKE=1
 endif
 
+.PHONY: tup
+tup: install-manifests buildid.h
+	@$(TUP)
+
 # process_install_manifest needs to be invoked with --no-remove when building
 # js as standalone because automated builds are building nspr separately and
 # that would remove the resulting files.
 # Eventually, a standalone js build would just be able to build nspr itself,
 # removing the need for the former.
 ifdef JS_STANDALONE
 NO_REMOVE=1
 endif
--- a/b2g/dev/config/tooltool-manifests/linux64/hazard.manifest
+++ b/b2g/dev/config/tooltool-manifests/linux64/hazard.manifest
@@ -19,21 +19,22 @@
 "algorithm" : "sha512",
 "filename" : "gtk3.tar.xz",
 "setup" : "setup.sh",
 "unpack" : true,
 "digest" : "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
 "size" : 12072532
 },
 {
-"size" : 89319524,
-"digest" : "5383d843c9f28abf0a6d254e9d975d96972d2c86d627ca836fa8e272a5d53230603b387d7d1499c49df7f84b1bb946946e800a85c88d968bdbe81c755fcb02e1",
-"algorithm" : "sha512",
-"filename" : "rustc.tar.xz",
-"unpack" : true
+"version": "rustc 1.11.0 (9b21dcd6a 2016-08-15) repack",
+"size": 131489924,
+"digest": "59f7463a0da38f324daa4ffc2678d78afb4fe0df13248c1d215bcb996ec05e8521155563cde9a8b719a9b98c5feeaf97cc9e8d52c9b95f6b44728870d908d5b6",
+"algorithm": "sha512",
+"filename": "rustc.tar.xz",
+"unpack": true
 },
 {
 "algorithm" : "sha512",
 "filename" : "sccache.tar.bz2",
 "unpack" : true,
 "digest" : "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "size" : 167175
 },
--- a/b2g/dev/config/tooltool-manifests/linux64/releng.manifest
+++ b/b2g/dev/config/tooltool-manifests/linux64/releng.manifest
@@ -11,19 +11,19 @@
 "size": 12072532,
 "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
 "algorithm": "sha512",
 "filename": "gtk3.tar.xz",
 "setup": "setup.sh",
 "unpack": true
 },
 {
-"version": "gecko rustc 1.10.0 (cfcb716cf 2016-07-03)",
-"size": 102276708,
-"digest": "8cc9ea8347fc7e6e6fdb15a8fd1faae977f1235a426b879b3f9128ec91d8f2b6268297ce80bf4eceb47738bd40bfeda13f143dc3fe85f1434b13adfbc095ab90",
+"version": "rustc 1.11.0 (9b21dcd6a 2016-08-15) repack",
+"size": 131489924,
+"digest": "59f7463a0da38f324daa4ffc2678d78afb4fe0df13248c1d215bcb996ec05e8521155563cde9a8b719a9b98c5feeaf97cc9e8d52c9b95f6b44728870d908d5b6",
 "algorithm": "sha512",
 "filename": "rustc.tar.xz",
 "unpack": true
 },
 {
 "version": "cargo 0.13.0-nightly (e713e7f 2016-08-31)",
 "size": 3245716,
 "digest": "d5bb0d88ce7bb1b5a316d7a8ca6341672f5ee8008fa7754511bf53fabd54c0770e95397232896d6087547891f1143f6968d8b1e106e39800b43defeb0025c7c0",
--- a/browser/config/tooltool-manifests/linux32/releng.manifest
+++ b/browser/config/tooltool-manifests/linux32/releng.manifest
@@ -11,19 +11,19 @@
 "size": 11189216,
 "digest": "18bc52b0599b1308b667e282abb45f47597bfc98a5140cfcab8da71dacf89dd76d0dee22a04ce26fe7ad1f04e2d6596991f9e5b01fd2aaaab5542965f596b0e6",
 "algorithm": "sha512",
 "filename": "gtk3.tar.xz",
 "setup": "setup.sh",
 "unpack": true
 },
 {
-"version": "gecko rustc 1.10.0 (cfcb716cf 2016-07-03)",
-"size": 102276708,
-"digest": "8cc9ea8347fc7e6e6fdb15a8fd1faae977f1235a426b879b3f9128ec91d8f2b6268297ce80bf4eceb47738bd40bfeda13f143dc3fe85f1434b13adfbc095ab90",
+"version": "rustc 1.11.0 (9b21dcd6a 2016-08-15) repack",
+"size": 131489924,
+"digest": "59f7463a0da38f324daa4ffc2678d78afb4fe0df13248c1d215bcb996ec05e8521155563cde9a8b719a9b98c5feeaf97cc9e8d52c9b95f6b44728870d908d5b6",
 "algorithm": "sha512",
 "filename": "rustc.tar.xz",
 "unpack": true
 },
 {
 "version": "cargo 0.13.0-nightly (e713e7f 2016-08-31)",
 "size": 3245716,
 "digest": "d5bb0d88ce7bb1b5a316d7a8ca6341672f5ee8008fa7754511bf53fabd54c0770e95397232896d6087547891f1143f6968d8b1e106e39800b43defeb0025c7c0",
--- a/browser/config/tooltool-manifests/linux64/hazard.manifest
+++ b/browser/config/tooltool-manifests/linux64/hazard.manifest
@@ -19,21 +19,22 @@
 "digest" : "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
 "unpack" : true,
 "setup" : "setup.sh",
 "algorithm" : "sha512",
 "filename" : "gtk3.tar.xz",
 "size" : 12072532
 },
 {
-"unpack" : true,
-"digest" : "5383d843c9f28abf0a6d254e9d975d96972d2c86d627ca836fa8e272a5d53230603b387d7d1499c49df7f84b1bb946946e800a85c88d968bdbe81c755fcb02e1",
-"filename" : "rustc.tar.xz",
-"algorithm" : "sha512",
-"size" : 89319524
+"version": "rustc 1.11.0 (9b21dcd6a 2016-08-15) repack",
+"size": 131489924,
+"digest": "59f7463a0da38f324daa4ffc2678d78afb4fe0df13248c1d215bcb996ec05e8521155563cde9a8b719a9b98c5feeaf97cc9e8d52c9b95f6b44728870d908d5b6",
+"algorithm": "sha512",
+"filename": "rustc.tar.xz",
+"unpack": true
 },
 {
 "filename" : "sccache.tar.bz2",
 "algorithm" : "sha512",
 "digest" : "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "unpack" : true,
 "size" : 167175
 }
--- a/browser/config/tooltool-manifests/linux64/releng.manifest
+++ b/browser/config/tooltool-manifests/linux64/releng.manifest
@@ -11,19 +11,19 @@
 "size": 12072532,
 "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
 "algorithm": "sha512",
 "filename": "gtk3.tar.xz",
 "setup": "setup.sh",
 "unpack": true
 },
 {
-"version": "gecko rustc 1.10.0 (cfcb716cf 2016-07-03)",
-"size": 102276708,
-"digest": "8cc9ea8347fc7e6e6fdb15a8fd1faae977f1235a426b879b3f9128ec91d8f2b6268297ce80bf4eceb47738bd40bfeda13f143dc3fe85f1434b13adfbc095ab90",
+"version": "rustc 1.11.0 (9b21dcd6a 2016-08-15) repack",
+"size": 131489924,
+"digest": "59f7463a0da38f324daa4ffc2678d78afb4fe0df13248c1d215bcb996ec05e8521155563cde9a8b719a9b98c5feeaf97cc9e8d52c9b95f6b44728870d908d5b6",
 "algorithm": "sha512",
 "filename": "rustc.tar.xz",
 "unpack": true
 },
 {
 "version": "cargo 0.13.0-nightly (e713e7f 2016-08-31)",
 "size": 3245716,
 "digest": "d5bb0d88ce7bb1b5a316d7a8ca6341672f5ee8008fa7754511bf53fabd54c0770e95397232896d6087547891f1143f6968d8b1e106e39800b43defeb0025c7c0",
--- a/browser/config/tooltool-manifests/macosx64/cross-releng.manifest
+++ b/browser/config/tooltool-manifests/macosx64/cross-releng.manifest
@@ -50,24 +50,16 @@
 "size": 188880, 
 "visibility": "public", 
 "digest": "1ffddd43efb03aed897ee42035d9d8d758a8d66ab6c867599ef755e1a586768fc22011ce03698af61454920b00fe8bed08c9a681e7bd324d7f8f78c026c83943", 
 "algorithm": "sha512", 
 "unpack": true,
 "filename": "genisoimage.tar.xz"
 },
 {
-"version": "gecko rustc 1.10.0 (cfcb716cf 2016-07-03)",
-"size": 102276708,
-"digest": "8cc9ea8347fc7e6e6fdb15a8fd1faae977f1235a426b879b3f9128ec91d8f2b6268297ce80bf4eceb47738bd40bfeda13f143dc3fe85f1434b13adfbc095ab90",
+"version": "rustc 1.11.0 (9b21dcd6a 2016-08-15) repack",
+"size": 171059204,
+"digest": "7554ac993f55818827c80dab90135209e57db70c7c9131bef4309aff3b8d7452c4c0de663df7e8c46bd5702455c36292ade6c7a8007e567c4588c7f91aa88b57",
 "algorithm": "sha512",
 "filename": "rustc.tar.xz",
 "unpack": true
-},
-{
-"size": 53754194,
-"visibility": "public",
-"digest": "d861a8c857e5cbc98318951b009c82cd88cbf151f1f5ad83d140e5e662b92c1ae1250c71c5d4cd29ebbcb4efa360a2ed4a6eb4db6281ecfb313038b5487eb1d2",
-"algorithm": "sha512",
-"filename": "rust-std-lib-x86_64-apple-darwin.tar.bz2",
-"unpack": true
 }
 ]
--- a/browser/config/tooltool-manifests/macosx64/releng.manifest
+++ b/browser/config/tooltool-manifests/macosx64/releng.manifest
@@ -3,19 +3,19 @@
 "version": "clang 3.8.0",
 "size": 133060926,
 "digest": "aff5ad3ac2d41db19d1ba0df5f97b189a7d7e1b6af8c56e22c2b0cced84d75fa98394ded6a4ba5713652e6684a0a46f47aeccf87991f9e849bf8d7d82e564f6f",
 "algorithm": "sha512",
 "filename": "clang.tar.bz2",
 "unpack": true
 },
 {
-"version": "rust 1.10 repack",
-"size": 150726627,
-"digest": "a30476113212895a837b2c4c18eb34d767c7192c3e327fba84c0138eaf7e671e84d5294e75370af3fe7e527a61e0938cd6cce20fba0aec94537070eb0094e27e",
+"version": "rustc 1.11.0 (9b21dcd6a 2016-08-15) repack",
+"size": 146060042,
+"digest": "c7c5556af0dea1f97a737e4634496d407a5e0f7d14a7013746ad41ef188bab03be60cea59ed63d733dcb03bf11b05d8bf637dc0261f15cd5b0ab46d1199243cf",
 "algorithm": "sha512",
 "filename": "rustc.tar.bz2",
 "unpack": true
 },
 {
 "version": "cargo 0.13.0-nightly (e713e7f 2016-08-31)",
 "size": 2715131,
 "digest": "f037d2bbbeccb2c95519e083d6d9eecb5cb06a510e849b5721d6933a6c2428203b93ed3d20d3f20329f4d4eee17177d762f051b1ae79fee97d93b84611f3df66",
--- a/browser/config/tooltool-manifests/win32/clang.manifest
+++ b/browser/config/tooltool-manifests/win32/clang.manifest
@@ -1,20 +1,21 @@
 [
 {
 "size": 266240,
 "digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
 "algorithm": "sha512",
 "filename": "mozmake.exe"
 },
 {
-"size": 78886322,
-"digest": "9c2c40637de27a0852aa1166f2a08159908b23f7a55855c933087c541461bbb2a1ec9e0522df0d2b9da2b2c343b673dbb5a2fa8d30216fe8acee1eb1383336ea",
+"version": "rustc 1.11.0 (9b21dcd6a 2016-08-15) repack",
+"size": 86199150,
+"digest": "fec209dc85a098817c892655fbfda2bd6961199b1c28422994a50daddcb219608673b87dde30b3380555400cf4484863a12d431a6a25ef01cb9b1b32bef48f8b",
 "algorithm": "sha512",
-"filename": "rustc-beta-i686-pc-windows-msvc.tar.bz2",
+"filename": "rustc.tar.bz2",
 "unpack": true
 },
 {
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
 "filename": "sccache.tar.bz2",
 "unpack": true
--- a/browser/config/tooltool-manifests/win32/releng.manifest
+++ b/browser/config/tooltool-manifests/win32/releng.manifest
@@ -1,19 +1,19 @@
 [
 {
 "size": 266240,
 "digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
 "algorithm": "sha512",
 "filename": "mozmake.exe"
 },
 {
-"version": "rustc 1.10.0 (cfcb716cf 2016-07-03)",
-"size": 88820579,
-"digest": "3bc772d951bf90b01cdba9dcd0e1d131a98519dff0710bb219784ea43d4d001dbce191071a4b3824933386bb9613f173760c438939eb396b0e0dfdad9a42e4f0",
+"version": "rustc 1.11.0 (9b21dcd6a 2016-08-15) repack",
+"size": 86199150,
+"digest": "fec209dc85a098817c892655fbfda2bd6961199b1c28422994a50daddcb219608673b87dde30b3380555400cf4484863a12d431a6a25ef01cb9b1b32bef48f8b",
 "algorithm": "sha512",
 "filename": "rustc.tar.bz2",
 "unpack": true
 },
 {
 "version": "cargo 0.13.0-nightly (e713e7f 2016-08-31)",
 "size": 2402000,
 "digest": "56f12f7ac437742ed717ce0ccfb0b4134160948e45d73016e48d9033567e5b01a171ac95dd7965eb007702c31da73274b5913281655f461f611ddeee37181ecc",
--- a/browser/config/tooltool-manifests/win64/clang.manifest
+++ b/browser/config/tooltool-manifests/win64/clang.manifest
@@ -1,18 +1,19 @@
 [
 {
 "size": 266240,
 "digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
 "algorithm": "sha512",
 "filename": "mozmake.exe"
 },
 {
-"size": 80157273,
-"digest": "c4704dcc6774b9f3baaa9313192d26e36bfba2d4380d0518ee7cb89153d9adfe63f228f0ac29848f02948eb1ab7e6624ba71210f0121196d2b54ecebd640d1e6",
+"version": "rustc 1.11.0 (9b21dcd6a 2016-08-15) repack",
+"size": 91329933,
+"digest": "db97f0186db432c57698e287798940abb5946c8903f990b087ea977fb938e83f2f9ca1bf90377bc575563af3144d429cc897a36750a1978a288a42b132c3d25d",
 "algorithm": "sha512",
 "visibility": "public",
 "filename": "rustc.tar.bz2",
 "unpack": true
 },
 {
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
--- a/browser/config/tooltool-manifests/win64/releng.manifest
+++ b/browser/config/tooltool-manifests/win64/releng.manifest
@@ -1,19 +1,19 @@
 [
 {
 "size": 266240,
 "digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
 "algorithm": "sha512",
 "filename": "mozmake.exe"
 },
 {
-"version": "rustc 1.10.0 (cfcb716cf 2016-07-03)",
-"size": 94067220,
-"digest": "05cabda2a28ce6674f062aab589b4b3758e0cd4a4af364bb9a2e736254baa10d668936b2b7ed0df530c7f5ba8ea1e7f51ff3affc84a6551c46188b2f67f10e05",
+"version": "rustc 1.11.0 (9b21dcd6a 2016-08-15) repack",
+"size": 91329933,
+"digest": "db97f0186db432c57698e287798940abb5946c8903f990b087ea977fb938e83f2f9ca1bf90377bc575563af3144d429cc897a36750a1978a288a42b132c3d25d",
 "algorithm": "sha512",
 "visibility": "public",
 "filename": "rustc.tar.bz2",
 "unpack": true
 },
 {
 "version": "cargo 0.13.0-nightly (e713e7f 2016-08-31)",
 "size": 2677831,
--- a/build/autoconf/android.m4
+++ b/build/autoconf/android.m4
@@ -318,16 +318,17 @@ case "$target" in
     AC_SUBST(ANDROID_BUILD_TOOLS_VERSION)
 
     MOZ_ANDROID_AAR(customtabs, $ANDROID_SUPPORT_LIBRARY_VERSION, android, com/android/support)
     MOZ_ANDROID_AAR(appcompat-v7, $ANDROID_SUPPORT_LIBRARY_VERSION, android, com/android/support)
     MOZ_ANDROID_AAR(cardview-v7, $ANDROID_SUPPORT_LIBRARY_VERSION, android, com/android/support)
     MOZ_ANDROID_AAR(design, $ANDROID_SUPPORT_LIBRARY_VERSION, android, com/android/support)
     MOZ_ANDROID_AAR(recyclerview-v7, $ANDROID_SUPPORT_LIBRARY_VERSION, android, com/android/support)
     MOZ_ANDROID_AAR(support-v4, $ANDROID_SUPPORT_LIBRARY_VERSION, android, com/android/support, REQUIRED_INTERNAL_IMPL)
+    MOZ_ANDROID_AAR(palette-v7, $ANDROID_SUPPORT_LIBRARY_VERSION, android, com/android/support)
 
     ANDROID_SUPPORT_ANNOTATIONS_JAR="$ANDROID_SDK_ROOT/extras/android/m2repository/com/android/support/support-annotations/$ANDROID_SUPPORT_LIBRARY_VERSION/support-annotations-$ANDROID_SUPPORT_LIBRARY_VERSION.jar"
     AC_MSG_CHECKING([for support-annotations JAR])
     if ! test -e $ANDROID_SUPPORT_ANNOTATIONS_JAR ; then
         AC_MSG_ERROR([You must download the support-annotations lib.  Run the Android SDK tool and install the Android Support Repository under Extras.  See https://developer.android.com/tools/extras/support-library.html for more info. (looked for $ANDROID_SUPPORT_ANNOTATIONS_JAR)])
     fi
     AC_MSG_RESULT([$ANDROID_SUPPORT_ANNOTATIONS_JAR])
     AC_SUBST(ANDROID_SUPPORT_ANNOTATIONS_JAR)
--- a/devtools/client/debugger/new/index.html
+++ b/devtools/client/debugger/new/index.html
@@ -3,11 +3,14 @@
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 <!DOCTYPE html>
 <html>
   <head>
     <link rel="stylesheet" type="text/css" href="resource://devtools/client/debugger/new/styles.css" />
   </head>
   <body>
     <div id="mount"></div>
+    <script type="text/javascript">
+      var devtoolsRequire = Components.utils.import("resource://devtools/shared/Loader.jsm", {}).require;
+    </script>
     <script type="text/javascript" src="resource://devtools/client/debugger/new/bundle.js"></script>
   </body>
 </html>
--- a/devtools/client/framework/toolbox-process-window.js
+++ b/devtools/client/framework/toolbox-process-window.js
@@ -65,16 +65,17 @@ function setPrefDefaults() {
   Services.prefs.setBoolPref("devtools.inspector.showUserAgentStyles", true);
   Services.prefs.setBoolPref("devtools.performance.ui.show-platform-data", true);
   Services.prefs.setBoolPref("devtools.inspector.showAllAnonymousContent", true);
   Services.prefs.setBoolPref("browser.dom.window.dump.enabled", true);
   Services.prefs.setBoolPref("devtools.command-button-noautohide.enabled", true);
   Services.prefs.setBoolPref("devtools.scratchpad.enabled", true);
   // Bug 1225160 - Using source maps with browser debugging can lead to a crash
   Services.prefs.setBoolPref("devtools.debugger.source-maps-enabled", false);
+  Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
 }
 
 window.addEventListener("load", function() {
   let cmdClose = document.getElementById("toolbox-cmd-close");
   cmdClose.addEventListener("command", onCloseCommand);
   setPrefDefaults();
   connect().catch(e => {
     let errorMessageContainer = document.getElementById("error-message-container");
--- a/devtools/client/inspector/test/browser_inspector_highlighter-eyedropper-events.js
+++ b/devtools/client/inspector/test/browser_inspector_highlighter-eyedropper-events.js
@@ -2,48 +2,118 @@
  * 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";
 
 // Test the eyedropper mouse and keyboard handling.
 
 const HIGHLIGHTER_TYPE = "EyeDropper";
 const ID = "eye-dropper-";
+const TEST_URI = `
+<style>
+  html{width:100%;height:100%;}
+</style>
+<body>eye-dropper test</body>`;
 
 const MOVE_EVENTS_DATA = [
   {type: "mouse", x: 200, y: 100, expected: {x: 200, y: 100}},
   {type: "mouse", x: 100, y: 200, expected: {x: 100, y: 200}},
   {type: "keyboard", key: "VK_LEFT", expected: {x: 99, y: 200}},
   {type: "keyboard", key: "VK_LEFT", shift: true, expected: {x: 89, y: 200}},
   {type: "keyboard", key: "VK_RIGHT", expected: {x: 90, y: 200}},
   {type: "keyboard", key: "VK_RIGHT", shift: true, expected: {x: 100, y: 200}},
   {type: "keyboard", key: "VK_DOWN", expected: {x: 100, y: 201}},
   {type: "keyboard", key: "VK_DOWN", shift: true, expected: {x: 100, y: 211}},
   {type: "keyboard", key: "VK_UP", expected: {x: 100, y: 210}},
   {type: "keyboard", key: "VK_UP", shift: true, expected: {x: 100, y: 200}},
+  // Mouse initialization for left and top snapping
+  {type: "mouse", x: 7, y: 7, expected: {x: 7, y: 7}},
+  // Left Snapping
+  {type: "keyboard", key: "VK_LEFT", shift: true, expected: {x: 0, y: 7},
+   desc: "Left Snapping to x=0"},
+  // Top Snapping
+  {type: "keyboard", key: "VK_UP", shift: true, expected: {x: 0, y: 0},
+   desc: "Top Snapping to y=0"},
+  // Mouse initialization for right snapping
+  {
+    type: "mouse",
+    x: (width, height) => width - 5,
+    y: 0,
+    expected: {
+      x: (width, height) => width - 5,
+      y: 0
+    }
+  },
+  // Right snapping
+  {
+    type: "keyboard",
+    key: "VK_RIGHT",
+    shift: true,
+    expected: {
+      x: (width, height) => width,
+      y: 0
+    },
+    desc: "Right snapping to x=max window width available"
+  },
+  // Mouse initialization for bottom snapping
+  {
+    type: "mouse",
+    x: 0,
+    y: (width, height) => height - 5,
+    expected: {
+      x: 0,
+      y: (width, height) => height - 5
+    }
+  },
+  // Bottom snapping
+  {
+    type: "keyboard",
+    key: "VK_DOWN",
+    shift: true,
+    expected: {
+      x: 0,
+      y: (width, height) => height
+    },
+    desc: "Bottom snapping to y=max window height available"
+  },
 ];
 
 add_task(function* () {
-  let helper = yield openInspectorForURL("data:text/html;charset=utf-8,eye-dropper test")
-               .then(getHighlighterHelperFor(HIGHLIGHTER_TYPE));
+  let {inspector, testActor} = yield openInspectorForURL(
+    "data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let helper = yield getHighlighterHelperFor(HIGHLIGHTER_TYPE)({inspector, testActor});
+
   helper.prefix = ID;
 
   yield helper.show("html");
-  yield respondsToMoveEvents(helper);
+  yield respondsToMoveEvents(helper, testActor);
   yield respondsToReturnAndEscape(helper);
 
   helper.finalize();
 });
 
-function* respondsToMoveEvents(helper) {
+function* respondsToMoveEvents(helper, testActor) {
   info("Checking that the eyedropper responds to events from the mouse and keyboard");
   let {mouse} = helper;
+  let {width, height} = yield testActor.getBoundingClientRect("html");
 
-  for (let {type, x, y, key, shift, expected} of MOVE_EVENTS_DATA) {
-    info(`Simulating a ${type} event to move to ${expected.x} ${expected.y}`);
+  for (let {type, x, y, key, shift, expected, desc} of MOVE_EVENTS_DATA) {
+    x = typeof x === "function" ? x(width, height) : x;
+    y = typeof y === "function" ? y(width, height) : y;
+    expected.x = typeof expected.x === "function" ?
+      expected.x(width, height) : expected.x;
+    expected.y = typeof expected.y === "function" ?
+      expected.y(width, height) : expected.y;
+
+    if (typeof desc === "undefined") {
+      info(`Simulating a ${type} event to move to ${expected.x} ${expected.y}`);
+    } else {
+      info(`Simulating ${type} event: ${desc}`);
+    }
+
     if (type === "mouse") {
       yield mouse.move(x, y);
     } else if (type === "keyboard") {
       let options = shift ? {shiftKey: true} : {};
       yield EventUtils.synthesizeKey(key, options);
     }
     yield checkPosition(expected, helper);
   }
--- a/devtools/client/themes/rules.css
+++ b/devtools/client/themes/rules.css
@@ -320,16 +320,21 @@
   visibility: hidden;
 }
 
 .ruleview-expander {
   vertical-align: middle;
   display: inline-block;
 }
 
+.ruleview-expander.theme-twisty:-moz-locale-dir(rtl) {
+  /* for preventing .theme-twisty's wrong direction in rtl; Bug 1296648 */
+  transform: none;
+}
+
 .ruleview-newproperty {
   /* (enable checkbox width: 12px) + (expander width: 15px) */
   margin-inline-start: 27px;
 }
 
 .ruleview-namecontainer,
 .ruleview-propertyvaluecontainer,
 .ruleview-propertyname,
--- a/devtools/client/webconsole/test/browser_webconsole_output_06.js
+++ b/devtools/client/webconsole/test/browser_webconsole_output_06.js
@@ -106,38 +106,40 @@ var inputTests = [
     variablesViewLabel: "Array[6]"
   },
 
   // 12 - array with long strings as elements
   {
     input: '["' + testStrIn + '", "' + testStrIn + '", "' + testStrIn + '"]',
     output: 'Array [ "' + testStrOut + '", "' + testStrOut + '", "' +
             testStrOut + '" ]',
-    inspectable: false,
+    inspectable: true,
     printOutput: "SHOW\nALL\nOF\nTHIS\nON\nA\nSINGLE\nLINE ONLY. ESCAPE " +
                  "ALL NEWLINE,SHOW\nALL\nOF\nTHIS\nON\nA\nSINGLE\nLINE ONLY. " +
                  "ESCAPE ALL NEWLINE,SHOW\nALL\nOF\nTHIS\nON\nA\nSINGLE\n" +
                  "LINE ONLY. ESCAPE ALL NEWLINE",
     variablesViewLabel: "Array[3]"
   },
 
   // 13
   {
     input: '({0: "a", 1: "b"})',
     output: 'Object [ "a", "b" ]',
     printOutput: "[object Object]",
-    inspectable: false,
+    inspectable: true,
+    variablesViewLabel: "Object[2]",
   },
 
   // 14
   {
     input: '({0: "a", 42: "b"})',
     output: 'Object { 0: "a", 42: "b" }',
     printOutput: "[object Object]",
-    inspectable: false,
+    inspectable: true,
+    variablesViewLabel: "Object",
   },
 
   // 15
   {
     input: '({0: "a", 1: "b", 2: "c", 3: "d", 4: "e", 5: "f", 6: "g", ' +
            '7: "h", 8: "i", 9: "j", 10: "k", 11: "l"})',
     output: 'Object [ "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", ' +
             "2 more\u2026 ]",
@@ -160,16 +162,106 @@ var inputTests = [
   // 17
   {
     input: '({" ": "a"})',
     output: 'Object {  : "a" }',
     printOutput: "[object Object]",
     inspectable: true,
     variablesViewLabel: "Object",
   },
+
+  // 18
+  {
+    input: '({})',
+    output: 'Object {  }',
+    printOutput: "[object Object]",
+    inspectable: true,
+    variablesViewLabel: "Object",
+  },
+
+  // 19
+  {
+    input: '({length: 0})',
+    output: 'Object [  ]',
+    printOutput: "[object Object]",
+    inspectable: true,
+    variablesViewLabel: "Object[0]",
+  },
+
+  // 20
+  {
+    input: '({length: 1})',
+    output: 'Object { length: 1 }',
+    printOutput: "[object Object]",
+    inspectable: true,
+    variablesViewLabel: "Object",
+  },
+
+  // 21
+  {
+    input: '({0: "a", 1: "b", length: 1})',
+    output: 'Object { 1: "b", length: 1, 1 more\u2026 }',
+    printOutput: "[object Object]",
+    inspectable: true,
+    variablesViewLabel: "Object",
+  },
+
+  // 22
+  {
+    input: '({0: "a", 1: "b", length: 2})',
+    output: 'Object [ "a", "b" ]',
+    printOutput: "[object Object]",
+    inspectable: true,
+    variablesViewLabel: "Object[2]",
+  },
+
+  // 23
+  {
+    input: '({0: "a", 1: "b", length: 3})',
+    output: 'Object { length: 3, 2 more\u2026 }',
+    printOutput: "[object Object]",
+    inspectable: true,
+    variablesViewLabel: "Object",
+  },
+
+  // 24
+  {
+    input: '({0: "a", 2: "b", length: 2})',
+    output: 'Object { 2: "b", length: 2, 1 more\u2026 }',
+    printOutput: "[object Object]",
+    inspectable: true,
+    variablesViewLabel: "Object",
+  },
+
+  // 25
+  {
+    input: '({0: "a", 2: "b", length: 3})',
+    output: 'Object { length: 3, 2 more\u2026 }',
+    printOutput: "[object Object]",
+    inspectable: true,
+    variablesViewLabel: "Object",
+  },
+
+  // 26
+  {
+    input: '({0: "a", b: "b", length: 1})',
+    output: 'Object { b: "b", length: 1, 1 more\u2026 }',
+    printOutput: "[object Object]",
+    inspectable: true,
+    variablesViewLabel: "Object",
+  },
+
+  // 27
+  {
+    input: '({0: "a", b: "b", length: 2})',
+    output: 'Object { b: "b", length: 2, 1 more\u2026 }',
+    printOutput: "[object Object]",
+    inspectable: true,
+    variablesViewLabel: "Object",
+  },
 ];
 
 function test() {
   requestLongerTimeout(2);
   Task.spawn(function* () {
     let {tab} = yield loadTab(TEST_URI);
     let hud = yield openConsole(tab);
     return checkOutputForInputs(hud, inputTests);
--- a/devtools/server/actors/highlighters/eye-dropper.js
+++ b/devtools/server/actors/highlighters/eye-dropper.js
@@ -424,18 +424,20 @@ EyeDropper.prototype = {
     if (e.shiftKey) {
       modifier = 10;
     }
 
     offsetY *= modifier;
     offsetX *= modifier;
 
     if (offsetX !== 0 || offsetY !== 0) {
-      this.magnifiedArea.x += offsetX;
-      this.magnifiedArea.y += offsetY;
+      this.magnifiedArea.x = cap(this.magnifiedArea.x + offsetX,
+                                 0, this.win.innerWidth * this.pageZoom);
+      this.magnifiedArea.y = cap(this.magnifiedArea.y + offsetY, 0,
+                                 this.win.innerHeight * this.pageZoom);
 
       this.draw();
 
       this.moveTo(this.magnifiedArea.x / this.pageZoom,
                   this.magnifiedArea.y / this.pageZoom);
 
       e.preventDefault();
     }
@@ -521,8 +523,12 @@ function toColorString(rgb, format) {
  * Produce a hex-formatted color string from rgb values.
  * @param {array} rgb Rgb values of color to stringify.
  * @return {string} Hex formatted string for color, e.g. "#FFEE00".
  */
 function hexString([r, g, b]) {
   let val = (1 << 24) + (r << 16) + (g << 8) + (b << 0);
   return "#" + val.toString(16).substr(-6).toUpperCase();
 }
+
+function cap(value, min, max) {
+  return Math.max(min, Math.min(value, max));
+}
--- a/devtools/server/actors/object.js
+++ b/devtools/server/actors/object.js
@@ -1795,23 +1795,37 @@ DebuggerServer.ObjectActorPreviewers.Obj
   function PseudoArray({obj, hooks}, grip, rawObj) {
     let length = 0;
 
     let keys = obj.getOwnPropertyNames();
     if (keys.length == 0) {
       return false;
     }
 
-    // Making sure that all keys are array indices, that is:
-    // `ToString(ToUint32(key)) === key  &&  key !== "4294967295"`.
-    // Also ensuring that the keys are consecutive and start at "0",
-    // this implies checking `key !== "4294967295"` is not necessary.
+    // Pseudo-arrays should only have array indices and, optionally, a "length" property.
+    // Since array indices are sorted first, check if the last property is "length".
+    if(keys[keys.length-1] === "length") {
+      keys.pop();
+      // The value of "length" should equal the number of other properties. If eventually
+      // we allow sparse pseudo-arrays, we should check whether it's a Uint32 instead.
+      if(rawObj.length !== keys.length) {
+        return false;
+      }
+    }
+
+    // Ensure that the keys are consecutive integers starting at "0". If eventually we
+    // allow sparse pseudo-arrays, we should check that they are array indices, that is:
+    // `(key >>> 0) + '' === key && key !== "4294967295"`.
+    // Checking the last property first allows us to avoid useless iterations when
+    // there is any property which is not an array index.
+    if(keys.length && keys[keys.length-1] !== keys.length - 1 + '') {
+      return false;
+    }
     for (let key of keys) {
-      let numKey = key >>> 0; // ToUint32(key)
-      if (numKey + '' != key || numKey != length++) {
+      if (key !== (length++) + '') {
         return false;
       }
     }
 
     grip.preview = {
       kind: "ArrayLike",
       length: length,
     };
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -8541,21 +8541,24 @@ nsDocShell::RestoreFromHistory()
     mSavingOldViewer = CanSavePresentation(mLoadType, request, doc);
   }
 
   nsCOMPtr<nsIContentViewer> oldCv(mContentViewer);
   nsCOMPtr<nsIContentViewer> newCv(viewer);
   int32_t minFontSize = 0;
   float textZoom = 1.0f;
   float pageZoom = 1.0f;
+  float overrideDPPX = 0.0f;
+
   bool styleDisabled = false;
   if (oldCv && newCv) {
     oldCv->GetMinFontSize(&minFontSize);
     oldCv->GetTextZoom(&textZoom);
     oldCv->GetFullZoom(&pageZoom);
+    oldCv->GetOverrideDPPX(&overrideDPPX);
     oldCv->GetAuthorStyleDisabled(&styleDisabled);
   }
 
   // Protect against mLSHE going away via a load triggered from
   // pagehide or unload.
   nsCOMPtr<nsISHEntry> origLSHE = mLSHE;
 
   // Make sure to blow away our mLoadingURI just in case.  No loads
@@ -8778,16 +8781,17 @@ nsDocShell::RestoreFromHistory()
   if (++gNumberOfDocumentsLoading == 1) {
     FavorPerformanceHint(true);
   }
 
   if (oldCv && newCv) {
     newCv->SetMinFontSize(minFontSize);
     newCv->SetTextZoom(textZoom);
     newCv->SetFullZoom(pageZoom);
+    newCv->SetOverrideDPPX(overrideDPPX);
     newCv->SetAuthorStyleDisabled(styleDisabled);
   }
 
   nsCOMPtr<nsIDocument> document = do_QueryInterface(domDoc);
   uint32_t parentSuspendCount = 0;
   if (document) {
     RefPtr<nsDocShell> parent = GetParentDocshell();
     if (parent) {
@@ -9271,16 +9275,17 @@ nsDocShell::SetupNewViewer(nsIContentVie
   nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem));
 
   nsAutoCString forceCharset;
   nsAutoCString hintCharset;
   int32_t hintCharsetSource;
   int32_t minFontSize;
   float textZoom;
   float pageZoom;
+  float overrideDPPX;
   bool styleDisabled;
   // |newMUDV| also serves as a flag to set the data from the above vars
   nsCOMPtr<nsIContentViewer> newCv;
 
   if (mContentViewer || parent) {
     nsCOMPtr<nsIContentViewer> oldCv;
     if (mContentViewer) {
       // Get any interesting state from old content viewer
@@ -9312,16 +9317,18 @@ nsDocShell::SetupNewViewer(nsIContentVie
         NS_ENSURE_SUCCESS(oldCv->GetHintCharacterSetSource(&hintCharsetSource),
                           NS_ERROR_FAILURE);
         NS_ENSURE_SUCCESS(oldCv->GetMinFontSize(&minFontSize),
                           NS_ERROR_FAILURE);
         NS_ENSURE_SUCCESS(oldCv->GetTextZoom(&textZoom),
                           NS_ERROR_FAILURE);
         NS_ENSURE_SUCCESS(oldCv->GetFullZoom(&pageZoom),
                           NS_ERROR_FAILURE);
+        NS_ENSURE_SUCCESS(oldCv->GetOverrideDPPX(&overrideDPPX),
+                          NS_ERROR_FAILURE);
         NS_ENSURE_SUCCESS(oldCv->GetAuthorStyleDisabled(&styleDisabled),
                           NS_ERROR_FAILURE);
       }
     }
   }
 
   nscolor bgcolor = NS_RGBA(0, 0, 0, 0);
   // Ensure that the content viewer is destroyed *after* the GC - bug 71515
@@ -9380,16 +9387,18 @@ nsDocShell::SetupNewViewer(nsIContentVie
     NS_ENSURE_SUCCESS(newCv->SetHintCharacterSetSource(hintCharsetSource),
                       NS_ERROR_FAILURE);
     NS_ENSURE_SUCCESS(newCv->SetMinFontSize(minFontSize),
                       NS_ERROR_FAILURE);
     NS_ENSURE_SUCCESS(newCv->SetTextZoom(textZoom),
                       NS_ERROR_FAILURE);
     NS_ENSURE_SUCCESS(newCv->SetFullZoom(pageZoom),
                       NS_ERROR_FAILURE);
+    NS_ENSURE_SUCCESS(newCv->SetOverrideDPPX(overrideDPPX),
+                      NS_ERROR_FAILURE);
     NS_ENSURE_SUCCESS(newCv->SetAuthorStyleDisabled(styleDisabled),
                       NS_ERROR_FAILURE);
   }
 
   // Stuff the bgcolor from the old pres shell into the new
   // pres shell. This improves page load continuity.
   nsCOMPtr<nsIPresShell> shell;
   mContentViewer->GetPresShell(getter_AddRefs(shell));
--- a/dom/canvas/MurmurHash3.cpp
+++ b/dom/canvas/MurmurHash3.cpp
@@ -3,30 +3,29 @@
 // domain. The author hereby disclaims copyright to this source code.
 
 // Note - The x86 and x64 versions do _not_ produce the same results, as the
 // algorithms are optimized for their respective platforms. You can still
 // compile and run any of them on any platform, but your performance with the
 // non-native version will be less than optimal.
 
 #include "MurmurHash3.h"
+#include <stdlib.h>
 
 namespace {
 
 //-----------------------------------------------------------------------------
 // Platform-specific functions and macros
 
 // Microsoft Visual Studio
 
 #if defined(_MSC_VER)
 
 #define FORCE_INLINE	__forceinline
 
-#include <stdlib.h>
-
 #define ROTL32(x,y)	_rotl(x,y)
 #define ROTL64(x,y)	_rotl64(x,y)
 
 #define BIG_CONSTANT(x) (x)
 
 // Other compilers
 
 #else	// defined(_MSC_VER)
--- a/dom/events/ContentEventHandler.cpp
+++ b/dom/events/ContentEventHandler.cpp
@@ -1805,17 +1805,21 @@ ContentEventHandler::OnQueryTextRectArra
 
   RefPtr<nsRange> range = new nsRange(mRootContent);
 
   bool isVertical = false;
   LayoutDeviceIntRect rect;
   uint32_t offset = aEvent->mInput.mOffset;
   const uint32_t kEndOffset = offset + aEvent->mInput.mLength;
   bool wasLineBreaker = false;
+  // lastCharRect stores the last charRect value (see below for the detail of
+  // charRect).
   nsRect lastCharRect;
+  // lastFrame is base frame of lastCharRect.
+  nsIFrame* lastFrame;
   while (offset < kEndOffset) {
     nsCOMPtr<nsIContent> lastTextContent;
     rv = SetRangeFromFlatTextOffset(range, offset, 1, lineBreakType, true,
                                     nullptr, getter_AddRefs(lastTextContent));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
@@ -1848,28 +1852,24 @@ ContentEventHandler::OnQueryTextRectArra
       break;
     }
 
     nsIContent* firstContent = firstFrame.mFrame->GetContent();
     if (NS_WARN_IF(!firstContent)) {
       return NS_ERROR_FAILURE;
     }
 
-    // get the starting frame rect
-    nsRect frameRect(nsPoint(0, 0), firstFrame->GetRect().Size());
-    rv = ConvertToRootRelativeOffset(firstFrame, frameRect);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
     bool startsBetweenLineBreaker = false;
     nsAutoString chars;
     // XXX not bidi-aware this class...
     isVertical = firstFrame->GetWritingMode().IsVertical();
 
+    nsIFrame* baseFrame = firstFrame;
+    // charRect should have each character rect or line breaker rect relative
+    // to the base frame.
     AutoTArray<nsRect, 16> charRects;
 
     // If the first frame is a text frame, the result should be computed with
     // the frame's API.
     if (firstFrame->GetType() == nsGkAtoms::textFrame) {
       rv = firstFrame->GetCharacterRectsInRange(firstFrame.mOffsetInNode,
                                                 kEndOffset - offset, charRects);
       if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(charRects.IsEmpty())) {
@@ -1932,20 +1932,18 @@ ContentEventHandler::OnQueryTextRectArra
       // node before the first node in the queried range, we should compute the
       // first rect with the previous character's rect.
       // If we already compute a character's rect in the queried range, we can
       // compute it with the cached last character's rect.  (However, don't
       // use this path if it's a <br> frame because trusting <br> frame's rect
       // is better than guessing the rect from the previous character.)
       if (firstFrame->GetType() != nsGkAtoms::brFrame &&
           aEvent->mInput.mOffset != offset) {
-        // The frame position in the root widget will be added in the
-        // following for loop but we need the rect in the previous frame.
-        // So, we need to avoid using current frame position.
-        brRect = lastCharRect - frameRect.TopLeft();
+        baseFrame = lastFrame;
+        brRect = lastCharRect;
         if (!wasLineBreaker) {
           if (isVertical) {
             // Right of the last character.
             brRect.y = brRect.YMost() + 1;
             brRect.height = 1;
           } else {
             // Under the last character.
             brRect.x = brRect.XMost() + 1;
@@ -1957,17 +1955,26 @@ ContentEventHandler::OnQueryTextRectArra
       // queried range, we need to the previous character of the start of
       // the queried range if there is a text node.
       else if (firstFrame->GetType() != nsGkAtoms::brFrame && lastTextContent) {
         FrameRelativeRect brRectRelativeToLastTextFrame =
           GuessLineBreakerRectAfter(lastTextContent);
         if (NS_WARN_IF(!brRectRelativeToLastTextFrame.IsValid())) {
           return NS_ERROR_FAILURE;
         }
-        brRect = brRectRelativeToLastTextFrame.RectRelativeTo(firstFrame);
+        // Look for the last text frame for lastTextContent.
+        nsIFrame* primaryFrame = lastTextContent->GetPrimaryFrame();
+        if (NS_WARN_IF(!primaryFrame)) {
+          return NS_ERROR_FAILURE;
+        }
+        baseFrame = primaryFrame->LastContinuation();
+        if (NS_WARN_IF(!baseFrame)) {
+          return NS_ERROR_FAILURE;
+        }
+        brRect = brRectRelativeToLastTextFrame.RectRelativeTo(baseFrame);
       }
       // Otherwise, we need to compute the line breaker's rect only with the
       // first frame's rect.  But this may be unexpected.  For example,
       // |<div contenteditable>[<p>]abc</p></div>|.  In this case, caret is
       // before "a", therefore, users expect the rect left of "a".  However,
       // we don't have enough information about the next character here and
       // this isn't usual case (e.g., IME typically tries to query the rect
       // of "a" or caret rect for computing its popup position).  Therefore,
@@ -1996,19 +2003,27 @@ ContentEventHandler::OnQueryTextRectArra
     } else {
       NS_WARNING("The frame is neither a text frame nor a frame whose content "
                  "causes a line break");
       return NS_ERROR_FAILURE;
     }
 
     for (size_t i = 0; i < charRects.Length() && offset < kEndOffset; i++) {
       nsRect charRect = charRects[i];
-      charRect.x += frameRect.x;
-      charRect.y += frameRect.y;
+      // Store lastCharRect before applying CSS transform because it may be
+      // used for computing a line breaker rect.  Then, the computed line
+      // breaker rect will be applied CSS transform again.  Therefore,
+      // the value of lastCharRect should be raw rect value relative to the
+      // base frame.
       lastCharRect = charRect;
+      lastFrame = baseFrame;
+      rv = ConvertToRootRelativeOffset(baseFrame, charRect);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
 
       rect = LayoutDeviceIntRect::FromUnknownRect(
                charRect.ToOutsidePixels(mPresContext->AppUnitsPerDevPixel()));
       // Returning empty rect may cause native IME confused, let's make sure to
       // return non-empty rect.
       EnsureNonEmptyRect(rect);
 
       aEvent->mReply.mRectArray.AppendElement(rect);
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -613,19 +613,29 @@ TabParent::ActorDestroy(ActorDestroyReas
     nsCOMPtr<Element> frameElement(mFrameElement);
     ReceiveMessage(CHILD_PROCESS_SHUTDOWN_MESSAGE, false, nullptr, nullptr,
                    nullptr);
     frameLoader->DestroyComplete();
 
     if (why == AbnormalShutdown && os) {
       os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, frameLoader),
                           "oop-frameloader-crashed", nullptr);
-      nsContentUtils::DispatchTrustedEvent(frameElement->OwnerDoc(), frameElement,
-                                           NS_LITERAL_STRING("oop-browser-crashed"),
-                                           true, true);
+      nsCOMPtr<nsIFrameLoaderOwner> owner = do_QueryInterface(frameElement);
+      if (owner) {
+        RefPtr<nsFrameLoader> currentFrameLoader = owner->GetFrameLoader();
+        // It's possible that the frameloader owner has already moved on
+        // and created a new frameloader. If so, we don't fire the event,
+        // since the frameloader owner has clearly moved on.
+        if (currentFrameLoader == frameLoader) {
+          nsContentUtils::DispatchTrustedEvent(frameElement->OwnerDoc(), frameElement,
+                                               NS_LITERAL_STRING("oop-browser-crashed"),
+                                               true, true);
+
+        }
+      }
     }
 
     mFrameLoader = nullptr;
   }
 
   if (os) {
     os->NotifyObservers(NS_ISUPPORTS_CAST(nsITabParent*, this), "ipc:browser-destroyed", nullptr);
   }
--- a/dom/media/test/external/external_media_harness/testcase.py
+++ b/dom/media/test/external/external_media_harness/testcase.py
@@ -8,18 +8,22 @@ import time
 
 from marionette import BrowserMobProxyTestCaseMixin, MarionetteTestCase, Marionette
 from marionette_driver import Wait
 from marionette_driver.errors import TimeoutException
 from marionette.marionette_test import SkipTest
 
 from firefox_puppeteer.testcases import BaseFirefoxTestCase
 from external_media_tests.utils import (timestamp_now, verbose_until)
-from external_media_tests.media_utils.video_puppeteer import (playback_done, playback_started,
-                                         VideoException, VideoPuppeteer as VP)
+from external_media_tests.media_utils.video_puppeteer import (
+    playback_done,
+    playback_started,
+    VideoException,
+    VideoPuppeteer as VP
+)
 
 
 class MediaTestCase(BaseFirefoxTestCase, MarionetteTestCase):
 
     """
     Necessary methods for MSE playback
 
     :param video_urls: Urls you are going to play as part of the tests.
@@ -42,17 +46,18 @@ class MediaTestCase(BaseFirefoxTestCase,
                             '.png'])
         path = os.path.join(screenshot_dir, filename)
         if not os.path.exists(screenshot_dir):
             os.makedirs(screenshot_dir)
         with self.marionette.using_context(Marionette.CONTEXT_CONTENT):
             img_data = self.marionette.screenshot()
         with open(path, 'wb') as f:
             f.write(img_data.decode('base64'))
-        self.marionette.log('Screenshot saved in %s' % os.path.abspath(path))
+        self.marionette.log('Screenshot saved in {}'
+                            .format(os.path.abspath(path)))
 
     def log_video_debug_lines(self):
         """
         Log the debugging information that Firefox provides for video elements.
         """
         with self.marionette.using_context(Marionette.CONTEXT_CHROME):
             debug_lines = self.marionette.execute_script(VP._debug_script)
             if debug_lines:
@@ -155,21 +160,16 @@ class VideoPlaybackTestsMixin(object):
         with self.marionette.using_context(Marionette.CONTEXT_CONTENT):
             for url in self.video_urls:
                 try:
                     video = VP(self.marionette, url, timeout=60)
                     # Second playback_started check in case video._start_time
                     # is not 0
                     self.check_playback_starts(video)
                     video.pause()
-                    src = video.video_src
-                    if not src.startswith('mediasource'):
-                        self.marionette.log('video is not '
-                                            'mediasource: %s' % src,
-                                            level='WARNING')
                 except TimeoutException as e:
                     raise self.failureException(e)
 
     def test_video_playback_partial(self):
         """
         Test to make sure that playback of 60 seconds works for each video.
         """
         with self.marionette.using_context(Marionette.CONTEXT_CONTENT):
@@ -263,53 +263,55 @@ class EMESetupMixin(object):
                 adobe_result = self.marionette.execute_async_script(
                     reset_adobe_gmp_script,
                     script_timeout=60000)
                 widevine_result = self.marionette.execute_async_script(
                     reset_widevine_gmp_script,
                     script_timeout=60000)
                 if not adobe_result == 'success':
                     raise VideoException(
-                        'ERROR: Resetting Adobe GMP failed % s' % adobe_result)
+                        'ERROR: Resetting Adobe GMP failed {}'
+                        .format(adobe_result))
                 if not widevine_result == 'success':
                     raise VideoException(
-                        'ERROR: Resetting Widevine GMP failed % s'
-                        % widevine_result)
+                        'ERROR: Resetting Widevine GMP failed {}'
+                        .format(widevine_result))
 
             EMESetupMixin.version_needs_reset = False
 
     def check_and_log_boolean_pref(self, pref_name, expected_value):
         with self.marionette.using_context(Marionette.CONTEXT_CHROME):
             pref_value = self.prefs.get_pref(pref_name)
 
             if pref_value is None:
-                self.logger.info('Pref %s has no value.' % pref_name)
+                self.logger.info('Pref {} has no value.'.format(pref_name))
                 return False
             else:
-                self.logger.info('Pref %s = %s' % (pref_name, pref_value))
+                self.logger.info('Pref {} = {}'.format(pref_name, pref_value))
                 if pref_value != expected_value:
-                    self.logger.info('Pref %s has unexpected value.'
-                                     % pref_name)
+                    self.logger.info('Pref {} has unexpected value.'
+                                     .format(pref_name))
                     return False
 
         return True
 
     def check_and_log_integer_pref(self, pref_name, minimum_value=0):
         with self.marionette.using_context(Marionette.CONTEXT_CHROME):
             pref_value = self.prefs.get_pref(pref_name)
 
             if pref_value is None:
-                self.logger.info('Pref %s has no value.' % pref_name)
+                self.logger.info('Pref {} has no value.'.format(pref_name))
                 return False
             else:
-                self.logger.info('Pref %s = %s' % (pref_name, pref_value))
+                self.logger.info('Pref {} = {}'.format(pref_name, pref_value))
 
                 match = re.search('^\d+$', pref_value)
                 if not match:
-                    self.logger.info('Pref %s is not an integer' % pref_name)
+                    self.logger.info('Pref {} is not an integer'
+                                     .format(pref_name))
                     return False
 
             return pref_value >= minimum_value
 
     def chceck_and_log_version_string_pref(self, pref_name, minimum_value='0'):
         """
         Compare a pref made up of integers separated by stops .s, with a
         version string of the same format. The number of integers in each
@@ -318,25 +320,25 @@ class EMESetupMixin(object):
         must be made up of only integers, or this method will raise an
         unhandled exception of type ValueError when the conversion to int
         fails.
         """
         with self.marionette.using_context(Marionette.CONTEXT_CHROME):
             pref_value = self.prefs.get_pref(pref_name)
 
             if pref_value is None:
-                self.logger.info('Pref %s has no value.' % pref_name)
+                self.logger.info('Pref {} has no value.'.format(pref_name))
                 return False
             else:
-                self.logger.info('Pref %s = %s' % (pref_name, pref_value))
+                self.logger.info('Pref {} = {}'.format(pref_name, pref_value))
 
                 match = re.search('^\d(.\d+)*$', pref_value)
                 if not match:
-                    self.logger.info('Pref %s is not a version string'
-                                     % pref_name)
+                    self.logger.info('Pref {} is not a version string'
+                                     .format(pref_name))
                     return False
 
             pref_ints = [int(n) for n in pref_value.split('.')]
             minumum_ints = [int(n) for n in minimum_value.split('.')]
 
             return pref_ints >= minumum_ints
 
     def check_eme_prefs(self):
--- a/dom/media/test/external/external_media_tests/media_utils/video_puppeteer.py
+++ b/dom/media/test/external/external_media_tests/media_utils/video_puppeteer.py
@@ -269,25 +269,27 @@ class VideoPuppeteer(object):
         :param script: script to be executed
         :return: value returned by script
         """
         with self.marionette.using_context(Marionette.CONTEXT_CONTENT):
             return self.marionette.execute_script(script,
                                                   script_args=[self.video])
 
     def __str__(self):
-        messages = ['%s - test url: %s: {' % (type(self).__name__,
-                                              self.test_url)]
+        messages = ['{} - test url: {}: '
+                    .format(type(self).__name__, self.test_url)]
+        messages += '{'
         if self.video:
             messages += [
                 '\t(video)',
                 '\tcurrent_time: {},'.format(self.current_time),
                 '\tduration: {},'.format(self.duration),
                 '\texpected_duration: {},'.format(self.expected_duration),
                 '\tplayed: {}'.format(self.played),
+                '\tinterval: {}'.format(self.interval),
                 '\tlag: {},'.format(self.lag),
                 '\turl: {}'.format(self.video_url),
                 '\tsrc: {}'.format(self.video_src),
                 '\tframes total: {}'.format(self.total_frames),
                 '\t - dropped: {}'.format(self.dropped_frames),
                 '\t - corrupted: {}'.format(self.corrupted_frames)
             ]
         else:
@@ -308,18 +310,20 @@ class TimeRanges:
     Class to represent the TimeRanges data returned by played(). Exposes a
     similar interface to the JavaScript TimeRanges object.
     """
     def __init__(self, length, ranges):
         self.length = length
         self.ranges = [(pair[0], pair[1]) for pair in ranges]
 
     def __repr__(self):
-        return 'TimeRanges: length: {}, ranges: {}'\
-               .format(self.length, self.ranges)
+        return (
+            'TimeRanges: length: {}, ranges: {}'
+            .format(self.length, self.ranges)
+        )
 
     def start(self, index):
         return self.ranges[index][0]
 
     def end(self, index):
         return self.ranges[index][1]
 
 
@@ -328,19 +332,21 @@ def playback_started(video):
     Determine if video has started
 
     :param video: The VideoPuppeteer instance that we are interested in.
 
     :return: True if is playing; False otherwise
     """
     try:
         played_ranges = video.played
-        return played_ranges.length > 0 and \
-               played_ranges.start(0) < played_ranges.end(0) and \
-               played_ranges.end(0) > 0.0
+        return (
+            played_ranges.length > 0 and
+            played_ranges.start(0) < played_ranges.end(0) and
+            played_ranges.end(0) > 0.0
+        )
     except Exception as e:
         print ('Got exception {}'.format(e))
         return False
 
 
 def playback_done(video):
     """
     If we are near the end and there is still a video element, then
@@ -353,13 +359,13 @@ def playback_done(video):
         otherwise.
     """
     if video.remaining_time < video.interval:
         return True
 
     # Check to see if the video has stalled. Accumulate the amount of lag
     # since the video started, and if it is too high, then raise.
     if video.stall_wait_time and (video.lag > video.stall_wait_time):
-        raise VideoException('Video %s stalled.\n%s' % (video.video_url,
-                                                        video))
+        raise VideoException('Video {} stalled.\n{}'
+                             .format(video.video_url, video))
 
     # We are cruising, so we are not done.
     return False
--- a/dom/media/test/external/external_media_tests/media_utils/youtube_puppeteer.py
+++ b/dom/media/test/external/external_media_tests/media_utils/youtube_puppeteer.py
@@ -335,18 +335,22 @@ class YouTubePuppeteer(VideoPuppeteer):
         """
         Try to skip this ad. If not, wait for this ad to finish.
         """
         if self.attempt_ad_skip() or self.ad_inactive:
             return
         ad_timeout = (self.search_ad_duration() or 30) + 5
         wait = Wait(self, timeout=ad_timeout, interval=1)
         try:
-            self.marionette.log('process_ad: waiting %s s for ad' % ad_timeout)
-            verbose_until(wait, self, lambda y: y.ad_ended, "Check if ad ended")
+            self.marionette.log('process_ad: waiting {} s for ad'
+                                .format(ad_timeout))
+            verbose_until(wait,
+                          self,
+                          lambda y: y.ad_ended,
+                          "Check if ad ended")
         except TimeoutException:
             self.marionette.log('Waiting for ad to end timed out',
                                 level='WARNING')
 
     def attempt_ad_skip(self):
         """
         Attempt to skip ad by clicking on skip-add button.
 
@@ -367,17 +371,17 @@ class YouTubePuppeteer(VideoPuppeteer):
                                                           selector))
                     ad_button = self.marionette.find_element(By.CSS_SELECTOR,
                                                              selector)
                     ad_button.click()
                     self.marionette.log('Skipped ad.')
                     return True
             except (TimeoutException, NoSuchElementException):
                 self.marionette.log('Could not obtain '
-                                    'element: %s' % selector,
+                                    'element: {}'.format(selector),
                                     level='WARNING')
         return False
 
     def search_ad_duration(self):
         """
 
         :return: ad duration in seconds, if currently displayed in player
         """
@@ -397,17 +401,17 @@ class YouTubePuppeteer(VideoPuppeteer):
                                                          selector)
                 ad_time = self._time_pattern.search(countdown.text)
                 if ad_time:
                     ad_minutes = int(ad_time.group('minute'))
                     ad_seconds = int(ad_time.group('second'))
                     return 60 * ad_minutes + ad_seconds
         except (TimeoutException, NoSuchElementException):
             self.marionette.log('Could not obtain '
-                                'element: %s' % selector,
+                                'element: {}'.format(selector),
                                 level='WARNING')
         return None
 
     @property
     def player_stalled(self):
         """
 
         :return: True if playback is not making progress for 4-9 seconds. This
@@ -457,35 +461,35 @@ class YouTubePuppeteer(VideoPuppeteer):
                 # the pref might get reset moments later.
                 sleep(1)
                 if get_status(checkbox):
                     mn.execute_script('return arguments[0].'
                                       'wrappedJSObject.click()',
                                       script_args=[checkbox])
                     self.marionette.log('Toggled autoplay.')
                 autoplay = get_status(checkbox)
-                self.marionette.log('Autoplay is %s' % autoplay)
+                self.marionette.log('Autoplay is {}'.format(autoplay))
                 return (autoplay is not None) and (not autoplay)
         except (NoSuchElementException, TimeoutException):
             return False
 
     def __str__(self):
         messages = [super(YouTubePuppeteer, self).__str__()]
         if self.player:
             player_state = self._yt_player_state_name[self.player_state]
             ad_state = self._yt_player_state_name[self.ad_state]
             messages += [
                 '.html5-media-player: {',
-                '\tvideo id: {0},'.format(self.movie_id),
-                '\tvideo_title: {0}'.format(self.movie_title),
-                '\tcurrent_state: {0},'.format(player_state),
-                '\tad_state: {0},'.format(ad_state),
-                '\tplayback_quality: {0},'.format(self.playback_quality),
-                '\tcurrent_time: {0},'.format(self.player_current_time),
-                '\tduration: {0},'.format(self.player_duration),
+                '\tvideo id: {},'.format(self.movie_id),
+                '\tvideo_title: {}'.format(self.movie_title),
+                '\tcurrent_state: {},'.format(player_state),
+                '\tad_state: {},'.format(ad_state),
+                '\tplayback_quality: {},'.format(self.playback_quality),
+                '\tcurrent_time: {},'.format(self.player_current_time),
+                '\tduration: {},'.format(self.player_duration),
                 '}'
             ]
         else:
             messages += ['\t.html5-media-player: None']
         return '\n'.join(messages)
 
 
 def playback_started(yt):
--- a/dom/media/test/external/external_media_tests/playback/test_eme_playback.py
+++ b/dom/media/test/external/external_media_tests/playback/test_eme_playback.py
@@ -1,13 +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/.
 
-from external_media_harness.testcase import MediaTestCase, VideoPlaybackTestsMixin, EMESetupMixin
+from external_media_harness.testcase import (
+    MediaTestCase,
+    VideoPlaybackTestsMixin,
+    EMESetupMixin
+)
 
 
 class TestEMEPlayback(MediaTestCase, VideoPlaybackTestsMixin, EMESetupMixin):
 
     def setUp(self):
         super(TestEMEPlayback, self).setUp()
         self.check_eme_system()
 
--- a/dom/media/test/external/external_media_tests/playback/youtube/test_basic_playback.py
+++ b/dom/media/test/external/external_media_tests/playback/youtube/test_basic_playback.py
@@ -4,18 +4,21 @@
 
 from marionette import Marionette
 from marionette_driver import Wait
 from marionette_driver.errors import TimeoutException
 
 from external_media_tests.utils import verbose_until
 from external_media_harness.testcase import MediaTestCase
 from external_media_tests.media_utils.video_puppeteer import VideoException
-from external_media_tests.media_utils.youtube_puppeteer import (YouTubePuppeteer, playback_done,
-                                           wait_for_almost_done)
+from external_media_tests.media_utils.youtube_puppeteer import (
+    YouTubePuppeteer,
+    playback_done,
+    wait_for_almost_done
+)
 
 
 class TestBasicYouTubePlayback(MediaTestCase):
     def test_mse_is_enabled_by_default(self):
         with self.marionette.using_context(Marionette.CONTEXT_CONTENT):
             youtube = YouTubePuppeteer(self.marionette, self.video_urls[0],
                                        timeout=60)
             wait = Wait(youtube,
@@ -28,37 +31,39 @@ class TestBasicYouTubePlayback(MediaTest
             except TimeoutException as e:
                 raise self.failureException(e)
 
     def test_video_playing_in_one_tab(self):
         with self.marionette.using_context(Marionette.CONTEXT_CONTENT):
             for url in self.video_urls:
                 self.logger.info(url)
                 youtube = YouTubePuppeteer(self.marionette, url)
-                self.logger.info('Expected duration: %s' %
-                                 youtube.expected_duration)
+                self.logger.info('Expected duration: {}'
+                                 .format(youtube.expected_duration))
                 youtube.deactivate_autoplay()
 
                 final_piece = 60
                 try:
                     time_left = wait_for_almost_done(youtube,
                                                      final_piece=final_piece)
                 except VideoException as e:
                     raise self.failureException(e)
                 duration = abs(youtube.expected_duration) + 1
                 if duration > 1:
-                    self.logger.info('Almost done: %s - %s seconds left.' %
-                                     (youtube.movie_id, time_left))
+                    self.logger.info('Almost done: {} - {} seconds left.'
+                                     .format(youtube.movie_id, time_left))
                     if time_left > final_piece:
                         self.marionette.log('time_left greater than '
-                                            'final_piece - %s' % time_left,
+                                            'final_piece - {}'
+                                            .format(time_left),
                                             level='WARNING')
                         self.save_screenshot()
                 else:
-                    self.marionette.log('Duration close to 0 - %s' % youtube,
+                    self.marionette.log('Duration close to 0 - {}'
+                                        .format(youtube),
                                         level='WARNING')
                     self.save_screenshot()
                 try:
                     verbose_until(Wait(youtube,
                                        timeout=max(100, time_left) * 1.3,
                                        interval=1),
                                   youtube,
                                   playback_done)
--- a/dom/network/tests/test_tcpsocket_client_and_server_basics.js
+++ b/dom/network/tests/test_tcpsocket_client_and_server_basics.js
@@ -30,33 +30,43 @@ function assertUint8ArraysEqual(a, b, co
 /**
  * Helper method to add event listeners to a socket and provide two Promise-returning
  * helpers (see below for docs on them).  This *must* be called during the turn of
  * the event loop where TCPSocket's constructor is called or the onconnect method is being
  * invoked.
  */
 function listenForEventsOnSocket(socket, socketType) {
   let wantDataLength = null;
+  let wantDataAndClose = false;
   let pendingResolve = null;
   let receivedEvents = [];
   let receivedData = null;
   let handleGenericEvent = function(event) {
     dump('(' + socketType + ' event: ' + event.type + ')\n');
     if (pendingResolve && wantDataLength === null) {
       pendingResolve(event);
       pendingResolve = null;
     } else {
       receivedEvents.push(event);
     }
   };
 
   socket.onopen = handleGenericEvent;
   socket.ondrain = handleGenericEvent;
   socket.onerror = handleGenericEvent;
-  socket.onclose = handleGenericEvent;
+  socket.onclose = function(event) {
+    if (!wantDataAndClose) {
+      handleGenericEvent(event);
+    } else if (pendingResolve) {
+      dump('(' + socketType + ' event: close)\n');
+      pendingResolve(receivedData);
+      pendingResolve = null;
+      wantDataAndClose = false;
+    }
+  }
   socket.ondata = function(event) {
     dump('(' + socketType + ' event: ' + event.type + ' length: ' +
          event.data.byteLength + ')\n');
     ok(socketCompartmentInstanceOfArrayBuffer(event.data),
        'payload is ArrayBuffer');
     var arr = new Uint8Array(event.data);
     if (receivedData === null) {
       receivedData = arr;
@@ -109,16 +119,29 @@ function listenForEventsOnSocket(socket,
         receivedData = null;
         return promise;
       }
       dump('(' + socketType + ' waiting for ' + length + ' bytes)\n');
       return new Promise(function(resolve, reject) {
         pendingResolve = resolve;
         wantDataLength = length;
       });
+    },
+    waitForAnyDataAndClose: function() {
+      if (pendingResolve) {
+        throw new Error('only one wait allowed at a time.');
+      }
+
+      return new Promise(function(resolve, reject) {
+        pendingResolve = resolve;
+        // we may receive no data before getting close, in which case we want to
+        // return an empty array
+        receivedData = new Uint8Array();
+        wantDataAndClose = true;
+      });
     }
   };
 }
 
 /**
  * Return a promise that is resolved when the server receives a connection.  The
  * promise is resolved with { socket, queue } where `queue` is the result of
  * calling listenForEventsOnSocket(socket).  This must be done because we need
@@ -374,23 +397,20 @@ function* test_basics() {
 
   // -- Attempt to send two non-string data.
   is(clientSocket.send(bigUint8Array.buffer, 0, bigUint8Array.length), false,
      'Server sending more than 64k should result in the buffer being full.');
   is(clientSocket.send(bigUint8Array.buffer, 0, bigUint8Array.length), false,
      'Server sending more than 64k should result in the buffer being full.');
   clientSocket.closeImmediately();
 
-  serverReceived = yield serverQueue.waitForDataWithAtLeastLength(1);
+  serverReceived = yield serverQueue.waitForAnyDataAndClose();
 
   is(serverReceived.length < (2 * bigUint8Array.length), true, 'Received array length less than sent array length');
 
-  is((yield serverQueue.waitForEvent()).type, 'close',
-     'The close event is received after calling closeImmediately');
-
   // -- Close the listening server (and try to connect)
   // We want to verify that the server actually closes / stops listening when
   // we tell it to.
   listeningServer.close();
 
   // - try and connect, get an error
   clientSocket = createSocket('127.0.0.1', serverPort,
                               { binaryType: 'arraybuffer' });
--- a/dom/tests/mochitest/general/test_contentViewer_overrideDPPX.html
+++ b/dom/tests/mochitest/general/test_contentViewer_overrideDPPX.html
@@ -279,16 +279,60 @@ const gTests = {
     promises[0]
       .then(() => setOverrideDPPX(0))
       .then(promises[1])
       .then(() => setFullZoom(originalZoom))
       .then(done, e => {throw e});
 
     setOverrideDPPX(dppx);
     setFullZoom(zoom);
+  },
+  "test OverrideDPPX is kept on document navigation": (done) => {
+    assertValuesAreInitial();
+
+    let frameOriginalFontSize = getBodyFontSize(frameWindow);
+    let frameStyle = createFontStyleForDPPX(frameWindow.document, dppx, "32");
+    let frameCurrentFontSize = getBodyFontSize(frameWindow);
+
+    is(frameCurrentFontSize, frameOriginalFontSize,
+      "frame's media queries are not applied yet");
+
+    setOverrideDPPX(dppx);
+
+    frameCurrentFontSize = getBodyFontSize(frameWindow);
+
+    is(frameWindow.devicePixelRatio, dppx,
+      "frame's devicePixelRatio overridden.");
+    isnot(frameCurrentFontSize, frameOriginalFontSize,
+      "frame's media queries are applied.");
+    is(frameCurrentFontSize, "32px",
+      "frame's font size has the expected value.");
+
+    frameWindow.frameElement.addEventListener("load", function listener() {
+      this.removeEventListener("load", listener);
+
+      frameStyle = createFontStyleForDPPX(frameWindow.document, dppx, "32");
+
+      frameCurrentFontSize = getBodyFontSize(frameWindow);
+
+      is(frameWindow.devicePixelRatio, dppx,
+        "frame's devicePixelRatio is still overridden.");
+      isnot(frameCurrentFontSize, frameOriginalFontSize,
+        "frame's media queries are still applied.");
+      is(frameCurrentFontSize, "32px",
+        "frame's font size has still the expected value.");
+
+      frameStyle.remove();
+
+      setOverrideDPPX(0);
+
+      done();
+    });
+
+    frameWindow.location.reload(true);
   }
 };
 
 function* runner(tests) {
   for (let name of Object.keys(tests)) {
     info(name);
     tests[name](next);
     yield undefined;
--- a/gfx/vr/VRDisplayHost.cpp
+++ b/gfx/vr/VRDisplayHost.cpp
@@ -40,26 +40,34 @@ VRDisplayHost::~VRDisplayHost()
 void
 VRDisplayHost::AddLayer(VRLayerParent *aLayer)
 {
   mLayers.AppendElement(aLayer);
   if (mLayers.Length() == 1) {
     StartPresentation();
   }
   mDisplayInfo.mIsPresenting = mLayers.Length() > 0;
+
+  // Ensure that the content process receives the change immediately
+  VRManager* vm = VRManager::Get();
+  vm->RefreshVRDisplays();
 }
 
 void
 VRDisplayHost::RemoveLayer(VRLayerParent *aLayer)
 {
   mLayers.RemoveElement(aLayer);
   if (mLayers.Length() == 0) {
     StopPresentation();
   }
   mDisplayInfo.mIsPresenting = mLayers.Length() > 0;
+
+  // Ensure that the content process receives the change immediately
+  VRManager* vm = VRManager::Get();
+  vm->RefreshVRDisplays();
 }
 
 #if defined(XP_WIN)
 
 void
 VRDisplayHost::SubmitFrame(VRLayerParent* aLayer, const int32_t& aInputFrameID,
   PTextureParent* aTexture, const gfx::Rect& aLeftEyeRect,
   const gfx::Rect& aRightEyeRect)
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -5232,17 +5232,17 @@ ScrollFrameHelper::ReflowFinished()
 
   nsAutoScriptBlocker scriptBlocker;
   ScrollToRestoredPosition();
 
   // Clamp current scroll position to new bounds. Normally this won't
   // do anything.
   nsPoint currentScrollPos = GetScrollPosition();
   ScrollToImpl(currentScrollPos, nsRect(currentScrollPos, nsSize(0, 0)));
-  if (!mAsyncScroll && !mAsyncSmoothMSDScroll) {
+  if (!mAsyncScroll && !mAsyncSmoothMSDScroll && !mApzSmoothScrollDestination) {
     // We need to have mDestination track the current scroll position,
     // in case it falls outside the new reflow area. mDestination is used
     // by ScrollBy as its starting position.
     mDestination = GetScrollPosition();
   }
 
   if (!mUpdateScrollbarAttributes) {
     return false;
--- a/layout/tools/reftest/mach_test_package_commands.py
+++ b/layout/tools/reftest/mach_test_package_commands.py
@@ -9,25 +9,22 @@ from functools import partial
 
 from mach.decorators import (
     CommandProvider,
     Command,
 )
 
 
 def run_reftest(context, **kwargs):
+    kwargs['app'] = kwargs['app'] or context.firefox_bin
+    kwargs['e10s'] = context.mozharness_config.get('e10s', kwargs['e10s'])
     kwargs['certPath'] = context.certs_dir
     kwargs['utilityPath'] = context.bin_dir
     kwargs['extraProfileFiles'].append(os.path.join(context.bin_dir, 'plugins'))
 
-    if not kwargs['app']:
-        # This could still return None in which case --appname must be used
-        # to specify the firefox binary.
-        kwargs['app'] = context.find_firefox()
-
     if not kwargs['tests']:
         kwargs['tests'] = [os.path.join('layout', 'reftests', 'reftest.list')]
 
     test_root = os.path.join(context.package_root, 'reftest', 'tests')
     normalize = partial(context.normalize_test_path, test_root)
     kwargs['tests'] = map(normalize, kwargs['tests'])
 
     from runreftest import run as run_test_harness
--- a/mobile/android/app/build.gradle
+++ b/mobile/android/app/build.gradle
@@ -193,16 +193,17 @@ dependencies {
     compile 'com.android.support:multidex:1.0.0'
 
     compile "com.android.support:support-v4:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
     compile "com.android.support:appcompat-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
     compile "com.android.support:cardview-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
     compile "com.android.support:recyclerview-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
     compile "com.android.support:design:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
     compile "com.android.support:customtabs:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
+    compile "com.android.support:palette-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
 
     if (mozconfig.substs.MOZ_NATIVE_DEVICES) {
         compile "com.android.support:mediarouter-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
         compile "com.google.android.gms:play-services-basement:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
         compile "com.google.android.gms:play-services-base:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
         compile "com.google.android.gms:play-services-cast:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
     }
 
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -59,16 +59,17 @@ JAVA_CLASSPATH += \
     $(ANDROID_SUPPORT_ANNOTATIONS_JAR_LIB) \
     $(ANDROID_SUPPORT_V4_AAR_LIB) \
     $(ANDROID_SUPPORT_V4_AAR_INTERNAL_LIB) \
     $(ANDROID_APPCOMPAT_V7_AAR_LIB) \
     $(ANDROID_CARDVIEW_V7_AAR_LIB) \
     $(ANDROID_DESIGN_AAR_LIB) \
     $(ANDROID_RECYCLERVIEW_V7_AAR_LIB) \
     $(ANDROID_CUSTOMTABS_AAR_LIB) \
+    $(ANDROID_PALETTE_V7_AAR_LIB) \
     $(NULL)
 
 # If native devices are enabled, add Google Play Services and some of the v7
 # compat libraries.
 ifdef MOZ_NATIVE_DEVICES
     JAVA_CLASSPATH += \
         $(ANDROID_PLAY_SERVICES_BASE_AAR_LIB) \
         $(ANDROID_PLAY_SERVICES_BASEMENT_AAR_LIB) \
@@ -101,16 +102,17 @@ JAVA_CLASSPATH := $(subst $(NULL) ,:,$(s
 java_bundled_libs := \
     $(ANDROID_SUPPORT_V4_AAR_LIB) \
     $(ANDROID_SUPPORT_V4_AAR_INTERNAL_LIB) \
     $(ANDROID_APPCOMPAT_V7_AAR_LIB) \
     $(ANDROID_CARDVIEW_V7_AAR_LIB) \
     $(ANDROID_DESIGN_AAR_LIB) \
     $(ANDROID_RECYCLERVIEW_V7_AAR_LIB) \
     $(ANDROID_CUSTOMTABS_AAR_LIB) \
+    $(ANDROID_PALETTE_V7_AAR_LIB) \
     $(NULL)
 
 ifdef MOZ_NATIVE_DEVICES
     java_bundled_libs += \
         $(ANDROID_PLAY_SERVICES_BASE_AAR_LIB) \
         $(ANDROID_PLAY_SERVICES_BASEMENT_AAR_LIB) \
         $(ANDROID_PLAY_SERVICES_CAST_AAR_LIB) \
         $(ANDROID_MEDIAROUTER_V7_AAR_LIB) \
@@ -391,16 +393,17 @@ generated/org/mozilla/gecko/R.java: .aap
 # If native devices are enabled, add Google Play Services, build their resources
 generated/android/support/v4/R.java: .aapt.deps ;
 generated/android/support/v7/appcompat/R.java: .aapt.deps ;
 generated/android/support/v7/cardview/R.java: .aapt.deps ;
 generated/android/support/design/R.java: .aapt.deps ;
 generated/android/support/v7/mediarouter/R.java: .aapt.deps ;
 generated/android/support/v7/recyclerview/R.java: .aapt.deps ;
 generated/android/support/customtabs/R.java: .aapt.deps ;
+generated/android/support/v7/palette/R.java: .aapt.deps ;
 generated/com/google/android/gms/R.java: .aapt.deps ;
 generated/com/google/android/gms/ads/R.java: .aapt.deps ;
 generated/com/google/android/gms/base/R.java: .aapt.deps ;
 generated/com/google/android/gms/cast/R.java: .aapt.deps ;
 generated/com/google/android/gms/gcm/R.java: .aapt.deps ;
 generated/com/google/android/gms/measurement/R.java: .aapt.deps ;
 
 gecko.ap_: .aapt.deps ;
--- a/mobile/android/base/java/org/mozilla/gecko/db/BrowserProvider.java
+++ b/mobile/android/base/java/org/mozilla/gecko/db/BrowserProvider.java
@@ -1206,16 +1206,20 @@ public class BrowserProvider extends Sha
         return db.rawQuery(query, null);
     }
 
     @Override
     public Cursor query(Uri uri, String[] projection, String selection,
             String[] selectionArgs, String sortOrder) {
         final int match = URI_MATCHER.match(uri);
 
+        // Handle only queries requiring a writable DB connection here: most queries need only a readable
+        // connection, hence we can get a readable DB once, and then handle most queries within a switch.
+        // TopSites requires a writable connection (because of the temporary tables it uses), hence
+        // we handle that separately, i.e. before retrieving a readable connection.
         if (match == TOPSITES) {
             if (uri.getBooleanQueryParameter(BrowserContract.PARAM_TOPSITES_DISABLE_PINNED, false)) {
                 return getPlainTopSites(uri);
             } else {
                 return getTopSites(uri);
             }
         }
 
--- a/mobile/android/base/java/org/mozilla/gecko/icons/IconRequestExecutor.java
+++ b/mobile/android/base/java/org/mozilla/gecko/icons/IconRequestExecutor.java
@@ -1,43 +1,47 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.icons;
 
+import android.support.annotation.NonNull;
+
 import org.mozilla.gecko.icons.loader.ContentProviderLoader;
 import org.mozilla.gecko.icons.loader.DataUriLoader;
+import org.mozilla.gecko.icons.loader.DiskLoader;
 import org.mozilla.gecko.icons.loader.IconDownloader;
 import org.mozilla.gecko.icons.loader.IconGenerator;
+import org.mozilla.gecko.icons.loader.IconLoader;
 import org.mozilla.gecko.icons.loader.JarLoader;
 import org.mozilla.gecko.icons.loader.LegacyLoader;
-import org.mozilla.gecko.icons.loader.IconLoader;
 import org.mozilla.gecko.icons.loader.MemoryLoader;
-import org.mozilla.gecko.icons.loader.DiskLoader;
 import org.mozilla.gecko.icons.preparation.AboutPagesPreparer;
 import org.mozilla.gecko.icons.preparation.AddDefaultIconUrl;
 import org.mozilla.gecko.icons.preparation.FilterKnownFailureUrls;
 import org.mozilla.gecko.icons.preparation.FilterMimeTypes;
 import org.mozilla.gecko.icons.preparation.FilterPrivilegedUrls;
 import org.mozilla.gecko.icons.preparation.LookupIconUrl;
 import org.mozilla.gecko.icons.preparation.Preparer;
 import org.mozilla.gecko.icons.processing.ColorProcessor;
+import org.mozilla.gecko.icons.processing.DiskProcessor;
 import org.mozilla.gecko.icons.processing.MemoryProcessor;
 import org.mozilla.gecko.icons.processing.Processor;
 import org.mozilla.gecko.icons.processing.ResizingProcessor;
-import org.mozilla.gecko.icons.processing.DiskProcessor;
 
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Executor for icon requests.
  */
 /* package-private */ class IconRequestExecutor {
     /**
      * Loader implementation that generates an icon if none could be loaded.
      */
@@ -97,30 +101,50 @@ import java.util.concurrent.Future;
             // Download the icon from the web.
             new IconDownloader()
     );
 
     /**
      * Ordered list of processors that run after an icon has been loaded.
      */
     private static final List<Processor> PROCESSORS = Arrays.asList(
-            // Extract the dominant color from the icon
-            new ColorProcessor(),
-
             // Store the icon (and mapping) in the disk cache if needed
             new DiskProcessor(),
 
             // Resize the icon to match the target size (if possible)
             new ResizingProcessor(),
 
+            // Extract the dominant color from the icon
+            new ColorProcessor(),
+
             // Store the icon in the memory cache
             new MemoryProcessor()
     );
 
-    private static final ExecutorService EXECUTOR = Executors.newSingleThreadExecutor();
+    private static final ExecutorService EXECUTOR;
+    static {
+        final ThreadFactory factory = new ThreadFactory() {
+            @Override
+            public Thread newThread(@NonNull Runnable runnable) {
+                Thread thread = new Thread(runnable, "GeckoIconTask");
+                thread.setDaemon(false);
+                thread.setPriority(Thread.NORM_PRIORITY);
+                return thread;
+            }
+        };
+
+        // Single thread executor
+        EXECUTOR = new ThreadPoolExecutor(
+                1, /* corePoolSize */
+                1, /* maximumPoolSize */
+                0L, /* keepAliveTime */
+                TimeUnit.MILLISECONDS,
+                new LinkedBlockingQueue<Runnable>(),
+                factory);
+    }
 
     /**
      * Submit the request for execution.
      */
     /* package-private */ static Future<IconResponse> submit(IconRequest request) {
         return EXECUTOR.submit(
                 new IconTask(request, PREPARERS, LOADERS, PROCESSORS, GENERATOR)
         );
--- a/mobile/android/base/java/org/mozilla/gecko/icons/loader/IconDownloader.java
+++ b/mobile/android/base/java/org/mozilla/gecko/icons/loader/IconDownloader.java
@@ -51,17 +51,17 @@ public class IconDownloader implements I
 
         final String iconUrl = request.getBestIcon().getUrl();
 
         if (!StringUtils.isHttpOrHttps(iconUrl)) {
             return null;
         }
 
         try {
-            LoadFaviconResult result = downloadAndDecodeImage(request.getContext(), iconUrl);
+            final LoadFaviconResult result = downloadAndDecodeImage(request.getContext(), iconUrl);
             if (result == null) {
                 return null;
             }
 
             final Bitmap bitmap = result.getBestBitmap(request.getTargetSize());
             if (bitmap == null) {
                 return null;
             }
@@ -85,57 +85,58 @@ public class IconDownloader implements I
      * @throws IOException If attempts to fully read the stream result in such an exception, such as
      *                     in the event of a transient connection failure.
      * @throws URISyntaxException If the underlying call to tryDownload retries and raises such an
      *                            exception trying a fallback URL.
      */
     @VisibleForTesting
     LoadFaviconResult downloadAndDecodeImage(Context context, String targetFaviconURL) throws IOException, URISyntaxException {
         // Try the URL we were given.
-        HttpURLConnection connection = tryDownload(targetFaviconURL);
+        final HttpURLConnection connection = tryDownload(targetFaviconURL);
         if (connection == null) {
             return null;
         }
 
         InputStream stream = null;
 
         // Decode the image from the fetched response.
         try {
-            return decodeImageFromResponse(context, connection.getInputStream(), connection.getHeaderFieldInt("Content-Length", -1));
+            stream = connection.getInputStream();
+            return decodeImageFromResponse(context, stream, connection.getHeaderFieldInt("Content-Length", -1));
         } finally {
             // Close the stream and free related resources.
             IOUtils.safeStreamClose(stream);
             connection.disconnect();
         }
     }
 
     /**
      * Helper method for trying the download request to grab a Favicon.
      *
      * @param faviconURI URL of Favicon to try and download
      * @return The HttpResponse containing the downloaded Favicon if successful, null otherwise.
      */
     private HttpURLConnection tryDownload(String faviconURI) throws URISyntaxException, IOException {
-        HashSet<String> visitedLinkSet = new HashSet<>();
+        final HashSet<String> visitedLinkSet = new HashSet<>();
         visitedLinkSet.add(faviconURI);
         return tryDownloadRecurse(faviconURI, visitedLinkSet);
     }
 
     /**
      * Try to download from the favicon URL and recursively follow redirects.
      */
     private HttpURLConnection tryDownloadRecurse(String faviconURI, HashSet<String> visited) throws URISyntaxException, IOException {
         if (visited.size() == MAX_REDIRECTS_TO_FOLLOW) {
             return null;
         }
 
-        HttpURLConnection connection = connectTo(faviconURI);
+        final HttpURLConnection connection = connectTo(faviconURI);
 
         // Was the response a failure?
-        int status = connection.getResponseCode();
+        final int status = connection.getResponseCode();
 
         // Handle HTTP status codes requesting a redirect.
         if (status >= 300 && status < 400) {
             final String newURI = connection.getHeaderField("Location");
 
             // Handle mad web servers.
             try {
                 if (newURI == null || newURI.equals(faviconURI)) {
@@ -163,17 +164,17 @@ public class IconDownloader implements I
             return null;
         }
 
         return connection;
     }
 
     @VisibleForTesting
     HttpURLConnection connectTo(String faviconURI) throws URISyntaxException, IOException {
-        HttpURLConnection connection = (HttpURLConnection) ProxySelector.openConnectionWithProxy(
+        final HttpURLConnection connection = (HttpURLConnection) ProxySelector.openConnectionWithProxy(
                 new URI(faviconURI));
 
         connection.setRequestProperty("User-Agent", GeckoAppShell.getGeckoInterface().getDefaultUAString());
 
         // We implemented or own way of following redirects back when this code was using HttpClient.
         // Nowadays we should let HttpUrlConnection do the work - assuming that it doesn't follow
         // redirects in loops forever.
         connection.setInstanceFollowRedirects(false);
@@ -191,28 +192,28 @@ public class IconDownloader implements I
      * @param contentLength as reported by the server (or -1)
      * @return A LoadFaviconResult containing the bitmap(s) extracted from the downloaded file, or
      *         null if no or corrupt data were received.
      * @throws IOException If attempts to fully read the stream result in such an exception, such as
      *                     in the event of a transient connection failure.
      */
     private LoadFaviconResult decodeImageFromResponse(Context context, InputStream stream, int contentLength) throws IOException {
         // This may not be provided, but if it is, it's useful.
-        int bufferSize;
+        final int bufferSize;
         if (contentLength > 0) {
             // The size was reported and sane, so let's use that.
             // Integer overflow should not be a problem for Favicon sizes...
             bufferSize = contentLength + 1;
         } else {
             // No declared size, so guess and reallocate later if it turns out to be too small.
             bufferSize = DEFAULT_FAVICON_BUFFER_SIZE_BYTES;
         }
 
         // Read the InputStream into a byte[].
-        IOUtils.ConsumedInputStream result = IOUtils.readFully(stream, bufferSize);
+        final IOUtils.ConsumedInputStream result = IOUtils.readFully(stream, bufferSize);
         if (result == null) {
             return null;
         }
 
         // Having downloaded the image, decode it.
         return FaviconDecoder.decodeFavicon(context, result.getData(), 0, result.consumedLength);
     }
 }
--- a/mobile/android/base/java/org/mozilla/gecko/icons/loader/LegacyLoader.java
+++ b/mobile/android/base/java/org/mozilla/gecko/icons/loader/LegacyLoader.java
@@ -20,33 +20,46 @@ import org.mozilla.gecko.icons.IconRespo
  * for a couple of releases and be removed afterwards.
  *
  * When updating to an app version with the new loaders our initial storage won't have any data so
  * we need to continue loading from the database storage until the new storage has a good set of data.
  */
 public class LegacyLoader implements IconLoader {
     @Override
     public IconResponse load(IconRequest request) {
+        if (!request.shouldSkipNetwork()) {
+            // If we are allowed to load from the network for this request then just ommit the legacy
+            // loader and fetch a fresh new icon.
+            return null;
+        }
+
         if (request.shouldSkipDisk()) {
             return null;
         }
 
+        if (request.getIconCount() > 1) {
+            // There are still other icon URLs to try. Let's try to load from the legacy loader only
+            // if there's one icon left and the other loads have failed. We will ignore the icon URL
+            // anyways and try to receive the legacy icon URL from the database.
+            return null;
+        }
+
         final Bitmap bitmap = loadBitmapFromDatabase(request);
 
         if (bitmap == null) {
             return null;
         }
 
         return IconResponse.create(bitmap);
     }
 
     /* package-private */ Bitmap loadBitmapFromDatabase(IconRequest request) {
         final Context context = request.getContext();
         final ContentResolver contentResolver = context.getContentResolver();
-        final BrowserDB db = GeckoProfile.get(request.getContext()).getDB();
+        final BrowserDB db = GeckoProfile.get(context).getDB();
 
         // We ask the database for the favicon URL and ignore the icon URL in the request object:
         // As we are not updating the database anymore the icon might be stored under a different URL.
         final String legacyFaviconUrl = db.getFaviconURLFromPageURL(contentResolver, request.getPageUrl());
         if (legacyFaviconUrl == null) {
             // No URL -> Nothing to load.
             return null;
         }
--- a/mobile/android/base/java/org/mozilla/gecko/icons/preparation/FilterMimeTypes.java
+++ b/mobile/android/base/java/org/mozilla/gecko/icons/preparation/FilterMimeTypes.java
@@ -23,17 +23,17 @@ public class FilterMimeTypes implements 
 
         while (iterator.hasNext()) {
             final IconDescriptor descriptor = iterator.next();
             final String mimeType = descriptor.getMimeType();
 
             if (TextUtils.isEmpty(mimeType)) {
                 // We do not have a MIME type for this icon, so we cannot know in advance if we are able
                 // to decode it. Let's just continue.
-                return;
+                continue;
             }
 
             if (!IconsHelper.canDecodeType(mimeType)) {
                 iterator.remove();
             }
         }
     }
 }
--- a/mobile/android/base/java/org/mozilla/gecko/icons/processing/ColorProcessor.java
+++ b/mobile/android/base/java/org/mozilla/gecko/icons/processing/ColorProcessor.java
@@ -1,25 +1,30 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.icons.processing;
 
-import org.mozilla.gecko.gfx.BitmapUtils;
+import android.support.v7.graphics.Palette;
+
 import org.mozilla.gecko.icons.IconRequest;
 import org.mozilla.gecko.icons.IconResponse;
 
 /**
  * Processor implementation to extract the dominant color from the icon and attach it to the icon
  * response object.
  */
 public class ColorProcessor implements Processor {
+    private static final int DEFAULT_COLOR = 0; // 0 == No color
+
     @Override
     public void process(IconRequest request, IconResponse response) {
         if (response.hasColor()) {
             return;
         }
 
-        response.updateColor(BitmapUtils.getDominantColor(response.getBitmap()));
+        final Palette palette = Palette.from(response.getBitmap()).generate();
+
+        response.updateColor(palette.getVibrantColor(DEFAULT_COLOR));
     }
 }
--- a/mobile/android/base/java/org/mozilla/gecko/widget/FaviconView.java
+++ b/mobile/android/base/java/org/mozilla/gecko/widget/FaviconView.java
@@ -60,29 +60,33 @@ public class FaviconView extends ImageVi
     private final float mBackgroundCornerRadius;
 
     // Type of the border whose value is defined in attrs.xml .
     private final boolean isDominantBorderEnabled;
 
     // boolean switch for overriding scaletype, whose value is defined in attrs.xml .
     private final boolean isOverrideScaleTypeEnabled;
 
+    // boolean switch for disabling rounded corners, value defined in attrs.xml .
+    private final boolean areRoundCornersEnabled;
+
     // Initializing the static paints.
     static {
         sBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
         sBackgroundPaint.setStyle(Paint.Style.FILL);
     }
 
     public FaviconView(Context context, AttributeSet attrs) {
         super(context, attrs);
         TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.FaviconView, 0, 0);
 
         try {
             isDominantBorderEnabled = a.getBoolean(R.styleable.FaviconView_dominantBorderEnabled, true);
             isOverrideScaleTypeEnabled = a.getBoolean(R.styleable.FaviconView_overrideScaleType, true);
+            areRoundCornersEnabled = a.getBoolean(R.styleable.FaviconView_enableRoundCorners, true);
         } finally {
             a.recycle();
         }
 
         if (isOverrideScaleTypeEnabled) {
             setScaleType(ImageView.ScaleType.CENTER);
         }
 
@@ -110,17 +114,21 @@ public class FaviconView extends ImageVi
         formatImage();
     }
 
     @Override
     public void onDraw(Canvas canvas) {
         if (isDominantBorderEnabled) {
             sBackgroundPaint.setColor(mDominantColor & 0x7FFFFFFF);
 
-            canvas.drawRoundRect(mBackgroundRect, mBackgroundCornerRadius, mBackgroundCornerRadius, sBackgroundPaint);
+            if (areRoundCornersEnabled) {
+                canvas.drawRoundRect(mBackgroundRect, mBackgroundCornerRadius, mBackgroundCornerRadius, sBackgroundPaint);
+            } else {
+                canvas.drawRect(mBackgroundRect, sBackgroundPaint);
+            }
         }
 
         super.onDraw(canvas);
     }
 
     /**
      * Formats the image for display, if the prerequisite data are available. Upscales tiny Favicons to
      * normal sized ones, replaces null bitmaps with the default Favicon, and fills all remaining space
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -75,16 +75,21 @@ if CONFIG['ANDROID_DESIGN_AAR']:
 if CONFIG['ANDROID_RECYCLERVIEW_V7_AAR']:
     ANDROID_EXTRA_PACKAGES += ['android.support.v7.recyclerview']
     ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['ANDROID_RECYCLERVIEW_V7_AAR_RES']]
     resjar.generated_sources += ['android/support/v7/recyclerview/R.java']
 if CONFIG['ANDROID_CUSTOMTABS_AAR']:
     ANDROID_EXTRA_PACKAGES += ['android.support.customtabs']
     ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['ANDROID_CUSTOMTABS_AAR_RES']]
     resjar.generated_sources += ['android/support/customtabs/R.java']
+if CONFIG['ANDROID_PALETTE_V7_AAR']:
+    ANDROID_EXTRA_PACKAGES += ['android.support.v7.palette']
+    ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['ANDROID_PALETTE_V7_AAR_RES']]
+    resjar.generated_sources += ['android/support/v7/palette/R.java']
+
 
 resjar.javac_flags += ['-Xlint:all']
 
 mgjar = add_java_jar('gecko-mozglue')
 mgjar.sources += [geckoview_source_dir + 'java/org/mozilla/gecko/' + x for x in [
     'mozglue/ByteBufferInputStream.java',
     'mozglue/DirectBufferAllocator.java',
     'mozglue/GeckoLoader.java',
@@ -876,16 +881,17 @@ if CONFIG['MOZ_INSTALL_TRACKING']:
         ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['ANDROID_PLAY_SERVICES_BASEMENT_AAR_RES']]
         resjar.generated_sources += ['com/google/android/gms/R.java']
 
 gbjar.extra_jars += [CONFIG['ANDROID_APPCOMPAT_V7_AAR_LIB']]
 gbjar.extra_jars += [CONFIG['ANDROID_CARDVIEW_V7_AAR_LIB']]
 gbjar.extra_jars += [CONFIG['ANDROID_DESIGN_AAR_LIB']]
 gbjar.extra_jars += [CONFIG['ANDROID_RECYCLERVIEW_V7_AAR_LIB']]
 gbjar.extra_jars += [CONFIG['ANDROID_CUSTOMTABS_AAR_LIB']]
+gbjar.extra_jars += [CONFIG['ANDROID_PALETTE_V7_AAR_LIB']]
 
 gbjar.javac_flags += ['-Xlint:all,-deprecation,-fallthrough', '-J-Xmx512m', '-J-Xms128m']
 
 # gecko-thirdparty is a good place to put small independent libraries
 gtjar = add_java_jar('gecko-thirdparty')
 gtjar.sources += [ thirdparty_source_dir + f for f in [
     'com/jakewharton/disklrucache/DiskLruCache.java',
     'com/jakewharton/disklrucache/StrictLineReader.java',
--- a/mobile/android/base/resources/layout/activity_stream_topsites_card.xml
+++ b/mobile/android/base/resources/layout/activity_stream_topsites_card.xml
@@ -1,29 +1,31 @@
 <?xml version="1.0" encoding="utf-8"?>
 <org.mozilla.gecko.widget.FilledCardView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="wrap_content"
     android:layout_height="115dp"
+    xmlns:gecko="http://schemas.android.com/apk/res-auto"
     android:layout_margin="1dp">
 
     <RelativeLayout
         android:layout_width="match_parent"
         android:layout_height="match_parent">
 
         <org.mozilla.gecko.widget.FaviconView
             android:id="@+id/favicon"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_above="@+id/title"
             android:layout_alignParentTop="true"
             android:layout_centerHorizontal="true"
             android:layout_gravity="center"
-            tools:background="@drawable/favicon_globe"/>
+            tools:background="@drawable/favicon_globe"
+            gecko:enableRoundCorners="false"/>
 
         <TextView
             android:id="@+id/title"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_alignParentBottom="true"
             android:layout_alignParentLeft="true"
             android:layout_alignParentStart="true"
--- a/mobile/android/base/resources/values/attrs.xml
+++ b/mobile/android/base/resources/values/attrs.xml
@@ -168,16 +168,17 @@
 
     <declare-styleable name="EllipsisTextView">
         <attr name="ellipsizeAtLine" format="integer"/>
     </declare-styleable>
 
     <declare-styleable name="FaviconView">
         <attr name="dominantBorderEnabled" format="boolean" />
         <attr name="overrideScaleType" format="boolean" />
+        <attr name="enableRoundCorners" format="boolean"/>
     </declare-styleable>
 
     <declare-styleable name="OverlayDialogButton">
         <attr name="drawable" format="reference" />
         <attr name="enabledText" format="string" />
         <attr name="disabledText" format="string" />
     </declare-styleable>
 
--- a/mobile/android/config/tooltool-manifests/android/releng.manifest
+++ b/mobile/android/config/tooltool-manifests/android/releng.manifest
@@ -67,33 +67,24 @@
 "algorithm": "sha512",
 "visibility": "public",
 "filename": "gradle-dist.tar.xz",
 "unpack": true,
 "digest": "990edc0e4039dbe5f77790ef59dc0d58faebbb8c82ee497615c7991eec99fe4668d0ab05508c48664b635ff6c0cfd4272db464ae1efaa548e471ab451fe0944f",
 "size": 51955340
 },
 {
-"version": "rustc 1.8.0 (db2939409 2016-04-11)",
-"size": 123218320,
-"digest": "7afcd7b39c4d5277db6b28951602aff4c698102ba45d3d811b353ca7446074beceebf03a2a529e323af19d73db4acbe96ec2bdad44def2e218ed36f55e82cab2",
+"version": "rustc 1.11.0 (9b21dcd6a 2016-08-15) repack",
+"size": 97552448,
+"digest": "272438c1692a46998dc44f22bd1fe18da1be7af2e7fdcf6c52709366c80c73e30637f0c3864f45c64edf46ce6a905538c14b2313983be973f9f29a2f191ec89b",
 "algorithm": "sha512",
 "filename": "rustc.tar.xz",
 "unpack": true
 },
 {
-"version": "rust stdlib repack 1.8.0 (db2939409 2016-04-11)",
-"size": 17874585,
-"visibility": "public",
-"digest": "bea72d352a70411240d95c7ab33e97d85be9cf0548807968dc721b14d816c70f4fd77a0ca1b43b851772ec06c0c1395b474ad342d506f8987fdbb070393c81f6",
-"algorithm": "sha512",
-"filename": "rust-std-lib.tar.bz2",
-"unpack": true
-},
-{
 "algorithm": "sha512",
 "visibility": "public",
 "filename": "dotgradle.tar.xz",
 "unpack": true,
 "digest": "9f082ccd71ad18991eb71fcad355c6990f50a72a09ab9b79696521485656083a72faf5a8d4714de9c4b901ee2319b6786a51964846bb7075061642a8505501c2",
 "size": 512
 }
 ]
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/icons/loader/TestLegacyLoader.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/icons/loader/TestLegacyLoader.java
@@ -10,38 +10,58 @@ import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mozilla.gecko.background.testhelpers.TestRunner;
 import org.mozilla.gecko.icons.IconDescriptor;
 import org.mozilla.gecko.icons.IconRequest;
 import org.mozilla.gecko.icons.IconResponse;
 import org.mozilla.gecko.icons.Icons;
 import org.robolectric.RuntimeEnvironment;
 
+import java.util.Iterator;
+
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
 @RunWith(TestRunner.class)
 public class TestLegacyLoader {
     private static final String TEST_PAGE_URL = "http://www.mozilla.org";
     private static final String TEST_ICON_URL = "https://example.org/favicon.ico";
+    private static final String TEST_ICON_URL_2 = "https://example.com/page/favicon.ico";
+    private static final String TEST_ICON_URL_3 = "https://example.net/icon/favicon.ico";
 
     @Test
-    public void testDatabaseIsQueriesForNormalRequests() {
+    public void testDatabaseIsQueriesForNormalRequestsWithNetworkSkipped() {
+        final IconRequest request = Icons.with(RuntimeEnvironment.application)
+                .pageUrl(TEST_PAGE_URL)
+                .icon(IconDescriptor.createGenericIcon(TEST_ICON_URL))
+                .skipNetwork()
+                .build();
+
+        final LegacyLoader loader = spy(new LegacyLoader());
+        final IconResponse response = loader.load(request);
+
+        verify(loader).loadBitmapFromDatabase(request);
+
+        Assert.assertNull(response);
+    }
+
+    @Test
+    public void testNothingIsLoadedIfNetworkIsNotSkipped() {
         final IconRequest request = Icons.with(RuntimeEnvironment.application)
                 .pageUrl(TEST_PAGE_URL)
                 .icon(IconDescriptor.createGenericIcon(TEST_ICON_URL))
                 .build();
 
         final LegacyLoader loader = spy(new LegacyLoader());
         final IconResponse response = loader.load(request);
 
-        verify(loader).loadBitmapFromDatabase(request);
+        verify(loader, never()).loadBitmapFromDatabase(request);
 
         Assert.assertNull(response);
     }
 
     @Test
     public void testNothingIsLoadedIfDiskSHouldBeSkipped() {
         final IconRequest request = Icons.with(RuntimeEnvironment.application)
                 .pageUrl(TEST_PAGE_URL)
@@ -57,21 +77,55 @@ public class TestLegacyLoader {
         Assert.assertNull(response);
     }
 
     @Test
     public void testLoadedBitmapIsReturnedAsResponse() {
         final IconRequest request = Icons.with(RuntimeEnvironment.application)
                 .pageUrl(TEST_PAGE_URL)
                 .icon(IconDescriptor.createGenericIcon(TEST_ICON_URL))
+                .skipNetwork()
                 .build();
 
         final Bitmap bitmap = mock(Bitmap.class);
 
         final LegacyLoader loader = spy(new LegacyLoader());
         doReturn(bitmap).when(loader).loadBitmapFromDatabase(request);
 
         final IconResponse response = loader.load(request);
 
         Assert.assertNotNull(response);
         Assert.assertEquals(bitmap, response.getBitmap());
     }
+
+    @Test
+    public void testLoaderOnlyLoadsIfThereIsOneIconLeft() {
+        final IconRequest request = Icons.with(RuntimeEnvironment.application)
+                .pageUrl(TEST_PAGE_URL)
+                .icon(IconDescriptor.createGenericIcon(TEST_ICON_URL))
+                .icon(IconDescriptor.createGenericIcon(TEST_ICON_URL_2))
+                .icon(IconDescriptor.createGenericIcon(TEST_ICON_URL_3))
+                .skipNetwork()
+                .build();
+
+        final LegacyLoader loader = spy(new LegacyLoader());
+        doReturn(mock(Bitmap.class)).when(loader).loadBitmapFromDatabase(request);
+
+        // First load doesn't load an icon.
+        Assert.assertNull(loader.load(request));
+
+        // Second load doesn't load an icon.
+        removeFirstIcon(request);
+        Assert.assertNull(loader.load(request));
+
+        // Now only one icon is left and a response will be returned.
+        removeFirstIcon(request);
+        Assert.assertNotNull(loader.load(request));
+    }
+
+    private void removeFirstIcon(IconRequest request) {
+        final Iterator<IconDescriptor> iterator = request.getIconIterator();
+        if (iterator.hasNext()) {
+            iterator.next();
+            iterator.remove();
+        }
+    }
 }
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/icons/processing/TestColorProcessor.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/icons/processing/TestColorProcessor.java
@@ -28,17 +28,17 @@ public class TestColorProcessor {
 
         Assert.assertFalse(response.hasColor());
         Assert.assertEquals(0, response.getColor());
 
         final Processor processor = new ColorProcessor();
         processor.process(null, response);
 
         Assert.assertTrue(response.hasColor());
-        Assert.assertEquals(Color.RED, response.getColor());
+        Assert.assertEquals(0xFFF80000, response.getColor());
     }
 
     private Bitmap createRedBitmapMock() {
         final Bitmap bitmap = mock(Bitmap.class);
 
         doReturn(1).when(bitmap).getWidth();
         doReturn(1).when(bitmap).getHeight();
 
--- a/moz.configure
+++ b/moz.configure
@@ -240,16 +240,27 @@ def possible_makes(make, host):
     if host.kernel == 'WINNT':
         candidates.extend(('make', 'gmake'))
     else:
         candidates.extend(('gmake', 'make'))
     return candidates
 
 check_prog('GMAKE', possible_makes)
 
+# tup detection
+# ==============================================================
+@depends(build_backends)
+def tup_progs(build_backends):
+    for backend in build_backends:
+        if 'Tup' in backend:
+            return ['tup']
+    return None
+
+tup = check_prog('TUP', tup_progs)
+
 # Miscellaneous programs
 # ==============================================================
 check_prog('DOXYGEN', ('doxygen',), allow_missing=True)
 check_prog('XARGS', ('xargs',))
 
 @depends(target)
 def extra_programs(target):
     if target.kernel == 'Darwin':
--- a/netwerk/base/Predictor.cpp
+++ b/netwerk/base/Predictor.cpp
@@ -1627,17 +1627,17 @@ Predictor::LearnInternal(PredictorLearnR
 
         MOZ_ASSERT(keysToOperateOn.Length() == valuesToOperateOn.Length());
         for (size_t i = 0; i < keysToOperateOn.Length(); ++i) {
           const char *key = keysToOperateOn[i].BeginReading();
           const char *value = valuesToOperateOn[i].BeginReading();
 
           nsCOMPtr<nsIURI> uri;
           uint32_t hitCount, lastHit, flags;
-          if (!ParseMetaDataEntry(key, value, getter_AddRefs(uri), hitCount, lastHit, flags)) {
+          if (!ParseMetaDataEntry(nullptr, value, nullptr, hitCount, lastHit, flags)) {
             // This failed, get rid of it so we don't waste space
             entry->SetMetaDataElement(key, nullptr);
             continue;
           }
           UpdateRollingLoadCount(entry, flags, key, hitCount, lastHit);
         }
       } else {
         PREDICTOR_LOG(("    nothing to do for toplevel"));
@@ -1667,35 +1667,31 @@ Predictor::SpaceCleaner::OnMetaDataEleme
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!IsURIMetadataElement(key)) {
     // This isn't a bit of metadata we care about
     return NS_OK;
   }
 
-  nsCOMPtr<nsIURI> parsedURI;
   uint32_t hitCount, lastHit, flags;
-  bool ok = mPredictor->ParseMetaDataEntry(key, value,
-                                           getter_AddRefs(parsedURI),
+  bool ok = mPredictor->ParseMetaDataEntry(nullptr, value, nullptr,
                                            hitCount, lastHit, flags);
 
   if (!ok) {
     // Couldn't parse this one, just get rid of it
     nsCString nsKey;
     nsKey.AssignASCII(key);
     mLongKeysToDelete.AppendElement(nsKey);
     return NS_OK;
   }
 
-  nsCString uri;
-  nsresult rv = parsedURI->GetAsciiSpec(uri);
+  nsCString uri(key + (sizeof(META_DATA_PREFIX) - 1));
   uint32_t uriLength = uri.Length();
-  if (NS_SUCCEEDED(rv) &&
-      uriLength > mPredictor->mMaxURILength) {
+  if (uriLength > mPredictor->mMaxURILength) {
     // Default to getting rid of URIs that are too long and were put in before
     // we had our limit on URI length, in order to free up some space.
     nsCString nsKey;
     nsKey.AssignASCII(key);
     mLongKeysToDelete.AppendElement(nsKey);
     return NS_OK;
   }
 
--- a/netwerk/protocol/http/Http2Compression.cpp
+++ b/netwerk/protocol/http/Http2Compression.cpp
@@ -275,16 +275,17 @@ nvFIFO::operator[] (size_t index) const
     return static_cast<nvPair *>(mTable.ObjectAt(index - gStaticHeaders->GetSize()));
   }
   return static_cast<nvPair *>(gStaticHeaders->ObjectAt(index));
 }
 
 Http2BaseCompressor::Http2BaseCompressor()
   : mOutput(nullptr)
   , mMaxBuffer(kDefaultMaxBuffer)
+  , mMaxBufferSetting(kDefaultMaxBuffer)
 {
   mDynamicReporter = new HpackDynamicTableReporter(this);
   RegisterStrongMemoryReporter(mDynamicReporter);
 }
 
 Http2BaseCompressor::~Http2BaseCompressor()
 {
   UnregisterStrongMemoryReporter(mDynamicReporter);
@@ -337,16 +338,33 @@ Http2BaseCompressor::DumpState()
   for (i = 0; i < length; ++i) {
     const nvPair *pair = mHeaderTable[i];
     // NWGH - make this <= staticLength
     LOG(("%sindex %u: %s %s", i < staticLength ? "static " : "", i,
          pair->mName.get(), pair->mValue.get()));
   }
 }
 
+void
+Http2BaseCompressor::SetMaxBufferSizeInternal(uint32_t maxBufferSize)
+{
+  MOZ_ASSERT(maxBufferSize <= mMaxBufferSetting);
+
+  uint32_t removedCount = 0;
+
+  LOG(("Http2BaseCompressor::SetMaxBufferSizeInternal %u called", maxBufferSize));
+
+  while (mHeaderTable.VariableLength() && (mHeaderTable.ByteCount() > maxBufferSize)) {
+    mHeaderTable.RemoveElement();
+    ++removedCount;
+  }
+
+  mMaxBuffer = maxBufferSize;
+}
+
 nsresult
 Http2Decompressor::DecodeHeaderBlock(const uint8_t *data, uint32_t datalen,
                                      nsACString &output, bool isPush)
 {
   mOffset = 0;
   mData = data;
   mDataLen = datalen;
   mOutput = &output;
@@ -944,24 +962,40 @@ Http2Decompressor::DoLiteralNeverIndexed
 }
 
 nsresult
 Http2Decompressor::DoContextUpdate()
 {
   // This starts with 001 bit pattern
   MOZ_ASSERT((mData[mOffset] & 0xE0) == 0x20);
 
-  // Getting here means we have to adjust the max table size
+  // Getting here means we have to adjust the max table size, because the
+  // compressor on the other end has signaled to us through HPACK (not H2)
+  // that it's using a size different from the currently-negotiated size.
+  // This change could either come about because we've sent a
+  // SETTINGS_HEADER_TABLE_SIZE, or because the encoder has decided that
+  // the current negotiated size doesn't fit its needs (for whatever reason)
+  // and so it needs to change it (either up to the max allowed by our SETTING,
+  // or down to some value below that)
   uint32_t newMaxSize;
   nsresult rv = DecodeInteger(5, newMaxSize);
   LOG(("Http2Decompressor::DoContextUpdate new maximum size %u", newMaxSize));
   if (NS_FAILED(rv)) {
     return rv;
   }
-  return mCompressor->SetMaxBufferSizeInternal(newMaxSize);
+
+  if (newMaxSize > mMaxBufferSetting) {
+    // This is fatal to the session - peer is trying to use a table larger
+    // than we have made available.
+    return NS_ERROR_FAILURE;
+  }
+
+  SetMaxBufferSizeInternal(newMaxSize);
+
+  return NS_OK;
 }
 
 /////////////////////////////////////////////////////////////////
 
 nsresult
 Http2Compressor::EncodeHeaderBlock(const nsCString &nvInput,
                                    const nsACString &method, const nsACString &path,
                                    const nsACString &host, const nsACString &scheme,
@@ -1354,31 +1388,10 @@ Http2Compressor::SetMaxBufferSize(uint32
   if (!mBufferSizeChangeWaiting) {
     mBufferSizeChangeWaiting = true;
     mLowestBufferSizeWaiting = maxBufferSize;
   } else if (maxBufferSize < mLowestBufferSizeWaiting) {
     mLowestBufferSizeWaiting = maxBufferSize;
   }
 }
 
-nsresult
-Http2Compressor::SetMaxBufferSizeInternal(uint32_t maxBufferSize)
-{
-  if (maxBufferSize > mMaxBufferSetting) {
-    return NS_ERROR_FAILURE;
-  }
-
-  uint32_t removedCount = 0;
-
-  LOG(("Http2Compressor::SetMaxBufferSizeInternal %u called", maxBufferSize));
-
-  while (mHeaderTable.VariableLength() && (mHeaderTable.ByteCount() > maxBufferSize)) {
-    mHeaderTable.RemoveElement();
-    ++removedCount;
-  }
-
-  mMaxBuffer = maxBufferSize;
-
-  return NS_OK;
-}
-
 } // namespace net
 } // namespace mozilla
--- a/netwerk/protocol/http/Http2Compression.h
+++ b/netwerk/protocol/http/Http2Compression.h
@@ -67,21 +67,23 @@ public:
   size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
 protected:
   const static uint32_t kDefaultMaxBuffer = 4096;
 
   virtual void ClearHeaderTable();
   virtual void MakeRoom(uint32_t amount, const char *direction);
   virtual void DumpState();
+  virtual void SetMaxBufferSizeInternal(uint32_t maxBufferSize);
 
   nsACString *mOutput;
   nvFIFO mHeaderTable;
 
   uint32_t mMaxBuffer;
+  uint32_t mMaxBufferSetting;
 
 private:
   RefPtr<HpackDynamicTableReporter> mDynamicReporter;
 };
 
 class Http2Compressor;
 
 class Http2Decompressor final : public Http2BaseCompressor
@@ -94,17 +96,16 @@ public:
   nsresult DecodeHeaderBlock(const uint8_t *data, uint32_t datalen,
                              nsACString &output, bool isPush);
 
   void GetStatus(nsACString &hdr) { hdr = mHeaderStatus; }
   void GetHost(nsACString &hdr) { hdr = mHeaderHost; }
   void GetScheme(nsACString &hdr) { hdr = mHeaderScheme; }
   void GetPath(nsACString &hdr) { hdr = mHeaderPath; }
   void GetMethod(nsACString &hdr) { hdr = mHeaderMethod; }
-  void SetCompressor(Http2Compressor *compressor) { mCompressor = compressor; }
 
 private:
   nsresult DoIndexed();
   nsresult DoLiteralWithoutIndex();
   nsresult DoLiteralWithIncremental();
   nsresult DoLiteralInternal(nsACString &, nsACString &, uint32_t);
   nsresult DoLiteralNeverIndexed();
   nsresult DoContextUpdate();
@@ -117,18 +118,16 @@ private:
   nsresult CopyStringFromInput(uint32_t index, nsACString &val);
   uint8_t ExtractByte(uint8_t bitsLeft, uint32_t &bytesConsumed);
   nsresult CopyHuffmanStringFromInput(uint32_t index, nsACString &val);
   nsresult DecodeHuffmanCharacter(const HuffmanIncomingTable *table, uint8_t &c,
                                   uint32_t &bytesConsumed, uint8_t &bitsLeft);
   nsresult DecodeFinalHuffmanCharacter(const HuffmanIncomingTable *table,
                                        uint8_t &c, uint8_t &bitsLeft);
 
-  Http2Compressor *mCompressor;
-
   nsCString mHeaderStatus;
   nsCString mHeaderHost;
   nsCString mHeaderScheme;
   nsCString mHeaderPath;
   nsCString mHeaderMethod;
 
   // state variables when DecodeBlock() is on the stack
   uint32_t mOffset;
@@ -138,33 +137,31 @@ private:
   bool mIsPush;
 };
 
 
 class Http2Compressor final : public Http2BaseCompressor
 {
 public:
   Http2Compressor() : mParsedContentLength(-1),
-                      mMaxBufferSetting(kDefaultMaxBuffer),
                       mBufferSizeChangeWaiting(false),
                       mLowestBufferSizeWaiting(0)
   { };
   virtual ~Http2Compressor() { }
 
   // HTTP/1 formatted header block as input - HTTP/2 formatted
   // header block as output
   nsresult EncodeHeaderBlock(const nsCString &nvInput,
                              const nsACString &method, const nsACString &path,
                              const nsACString &host, const nsACString &scheme,
                              bool connectForm, nsACString &output);
 
   int64_t GetParsedContentLength() { return mParsedContentLength; } // -1 on not found
 
   void SetMaxBufferSize(uint32_t maxBufferSize);
-  nsresult SetMaxBufferSizeInternal(uint32_t maxBufferSize);
 
 private:
   enum outputCode {
     kNeverIndexedLiteral,
     kPlainLiteral,
     kIndexedLiteral,
     kIndex
   };
@@ -173,17 +170,16 @@ private:
                 const class nvPair *pair, uint32_t index);
   void EncodeInteger(uint32_t prefixLen, uint32_t val);
   void ProcessHeader(const nvPair inputPair, bool noLocalIndex,
                      bool neverIndex);
   void HuffmanAppend(const nsCString &value);
   void EncodeTableSizeChange(uint32_t newMaxSize);
 
   int64_t mParsedContentLength;
-  uint32_t mMaxBufferSetting;
   bool mBufferSizeChangeWaiting;
   uint32_t mLowestBufferSizeWaiting;
 };
 
 } // namespace net
 } // namespace mozilla
 
 #endif // mozilla_net_Http2Compression_Internal_h
--- a/netwerk/protocol/http/Http2Session.cpp
+++ b/netwerk/protocol/http/Http2Session.cpp
@@ -118,17 +118,16 @@ Http2Session::Http2Session(nsISocketTran
   static uint64_t sSerial;
   mSerial = ++sSerial;
 
   LOG3(("Http2Session::Http2Session %p serial=0x%X\n", this, mSerial));
 
   mInputFrameBuffer = MakeUnique<char[]>(mInputFrameBufferSize);
   mOutputQueueBuffer = MakeUnique<char[]>(mOutputQueueSize);
   mDecompressBuffer.SetCapacity(kDefaultBufferSize);
-  mDecompressor.SetCompressor(&mCompressor);
 
   mPushAllowance = gHttpHandler->SpdyPushAllowance();
   mInitialRwin = std::max(gHttpHandler->SpdyPullAllowance(), mPushAllowance);
   mMaxConcurrent = gHttpHandler->DefaultSpdyConcurrent();
   mSendingChunkSize = gHttpHandler->SpdySendingChunkSize();
   SendHello();
 
   mLastDataReadEpoch = mLastReadEpoch;
--- a/python/mozbuild/mozbuild/backend/__init__.py
+++ b/python/mozbuild/mozbuild/backend/__init__.py
@@ -5,16 +5,17 @@
 backends = {
     'AndroidEclipse': 'mozbuild.backend.android_eclipse',
     'ChromeMap': 'mozbuild.codecoverage.chrome_map',
     'CompileDB': 'mozbuild.compilation.database',
     'CppEclipse': 'mozbuild.backend.cpp_eclipse',
     'FasterMake': 'mozbuild.backend.fastermake',
     'FasterMake+RecursiveMake': None,
     'RecursiveMake': 'mozbuild.backend.recursivemake',
+    'Tup': 'mozbuild.backend.tup',
     'VisualStudio': 'mozbuild.backend.visualstudio',
 }
 
 
 def get_backend_class(name):
     if '+' in name:
         from mozbuild.backend.base import HybridBackend
         return HybridBackend(*(get_backend_class(name)
--- a/python/mozbuild/mozbuild/backend/base.py
+++ b/python/mozbuild/mozbuild/backend/base.py
@@ -189,16 +189,25 @@ class BuildBackend(LoggingMixin):
 
         This is the main method used by child classes to react to build
         metadata.
         """
 
     def consume_finished(self):
         """Called when consume() has completed handling all objects."""
 
+    def build(self, config, output, jobs, verbose):
+        """Called when 'mach build' is executed.
+
+        This should return the status value of a subprocess, where 0 denotes
+        success and any other value is an error code. A return value of None
+        indicates that the default 'make -f client.mk' should run.
+        """
+        return None
+
     @contextmanager
     def _write_file(self, path=None, fh=None, mode='rU'):
         """Context manager to write a file.
 
         This is a glorified wrapper around FileAvoidWrite with integration to
         update the summary data on this instance.
 
         Example usage:
new file mode 100644
--- /dev/null
+++ b/python/mozbuild/mozbuild/backend/tup.py
@@ -0,0 +1,203 @@
+# 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/.
+
+from __future__ import absolute_import, unicode_literals
+
+import os
+
+import mozpack.path as mozpath
+from mozbuild.base import MozbuildObject
+from mozbuild.backend.base import PartialBackend, HybridBackend
+from mozbuild.backend.recursivemake import RecursiveMakeBackend
+from mozbuild.shellutil import quote as shell_quote
+
+from .common import CommonBackend
+from ..frontend.data import (
+    ContextDerived,
+)
+from ..util import (
+    FileAvoidWrite,
+)
+
+
+class BackendTupfile(object):
+    """Represents a generated Tupfile.
+    """
+
+    def __init__(self, srcdir, objdir, environment, topsrcdir, topobjdir):
+        self.topsrcdir = topsrcdir
+        self.srcdir = srcdir
+        self.objdir = objdir
+        self.relobjdir = mozpath.relpath(objdir, topobjdir)
+        self.environment = environment
+        self.name = mozpath.join(objdir, 'Tupfile')
+        self.rules_included = False
+
+        self.fh = FileAvoidWrite(self.name, capture_diff=True)
+        self.fh.write('# THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT.\n')
+        self.fh.write('\n')
+
+    def write(self, buf):
+        self.fh.write(buf)
+
+    def include_rules(self):
+        if not self.rules_included:
+            self.write('include_rules\n')
+            self.rules_included = True
+
+    def rule(self, cmd, inputs=None, outputs=None, display=None, extra_outputs=None):
+        inputs = inputs or []
+        outputs = outputs or []
+        self.include_rules()
+        self.write(': %(inputs)s |> %(display)s%(cmd)s |> %(outputs)s%(extra_outputs)s\n' % {
+            'inputs': ' '.join(inputs),
+            'display': '^ %s^ ' % display if display else '',
+            'cmd': ' '.join(cmd),
+            'outputs': ' '.join(outputs),
+            'extra_outputs': ' | ' + ' '.join(extra_outputs) if extra_outputs else '',
+        })
+
+    def close(self):
+        return self.fh.close()
+
+    @property
+    def diff(self):
+        return self.fh.diff
+
+
+class TupOnly(CommonBackend, PartialBackend):
+    """Backend that generates Tupfiles for the tup build system.
+    """
+
+    def _init(self):
+        CommonBackend._init(self)
+
+        self._backend_files = {}
+        self._cmd = MozbuildObject.from_environment()
+
+    def _get_backend_file(self, relativedir):
+        objdir = mozpath.join(self.environment.topobjdir, relativedir)
+        srcdir = mozpath.join(self.environment.topsrcdir, relativedir)
+        if objdir not in self._backend_files:
+            self._backend_files[objdir] = \
+                    BackendTupfile(srcdir, objdir, self.environment,
+                                   self.environment.topsrcdir, self.environment.topobjdir)
+        return self._backend_files[objdir]
+
+    def consume_object(self, obj):
+        """Write out build files necessary to build with tup."""
+
+        if not isinstance(obj, ContextDerived):
+            return False
+
+        consumed = CommonBackend.consume_object(self, obj)
+
+        # Even if CommonBackend acknowledged the object, we still need to let
+        # the RecursiveMake backend also handle these objects.
+        if consumed:
+            return False
+
+        return True
+
+    def consume_finished(self):
+        CommonBackend.consume_finished(self)
+
+        for objdir, backend_file in sorted(self._backend_files.items()):
+            with self._write_file(fh=backend_file):
+                pass
+
+        with self._write_file(mozpath.join(self.environment.topobjdir, 'Tuprules.tup')) as fh:
+            acdefines = [name for name in self.environment.defines
+                if not name in self.environment.non_global_defines]
+            acdefines_flags = ' '.join(['-D%s=%s' % (name,
+                shell_quote(self.environment.defines[name]))
+                for name in sorted(acdefines)])
+            fh.write('MOZ_OBJ_ROOT = $(TUP_CWD)\n')
+            fh.write('DIST = $(MOZ_OBJ_ROOT)/dist\n')
+            fh.write('ACDEFINES = %s\n' % acdefines_flags)
+            fh.write('topsrcdir = $(MOZ_OBJ_ROOT)/%s\n' % (
+                os.path.relpath(self.environment.topsrcdir, self.environment.topobjdir)
+            ))
+            fh.write('PYTHON = $(MOZ_OBJ_ROOT)/_virtualenv/bin/python -B\n')
+            fh.write('PYTHON_PATH = $(PYTHON) $(topsrcdir)/config/pythonpath.py\n')
+            fh.write('PLY_INCLUDE = -I$(topsrcdir)/other-licenses/ply\n')
+            fh.write('IDL_PARSER_DIR = $(topsrcdir)/xpcom/idl-parser\n')
+            fh.write('IDL_PARSER_CACHE_DIR = $(MOZ_OBJ_ROOT)/xpcom/idl-parser\n')
+
+        # Run 'tup init' if necessary.
+        if not os.path.exists(mozpath.join(self.environment.topsrcdir, ".tup")):
+            tup = self.environment.substs.get('TUP', 'tup')
+            self._cmd.run_process(cwd=self.environment.topsrcdir, log_name='tup', args=[tup, 'init'])
+
+    def _handle_idl_manager(self, manager):
+
+        # TODO: This should come from GENERATED_FILES, and can be removed once
+        # those are implemented.
+        backend_file = self._get_backend_file('xpcom/idl-parser')
+        backend_file.rule(
+            display='python header.py -> [%o]',
+            cmd=[
+                '$(PYTHON_PATH)',
+                '$(PLY_INCLUDE)',
+                '$(topsrcdir)/xpcom/idl-parser/xpidl/header.py',
+            ],
+            outputs=['xpidlyacc.py', 'xpidllex.py'],
+        )
+
+        backend_file = self._get_backend_file('xpcom/xpidl')
+
+        # These are used by mach/mixin/process.py to determine the current
+        # shell.
+        for var in ('SHELL', 'MOZILLABUILD', 'COMSPEC'):
+            backend_file.write('export %s\n' % var)
+
+        for module, data in sorted(manager.modules.iteritems()):
+            dest, idls = data
+            cmd = [
+                '$(PYTHON_PATH)',
+                '$(PLY_INCLUDE)',
+                '-I$(IDL_PARSER_DIR)',
+                '-I$(IDL_PARSER_CACHE_DIR)',
+                '$(topsrcdir)/python/mozbuild/mozbuild/action/xpidl-process.py',
+                '--cache-dir', '$(MOZ_OBJ_ROOT)/xpcom/idl-parser',
+                '$(DIST)/idl',
+                '$(DIST)/include',
+                '$(MOZ_OBJ_ROOT)/%s/components' % dest,
+                module,
+            ]
+            cmd.extend(sorted(idls))
+
+            outputs = ['$(MOZ_OBJ_ROOT)/%s/components/%s.xpt' % (dest, module)]
+            outputs.extend(['$(MOZ_OBJ_ROOT)/dist/include/%s.h' % f for f in sorted(idls)])
+            backend_file.rule(
+                inputs=[
+                    '$(MOZ_OBJ_ROOT)/xpcom/idl-parser/xpidllex.py',
+                    '$(MOZ_OBJ_ROOT)/xpcom/idl-parser/xpidlyacc.py',
+                ],
+                display='XPIDL %s' % module,
+                cmd=cmd,
+                outputs=outputs,
+            )
+
+    def _handle_ipdl_sources(self, ipdl_dir, sorted_ipdl_sources,
+                             unified_ipdl_cppsrcs_mapping):
+        # TODO: This isn't implemented yet in the tup backend, but it is called
+        # by the CommonBackend.
+        pass
+
+    def _handle_webidl_build(self, bindings_dir, unified_source_mapping,
+                             webidls, expected_build_output_files,
+                             global_define_files):
+        # TODO: This isn't implemented yet in the tup backend, but it is called
+        # by the CommonBackend.
+        pass
+
+
+class TupBackend(HybridBackend(TupOnly, RecursiveMakeBackend)):
+    def build(self, config, output, jobs, verbose):
+        status = config._run_make(directory=self.environment.topobjdir, target='tup',
+                                  line_handler=output.on_line, log=False, print_directory=False,
+                                  ensure_exit_code=False, num_jobs=jobs, silent=not verbose,
+                                  append_env={b'NO_BUILDSTATUS_MESSAGES': b'1'})
+        return status
--- a/python/mozbuild/mozbuild/mach_commands.py
+++ b/python/mozbuild/mozbuild/mach_commands.py
@@ -31,21 +31,20 @@ from mozbuild.base import (
     MachCommandBase,
     MachCommandConditions as conditions,
     MozbuildObject,
     MozconfigFindException,
     MozconfigLoadException,
     ObjdirMismatchException,
 )
 
-from mozpack.manifests import (
-    InstallManifest,
+from mozbuild.backend import (
+    backends,
+    get_backend_class,
 )
-
-from mozbuild.backend import backends
 from mozbuild.shellutil import quote as shell_quote
 
 
 BUILD_WHAT_HELP = '''
 What to build. Can be a top-level make target or a relative directory. If
 multiple options are provided, they will be built serially. Takes dependency
 information from `topsrcdir/build/dumbmake-dependencies` to build additional
 targets as needed. BUILDING ONLY PARTS OF THE TREE CAN RESULT IN BAD TREE
@@ -408,20 +407,49 @@ class Build(MachCommandBase):
                     status = self._run_make(directory=make_dir, target=make_target,
                         line_handler=output.on_line, log=False, print_directory=False,
                         ensure_exit_code=False, num_jobs=jobs, silent=not verbose,
                         append_env={b'NO_BUILDSTATUS_MESSAGES': b'1'})
 
                     if status != 0:
                         break
             else:
-                status = self._run_make(srcdir=True, filename='client.mk',
-                    line_handler=output.on_line, log=False, print_directory=False,
-                    allow_parallel=False, ensure_exit_code=False, num_jobs=jobs,
-                    silent=not verbose)
+                # Try to call the default backend's build() method. This will
+                # run configure to determine BUILD_BACKENDS if it hasn't run
+                # yet.
+                config = None
+                try:
+                    config = self.config_environment
+                except Exception:
+                    config_rc = self.configure()
+                    if config_rc != 0:
+                        return config_rc
+
+                    # Even if configure runs successfully, we may have trouble
+                    # getting the config_environment for some builds, such as
+                    # OSX Universal builds. These have to go through client.mk
+                    # regardless.
+                    try:
+                        config = self.config_environment
+                    except Exception:
+                        pass
+
+                if config:
+                    active_backend = config.substs.get('BUILD_BACKENDS', [None])[0]
+                    if active_backend:
+                        backend_cls = get_backend_class(active_backend)(config)
+                        status = backend_cls.build(self, output, jobs, verbose)
+
+                # If the backend doesn't specify a build() method, then just
+                # call client.mk directly.
+                if status is None:
+                    status = self._run_make(srcdir=True, filename='client.mk',
+                        line_handler=output.on_line, log=False, print_directory=False,
+                        allow_parallel=False, ensure_exit_code=False, num_jobs=jobs,
+                        silent=not verbose)
 
                 self.log(logging.WARNING, 'warning_summary',
                     {'count': len(monitor.warnings_database)},
                     '{count} compiler warnings present.')
 
             monitor.finish(record_usage=status==0)
 
         high_finder, finder_percent = monitor.have_high_finder_usage()
--- a/services/sync/modules/engines/bookmarks.js
+++ b/services/sync/modules/engines/bookmarks.js
@@ -777,17 +777,17 @@ BookmarksStore.prototype = {
   _frecencyCols: ["frecency"],
 
   GUIDForId: function GUIDForId(id) {
     let special = BookmarkSpecialIds.specialGUIDForId(id);
     if (special)
       return special;
 
     return Async.promiseSpinningly(
-      PlacesSyncUtils.bookmarks.ensureGuidForId(id));
+      PlacesUtils.promiseItemGuid(id));
   },
 
   // noCreate is provided as an optional argument to prevent the creation of
   // non-existent special records, such as "mobile".
   idForGUID: function idForGUID(guid, noCreate) {
     // guid might be a String object rather than a string.
     guid = guid.toString();
 
new file mode 100644
--- /dev/null
+++ b/testing/marionette/harness/.flake8
@@ -0,0 +1,3 @@
+[flake8]
+max-line-length = 99
+exclude = __init__.py,disti/*,build/*,session/*,marionette/runner/mixins/*, marionette/tests/*
--- a/testing/marionette/harness/docs/conf.py
+++ b/testing/marionette/harness/docs/conf.py
@@ -6,94 +6,95 @@
 # This file is execfile()d with the current directory set to its containing dir.
 #
 # Note that not all possible configuration values are present in this
 # autogenerated file.
 #
 # All configuration values have a default; values that are commented out
 # serve to show the default.
 
-import sys, os
+import os
+import sys
 
 # If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
 # documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.insert(0, os.path.abspath('.'))
+# sys.path.insert(0, os.path.abspath('.'))
 
 here = os.path.dirname(os.path.abspath(__file__))
 parent = os.path.dirname(here)
 sys.path.insert(0, parent)
 
 # -- General configuration -----------------------------------------------------
 
 # If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
+# needs_sphinx = '1.0'
 
 # Add any Sphinx extension module names here, as strings. They can be extensions
 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
 extensions = ['sphinx.ext.autodoc']
 
 # Add any paths that contain templates here, relative to this directory.
 templates_path = ['_templates']
 
 # The suffix of source filenames.
 source_suffix = '.rst'
 
 # The encoding of source files.
-#source_encoding = 'utf-8-sig'
+# source_encoding = 'utf-8-sig'
 
 # The master toctree document.
 master_doc = 'index'
 
 # General information about the project.
 project = u'Marionette Python Client'
 copyright = u'2013, Mozilla Automation and Tools and individual contributors'
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
 # built documents.
 #
 # The short X.Y version.
-#version = '0'
+# version = '0'
 # The full version, including alpha/beta/rc tags.
-#release = '0'
+# release = '0'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
-#language = None
+# language = None
 
 # There are two options for replacing |today|: either, you set today to some
 # non-false value, then it is used:
-#today = ''
+# today = ''
 # Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
+# today_fmt = '%B %d, %Y'
 
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
 exclude_patterns = ['_build']
 
 # The reST default role (used for this markup: `text`) to use for all documents.
-#default_role = None
+# default_role = None
 
 # If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
+# add_function_parentheses = True
 
 # If true, the current module name will be prepended to all description
 # unit titles (such as .. function::).
-#add_module_names = True
+# add_module_names = True
 
 # If true, sectionauthor and moduleauthor directives will be shown in the
 # output. They are ignored by default.
-#show_authors = False
+# show_authors = False
 
 # The name of the Pygments (syntax highlighting) style to use.
 pygments_style = 'sphinx'
 
 # A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
+# modindex_common_prefix = []
 
 
 # -- Options for HTML output ---------------------------------------------------
 
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
 
 html_theme = 'default'
@@ -107,152 +108,152 @@ if not on_rtd:
         html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
     except ImportError:
         pass
 
 
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
 # documentation.
-#html_theme_options = {}
+# html_theme_options = {}
 
 # Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
+# html_theme_path = []
 
 # The name for this set of Sphinx documents.  If None, it defaults to
 # "<project> v<release> documentation".
-#html_title = None
+# html_title = None
 
 # A shorter title for the navigation bar.  Default is the same as html_title.
-#html_short_title = None
+# html_short_title = None
 
 # The name of an image file (relative to this directory) to place at the top
 # of the sidebar.
-#html_logo = None
+# html_logo = None
 
 # The name of an image file (within the static path) to use as favicon of the
 # docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
 # pixels large.
-#html_favicon = None
+# html_favicon = None
 
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
-#html_static_path = ['_static']
+# html_static_path = ['_static']
 
 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
 # using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
+# html_last_updated_fmt = '%b %d, %Y'
 
 # If true, SmartyPants will be used to convert quotes and dashes to
 # typographically correct entities.
-#html_use_smartypants = True
+# html_use_smartypants = True
 
 # Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
+# html_sidebars = {}
 
 # Additional templates that should be rendered to pages, maps page names to
 # template names.
-#html_additional_pages = {}
+# html_additional_pages = {}
 
 # If false, no module index is generated.
-#html_domain_indices = True
+# html_domain_indices = True
 
 # If false, no index is generated.
-#html_use_index = True
+# html_use_index = True
 
 # If true, the index is split into individual pages for each letter.
-#html_split_index = False
+# html_split_index = False
 
 # If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
+# html_show_sourcelink = True
 
 # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
+# html_show_sphinx = True
 
 # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
+# html_show_copyright = True
 
 # If true, an OpenSearch description file will be output, and all pages will
 # contain a <link> tag referring to it.  The value of this option must be the
 # base URL from which the finished HTML is served.
-#html_use_opensearch = ''
+# html_use_opensearch = ''
 
 # This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
+# html_file_suffix = None
 
 # Output file base name for HTML help builder.
 htmlhelp_basename = 'MarionettePythonClientdoc'
 
 
 # -- Options for LaTeX output --------------------------------------------------
 
 latex_elements = {
-# The paper size ('letterpaper' or 'a4paper').
-#'papersize': 'letterpaper',
+    # The paper size ('letterpaper' or 'a4paper').
+    # 'papersize': 'letterpaper',
 
-# The font size ('10pt', '11pt' or '12pt').
-#'pointsize': '10pt',
+    # The font size ('10pt', '11pt' or '12pt').
+    # 'pointsize': '10pt',
 
-# Additional stuff for the LaTeX preamble.
-#'preamble': '',
+    # Additional stuff for the LaTeX preamble.
+    # 'preamble': '',
 }
 
 # Grouping the document tree into LaTeX files. List of tuples
 # (source start file, target name, title, author, documentclass [howto/manual]).
 latex_documents = [
-  ('index', 'MarionettePythonClient.tex', u'Marionette Python Client Documentation',
-   u'Mozilla Automation and Tools team', 'manual'),
+    ('index', 'MarionettePythonClient.tex', u'Marionette Python Client Documentation',
+     u'Mozilla Automation and Tools team', 'manual'),
 ]
 
 # The name of an image file (relative to this directory) to place at the top of
 # the title page.
-#latex_logo = None
+# latex_logo = None
 
 # For "manual" documents, if this is true, then toplevel headings are parts,
 # not chapters.
-#latex_use_parts = False
+# latex_use_parts = False
 
 # If true, show page references after internal links.
-#latex_show_pagerefs = False
+# latex_show_pagerefs = False
 
 # If true, show URL addresses after external links.
-#latex_show_urls = False
+# latex_show_urls = False
 
 # Documents to append as an appendix to all manuals.
-#latex_appendices = []
+# latex_appendices = []
 
 # If false, no module index is generated.
-#latex_domain_indices = True
+# latex_domain_indices = True
 
 
 # -- Options for manual page output --------------------------------------------
 
 # One entry per manual page. List of tuples
 # (source start file, name, description, authors, manual section).
 man_pages = [
     ('index', 'marionettepythonclient', u'Marionette Python Client Documentation',
      [u'Mozilla Automation and Tools team'], 1)
 ]
 
 # If true, show URL addresses after external links.
-#man_show_urls = False
+# man_show_urls = False
 
 
 # -- Options for Texinfo output ------------------------------------------------
 
 # Grouping the document tree into Texinfo files. List of tuples
 # (source start file, target name, title, author,
 #  dir menu entry, description, category)
 texinfo_documents = [
-  ('index', 'MarionettePythonClient', u'Marionette Python Client Documentation',
-   u'Mozilla Automation and Tools team', 'MarionettePythonClient', 'One line description of project.',
-   'Miscellaneous'),
+    ('index', 'MarionettePythonClient', 'Marionette Python Client Documentation',
+     'Mozilla Automation and Tools team', 'MarionettePythonClient',
+     'One line description of project.', 'Miscellaneous'),
 ]
 
 # Documents to append as an appendix to all manuals.
-#texinfo_appendices = []
+# texinfo_appendices = []
 
 # If false, no module index is generated.
-#texinfo_domain_indices = True
+# texinfo_domain_indices = True
 
 # How to display URL addresses: 'footnote', 'no', or 'inline'.
-#texinfo_show_urls = 'footnote'
+# texinfo_show_urls = 'footnote'
--- a/testing/marionette/harness/marionette/marionette_test.py
+++ b/testing/marionette/harness/marionette/marionette_test.py
@@ -11,128 +11,131 @@ import socket
 import time
 import types
 import unittest
 import weakref
 import warnings
 
 
 from marionette_driver.errors import (
-        MarionetteException, TimeoutException,
-        JavascriptException, NoSuchElementException, NoSuchWindowException,
-        StaleElementException, ScriptTimeoutException, ElementNotVisibleException,
-        NoSuchFrameException, InvalidElementStateException, NoAlertPresentException,
-        InvalidCookieDomainException, UnableToSetCookieException, InvalidSelectorException,
-        MoveTargetOutOfBoundsException
-        )
-from marionette_driver.marionette import Marionette
-from marionette_driver.wait import Wait
-from marionette_driver.expected import element_present, element_not_present
+    MarionetteException,
+    ScriptTimeoutException,
+    TimeoutException,
+)
 from mozlog import get_default_logger
 
 
 class SkipTest(Exception):
     """
     Raise this exception in a test to skip it.
 
     Usually you can use TestResult.skip() or one of the skipping decorators
     instead of raising this directly.
     """
+
     pass
 
+
 class _ExpectedFailure(Exception):
     """
     Raise this when a test is expected to fail.
 
     This is an implementation detail.
     """
 
     def __init__(self, exc_info):
         super(_ExpectedFailure, self).__init__()
         self.exc_info = exc_info
 
+
 class _UnexpectedSuccess(Exception):
-    """
-    The test was supposed to fail, but it didn't!
-    """
+    """The test was supposed to fail, but it didn't."""
+
     pass
 
+
 def skip(reason):
     """Unconditionally skip a test."""
     def decorator(test_item):
         if not isinstance(test_item, (type, types.ClassType)):
             @functools.wraps(test_item)
             def skip_wrapper(*args, **kwargs):
                 raise SkipTest(reason)
             test_item = skip_wrapper
 
         test_item.__unittest_skip__ = True
         test_item.__unittest_skip_why__ = reason
         return test_item
     return decorator
 
+
 def expectedFailure(func):
     @functools.wraps(func)
     def wrapper(*args, **kwargs):
         try:
             func(*args, **kwargs)
         except Exception:
             raise _ExpectedFailure(sys.exc_info())
         raise _UnexpectedSuccess
     return wrapper
 
+
 def skip_if_chrome(target):
     def wrapper(self, *args, **kwargs):
         if self.marionette._send_message("getContext", key="value") == "chrome":
             raise SkipTest("skipping test in chrome context")
         return target(self, *args, **kwargs)
     return wrapper
 
+
 def skip_if_desktop(target):
     def wrapper(self, *args, **kwargs):
         if self.marionette.session_capabilities.get('browserName') == 'firefox':
             raise SkipTest('skipping due to desktop')
         return target(self, *args, **kwargs)
     return wrapper
 
+
 def skip_if_mobile(target):
     def wrapper(self, *args, **kwargs):
         if self.marionette.session_capabilities.get('browserName') == 'fennec':
             raise SkipTest('skipping due to fennec')
         return target(self, *args, **kwargs)
     return wrapper
 
+
 def skip_if_e10s(target):
     def wrapper(self, *args, **kwargs):
         with self.marionette.using_context('chrome'):
             multi_process_browser = self.marionette.execute_script("""
             try {
               return Services.appinfo.browserTabsRemoteAutostart;
             } catch (e) {
               return false;
             }""")
 
         if multi_process_browser:
             raise SkipTest('skipping due to e10s')
         return target(self, *args, **kwargs)
     return wrapper
 
+
 def skip_unless_protocol(predicate):
-    """Given a predicate passed the current protocol level, skip the
-    test if the predicate does not match."""
+    """Skip the test if the predicate does not match the current protocol level."""
     def decorator(test_item):
         @functools.wraps(test_item)
         def skip_wrapper(self):
             level = self.marionette.client.protocol
             if not predicate(level):
                 raise SkipTest('skipping because protocol level is %s' % level)
             return test_item(self)
         return skip_wrapper
     return decorator
 
+
 def skip_unless_browser_pref(pref, predicate=bool):
     """
     Skip a test based on the value of a browser preference.
 
     :param pref: the preference name
     :param predicate: a function that should return false to skip the test.
                       The function takes one parameter, the preference value.
                       Defaults to the python built-in bool function.
@@ -154,18 +157,19 @@ def skip_unless_browser_pref(pref, predi
             if value is None:
                 self.fail("No such browser preference: %r" % pref)
             if not predicate(value):
                 raise SkipTest("browser preference %r: %r" % (pref, value))
             return target(self, *args, **kwargs)
         return wrapped
     return wrapper
 
+
 def parameterized(func_suffix, *args, **kwargs):
-    """
+    r"""
     A decorator that can generate methods given a base method and some data.
 
     **func_suffix** is used as a suffix for the new created method and must be
     unique given a base method. if **func_suffix** countains characters that
     are not allowed in normal python function name, these characters will be
     replaced with "_".
 
     This decorator can be used more than once on a single base method. The class
@@ -190,19 +194,21 @@ def parameterized(func_suffix, *args, **
     """
     def wrapped(func):
         if not hasattr(func, 'metaparameters'):
             func.metaparameters = []
         func.metaparameters.append((func_suffix, args, kwargs))
         return func
     return wrapped
 
+
 def with_parameters(parameters):
     """
     A decorator that can generate methods given a base method and some data.
+
     Acts like :func:`parameterized`, but define all methods in one call.
 
     Example::
 
       # This example will generate two methods:
       #
       # - MyTestCase.test_it_1
       # - MyTestCase.test_it_2
@@ -218,50 +224,58 @@ def with_parameters(parameters):
     :param parameters: list of tuples (**func_suffix**, **args**, **kwargs**)
                        defining parameters like in :func:`todo`.
     """
     def wrapped(func):
         func.metaparameters = parameters
         return func
     return wrapped
 
+
 def wraps_parameterized(func, func_suffix, args, kwargs):
-    """Internal: for MetaParameterized"""
+    """Internal: for MetaParameterized."""
     def wrapper(self):
         return func(self, *args, **kwargs)
     wrapper.__name__ = func.__name__ + '_' + str(func_suffix)
     wrapper.__doc__ = '[%s] %s' % (func_suffix, func.__doc__)
     return wrapper
 
+
 class MetaParameterized(type):
     """
-    A metaclass that allow a class to use decorators like :func:`parameterized`
+    A metaclass that allow a class to use decorators.
+
+    It can be used like :func:`parameterized`
     or :func:`with_parameters` to generate new methods.
     """
+
     RE_ESCAPE_BAD_CHARS = re.compile(r'[\.\(\) -/]')
+
     def __new__(cls, name, bases, attrs):
         for k, v in attrs.items():
             if callable(v) and hasattr(v, 'metaparameters'):
                 for func_suffix, args, kwargs in v.metaparameters:
                     func_suffix = cls.RE_ESCAPE_BAD_CHARS.sub('_', func_suffix)
                     wrapper = wraps_parameterized(v, func_suffix, args, kwargs)
                     if wrapper.__name__ in attrs:
                         raise KeyError("%s is already a defined method on %s" %
-                                        (wrapper.__name__, name))
+                                       (wrapper.__name__, name))
                     attrs[wrapper.__name__] = wrapper
                 del attrs[k]
 
         return type.__new__(cls, name, bases, attrs)
 
+
 class JSTest:
     head_js_re = re.compile(r"MARIONETTE_HEAD_JS(\s*)=(\s*)['|\"](.*?)['|\"];")
     context_re = re.compile(r"MARIONETTE_CONTEXT(\s*)=(\s*)['|\"](.*?)['|\"];")
     timeout_re = re.compile(r"MARIONETTE_TIMEOUT(\s*)=(\s*)(\d+);")
     inactivity_timeout_re = re.compile(r"MARIONETTE_INACTIVITY_TIMEOUT(\s*)=(\s*)(\d+);")
 
+
 class CommonTestCase(unittest.TestCase):
 
     __metaclass__ = MetaParameterized
     match_re = None
     failureException = AssertionError
     pydebugger = None
 
     def __init__(self, methodName, **kwargs):
@@ -305,21 +319,21 @@ class CommonTestCase(unittest.TestCase):
             startTestRun = getattr(result, 'startTestRun', None)
             if startTestRun is not None:
                 startTestRun()
 
         result.startTest(self)
 
         testMethod = getattr(self, self._testMethodName)
         if (getattr(self.__class__, "__unittest_skip__", False) or
-            getattr(testMethod, "__unittest_skip__", False)):
+                getattr(testMethod, "__unittest_skip__", False)):
             # If the class or method was skipped.
             try:
-                skip_why = (getattr(self.__class__, '__unittest_skip_why__', '')
-                            or getattr(testMethod, '__unittest_skip_why__', ''))
+                skip_why = (getattr(self.__class__, '__unittest_skip_why__', '') or
+                            getattr(testMethod, '__unittest_skip_why__', ''))
                 self._addSkip(result, skip_why)
             finally:
                 result.stopTest(self)
             self.stop_time = time.time()
             return
         try:
             success = False
             try:
@@ -356,17 +370,18 @@ class CommonTestCase(unittest.TestCase):
                     raise
                 except _ExpectedFailure as e:
                     expected_failure(result, e.exc_info)
                 except _UnexpectedSuccess:
                     addUnexpectedSuccess = getattr(result, 'addUnexpectedSuccess', None)
                     if addUnexpectedSuccess is not None:
                         addUnexpectedSuccess(self)
                     else:
-                        warnings.warn("TestResult has no addUnexpectedSuccess method, reporting as failures",
+                        warnings.warn("TestResult has no addUnexpectedSuccess method, "
+                                      "reporting as failures",
                                       RuntimeWarning)
                         result.addFailure(self, sys.exc_info())
                 except SkipTest as e:
                     self._addSkip(result, str(e))
                 except:
                     self._enter_pm()
                     result.addError(self, sys.exc_info())
                 else:
@@ -397,31 +412,28 @@ class CommonTestCase(unittest.TestCase):
             result.stopTest(self)
             if orig_result is None:
                 stopTestRun = getattr(result, 'stopTestRun', None)
                 if stopTestRun is not None:
                     stopTestRun()
 
     @classmethod
     def match(cls, filename):
-        """
-        Determines if the specified filename should be handled by this
-        test class; this is done by looking for a match for the filename
-        using cls.match_re.
+        """Determine if the specified filename should be handled by this test class.
+
+        This is done by looking for a match for the filename using cls.match_re.
         """
         if not cls.match_re:
             return False
         m = cls.match_re.match(filename)
         return m is not None
 
     @classmethod
     def add_tests_to_suite(cls, mod_name, filepath, suite, testloader, marionette, testvars):
-        """
-        Adds all the tests in the specified file to the specified suite.
-        """
+        """Add all the tests in the specified file to the specified suite."""
         raise NotImplementedError
 
     @property
     def test_name(self):
         if hasattr(self, 'jsFile'):
             return os.path.basename(self.jsFile)
         else:
             return '%s.py %s.%s' % (self.__class__.__module__,
@@ -475,59 +487,59 @@ class CommonTestCase(unittest.TestCase):
                         self.marionette.client.close()
                     except socket.error:
                         pass
         self.marionette = None
 
     def setup_SpecialPowers_observer(self):
         self.marionette.set_context("chrome")
         self.marionette.execute_script("""
-            let SECURITY_PREF = "security.turn_off_all_security_so_that_viruses_can_take_over_this_computer";
-            Components.utils.import("resource://gre/modules/Preferences.jsm");
-            Preferences.set(SECURITY_PREF, true);
+let SECURITY_PREF = "security.turn_off_all_security_so_that_viruses_can_take_over_this_computer";
+Components.utils.import("resource://gre/modules/Preferences.jsm");
+Preferences.set(SECURITY_PREF, true);
 
-            if (!testUtils.hasOwnProperty("specialPowersObserver")) {
-              let loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                .getService(Components.interfaces.mozIJSSubScriptLoader);
-              loader.loadSubScript("chrome://specialpowers/content/SpecialPowersObserver.jsm",
-                testUtils);
-              testUtils.specialPowersObserver = new testUtils.SpecialPowersObserver();
-              testUtils.specialPowersObserver.init();
-            }
-            """)
+if (!testUtils.hasOwnProperty("specialPowersObserver")) {
+  let loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+    .getService(Components.interfaces.mozIJSSubScriptLoader);
+  loader.loadSubScript("chrome://specialpowers/content/SpecialPowersObserver.jsm",
+    testUtils);
+  testUtils.specialPowersObserver = new testUtils.SpecialPowersObserver();
+  testUtils.specialPowersObserver.init();
+}
+""")
 
     def run_js_test(self, filename, marionette=None):
-        '''
-        Run a JavaScript test file and collect its set of assertions
-        into the current test's results.
+        """Run a JavaScript test file.
+
+        It collects its set of assertions into the current test's results.
 
         :param filename: The path to the JavaScript test file to execute.
                          May be relative to the current script.
         :param marionette: The Marionette object in which to execute the test.
                            Defaults to self.marionette.
-        '''
+        """
         marionette = marionette or self.marionette
         if not os.path.isabs(filename):
             # Find the caller's filename and make the path relative to that.
             caller_file = sys._getframe(1).f_globals.get('__file__', '')
             caller_file = os.path.abspath(caller_file)
             filename = os.path.join(os.path.dirname(caller_file), filename)
         self.assert_(os.path.exists(filename),
                      'Script "%s" must exist' % filename)
         original_test_name = self.marionette.test_name
         self.marionette.test_name = os.path.basename(filename)
         f = open(filename, 'r')
         js = f.read()
         args = []
 
-        head_js = JSTest.head_js_re.search(js);
+        head_js = JSTest.head_js_re.search(js)
         if head_js:
             head_js = head_js.group(3)
             head = open(os.path.join(os.path.dirname(filename), head_js), 'r')
-            js = head.read() + js;
+            js = head.read() + js
 
         context = JSTest.context_re.search(js)
         if context:
             context = context.group(3)
         else:
             context = 'content'
 
         if 'SpecialPowers' in js:
@@ -563,78 +575,81 @@ class CommonTestCase(unittest.TestCase):
         try:
             results = marionette.execute_js_script(
                 js,
                 args,
                 inactivity_timeout=inactivity_timeout,
                 filename=os.path.basename(filename)
             )
 
-            self.assertTrue(not 'timeout' in filename,
+            self.assertTrue('timeout' not in filename,
                             'expected timeout not triggered')
 
             if 'fail' in filename:
                 self.assertTrue(len(results['failures']) > 0,
                                 "expected test failures didn't occur")
             else:
                 for failure in results['failures']:
                     diag = "" if failure.get('diag') is None else failure['diag']
-                    name = "got false, expected true" if failure.get('name') is None else failure['name']
+                    name = ("got false, expected true" if failure.get('name') is None else
+                            failure['name'])
                     self.logger.test_status(self.test_name, name, 'FAIL',
                                             message=diag)
                 for failure in results['expectedFailures']:
                     diag = "" if failure.get('diag') is None else failure['diag']
-                    name = "got false, expected false" if failure.get('name') is None else failure['name']
+                    name = ("got false, expected false" if failure.get('name') is None else
+                            failure['name'])
                     self.logger.test_status(self.test_name, name, 'FAIL',
                                             expected='FAIL', message=diag)
                 for failure in results['unexpectedSuccesses']:
                     diag = "" if failure.get('diag') is None else failure['diag']
-                    name = "got true, expected false" if failure.get('name') is None else failure['name']
+                    name = ("got true, expected false" if failure.get('name') is None else
+                            failure['name'])
                     self.logger.test_status(self.test_name, name, 'PASS',
                                             expected='FAIL', message=diag)
                 self.assertEqual(0, len(results['failures']),
                                  '%d tests failed' % len(results['failures']))
                 if len(results['unexpectedSuccesses']) > 0:
                     raise _UnexpectedSuccess('')
                 if len(results['expectedFailures']) > 0:
                     raise _ExpectedFailure((AssertionError, AssertionError(''), None))
 
-            self.assertTrue(results['passed']
-                            + len(results['failures'])
-                            + len(results['expectedFailures'])
-                            + len(results['unexpectedSuccesses']) > 0,
+            self.assertTrue(results['passed'] +
+                            len(results['failures']) +
+                            len(results['expectedFailures']) +
+                            len(results['unexpectedSuccesses']) > 0,
                             'no tests run')
 
         except ScriptTimeoutException:
             if 'timeout' in filename:
                 # expected exception
                 pass
             else:
                 self.loglines = marionette.get_logs()
                 raise
         self.marionette.test_name = original_test_name
 
 
-
 class MarionetteTestCase(CommonTestCase):
 
     match_re = re.compile(r"test_(.*)\.py$")
 
     def __init__(self, marionette_weakref, methodName='runTest',
                  filepath='', **kwargs):
         self._marionette_weakref = marionette_weakref
         self.marionette = None
         self.methodName = methodName
         self.filepath = filepath
         self.testvars = kwargs.pop('testvars', None)
         self.test_container = kwargs.pop('test_container', None)
         CommonTestCase.__init__(self, methodName, **kwargs)
 
     @classmethod
-    def add_tests_to_suite(cls, mod_name, filepath, suite, testloader, marionette, testvars, **kwargs):
+    def add_tests_to_suite(cls, mod_name, filepath, suite, testloader, marionette,
+                           testvars, **kwargs):
         # since we use imp.load_source to load test modules, if a module
         # is loaded with the same name as another one the module would just be
         # reloaded.
         #
         # We may end up by finding too many test in a module then since
         # reload() only update the module dict (so old keys are still there!)
         # see https://docs.python.org/2/library/functions.html#reload
         #
@@ -644,17 +659,17 @@ class MarionetteTestCase(CommonTestCase)
         if mod_name in sys.modules:
             del sys.modules[mod_name]
 
         test_mod = imp.load_source(mod_name, filepath)
 
         for name in dir(test_mod):
             obj = getattr(test_mod, name)
             if (isinstance(obj, (type, types.ClassType)) and
-                issubclass(obj, unittest.TestCase)):
+                    issubclass(obj, unittest.TestCase)):
                 testnames = testloader.getTestCaseNames(obj)
                 for testname in testnames:
                     suite.addTest(obj(weakref.ref(marionette),
                                   methodName=testname,
                                   filepath=filepath,
                                   testvars=testvars,
                                   **kwargs))
 
@@ -691,30 +706,32 @@ class MarionetteTestCase(CommonTestCase)
         while time.time() < timeout:
             value = method(self.marionette)
             if value:
                 return value
             time.sleep(0.5)
         else:
             raise TimeoutException("wait_for_condition timed out")
 
+
 class MarionetteJSTestCase(CommonTestCase):
 
     match_re = re.compile(r"test_(.*)\.js$")
 
     def __init__(self, marionette_weakref, methodName='runTest', jsFile=None, **kwargs):
         assert(jsFile)
         self.jsFile = jsFile
         self._marionette_weakref = marionette_weakref
         self.marionette = None
         self.test_container = kwargs.pop('test_container', None)
         CommonTestCase.__init__(self, methodName)
 
     @classmethod
-    def add_tests_to_suite(cls, mod_name, filepath, suite, testloader, marionette, testvars, **kwargs):
+    def add_tests_to_suite(cls, mod_name, filepath, suite, testloader, marionette,
+                           testvars, **kwargs):
         suite.addTest(cls(weakref.ref(marionette), jsFile=filepath, **kwargs))
 
     def runTest(self):
         if self.marionette.session is None:
             self.marionette.start_session()
         self.marionette.execute_script(
             "log('TEST-START: %s');" % self.jsFile.replace('\\', '\\\\'),
             sandbox="simpletest")
--- a/testing/marionette/harness/marionette/runner/base.py
+++ b/testing/marionette/harness/marionette/runner/base.py
@@ -28,19 +28,19 @@ from moztest.adapters.unit import Struct
 from moztest.results import TestResultCollection, TestResult, relevant_line
 import mozversion
 
 import httpd
 
 
 here = os.path.abspath(os.path.dirname(__file__))
 
+
 def update_mozinfo(path=None):
-    """walk up directories to find mozinfo.json and update the info"""
-
+    """Walk up directories to find mozinfo.json and update the info."""
     path = path or here
     dirs = set()
     while path != os.path.expanduser('~'):
         if path in dirs:
             break
         dirs.add(path)
         path = os.path.split(path)[0]
 
@@ -53,26 +53,27 @@ class MarionetteTest(TestResult):
     def test_name(self):
         if self.test_class is not None:
             return '%s.py %s.%s' % (self.test_class.split('.')[0],
                                     self.test_class,
                                     self.name)
         else:
             return self.name
 
+
 class MarionetteTestResult(StructuredTestResult, TestResultCollection):
 
     resultClass = MarionetteTest
 
     def __init__(self, *args, **kwargs):
         self.marionette = kwargs.pop('marionette')
         TestResultCollection.__init__(self, 'MarionetteTest')
         self.passed = 0
         self.testsRun = 0
-        self.result_modifiers = [] # used by mixins to modify the result
+        self.result_modifiers = []  # used by mixins to modify the result
         StructuredTestResult.__init__(self, *args, **kwargs)
 
     @property
     def skipped(self):
         return [t for t in self if t.result == 'SKIPPED']
 
     @skipped.setter
     def skipped(self, value):
@@ -128,33 +129,36 @@ class MarionetteTestResult(StructuredTes
 
         name = str(test).split()[0]
         test_class = get_class(test)
         if hasattr(test, 'jsFile'):
             name = os.path.basename(test.jsFile)
             test_class = None
 
         t = self.resultClass(name=name, test_class=test_class,
-                       time_start=test.start_time, result_expected=result_expected,
-                       context=context, **kwargs)
+                             time_start=test.start_time, result_expected=result_expected,
+                             context=context, **kwargs)
         # call any registered result modifiers
         for modifier in self.result_modifiers:
-            result_expected, result_actual, output, context = modifier(t, result_expected, result_actual, output, context)
+            result_expected, result_actual, output, context = modifier(
+                t, result_expected, result_actual, output, context)
         t.finish(result_actual,
                  time_end=time.time() if test.start_time else 0,
                  reason=relevant_line(output),
                  output=output)
         self.append(t)
 
     def addError(self, test, err):
-        self.add_test_result(test, output=self._exc_info_to_string(err, test), result_actual='ERROR')
+        self.add_test_result(test, output=self._exc_info_to_string(err, test),
+                             result_actual='ERROR')
         super(MarionetteTestResult, self).addError(test, err)
 
     def addFailure(self, test, err):
-        self.add_test_result(test, output=self._exc_info_to_string(err, test), result_actual='UNEXPECTED-FAIL')
+        self.add_test_result(test, output=self._exc_info_to_string(err, test),
+                             result_actual='UNEXPECTED-FAIL')
         super(MarionetteTestResult, self).addFailure(test, err)
 
     def addSuccess(self, test):
         self.passed += 1
         self.add_test_result(test, result_actual='PASS')
         super(MarionetteTestResult, self).addSuccess(test)
 
     def addExpectedFailure(self, test, err):
@@ -188,17 +192,17 @@ class MarionetteTestResult(StructuredTes
     def printLogs(self, test):
         for testcase in test._tests:
             if hasattr(testcase, 'loglines') and testcase.loglines:
                 # Don't dump loglines to the console if they only contain
                 # TEST-START and TEST-END.
                 skip_log = True
                 for line in testcase.loglines:
                     str_line = ' '.join(line)
-                    if not 'TEST-END' in str_line and not 'TEST-START' in str_line:
+                    if 'TEST-END' not in str_line and 'TEST-START' not in str_line:
                         skip_log = False
                         break
                 if skip_log:
                     return
                 self.logger.info('START LOG:')
                 for line in testcase.loglines:
                     self.logger.info(' '.join(line).encode('ascii', 'replace'))
                 self.logger.info('END LOG:')
@@ -255,126 +259,131 @@ class BaseMarionetteArguments(ArgumentPa
                           nargs='*',
                           default=[],
                           help='Tests to run. '
                                'One or more paths to test files (Python or JS), '
                                'manifest files (.ini) or directories. '
                                'When a directory is specified, '
                                'all test files in the directory will be run.')
         self.add_argument('--binary',
-                        help='path to gecko executable to launch before running the test')
+                          help='path to gecko executable to launch before running the test')
         self.add_argument('--address',
-                        help='host:port of running Gecko instance to connect to')
+                          help='host:port of running Gecko instance to connect to')
         self.add_argument('--emulator',
-                        action='store_true',
-                        help='If no --address is given, then the harness will launch an emulator. (See Remote options group.) '
-                             'If --address is given, then the harness assumes you are running an '
-                             'emulator already, and will launch gecko app on  that emulator.')
+                          action='store_true',
+                          help='If no --address is given, then the harness will launch an '
+                               'emulator. (See Remote options group.) '
+                               'If --address is given, then the harness assumes you are '
+                               'running an emulator already, and will launch gecko app '
+                               'on that emulator.')
         self.add_argument('--app',
-                        help='application to use. see marionette_driver.geckoinstance')
+                          help='application to use. see marionette_driver.geckoinstance')
         self.add_argument('--app-arg',
-                        dest='app_args',
-                        action='append',
-                        default=[],
-                        help='specify a command line argument to be passed onto the application')
+                          dest='app_args',
+                          action='append',
+                          default=[],
+                          help='specify a command line argument to be passed onto the application')
         self.add_argument('--profile',
-                        help='profile to use when launching the gecko process. If not passed, then a profile will be '
-                             'constructed and used',
-                        type=dir_path)
+                          help='profile to use when launching the gecko process. If not passed, '
+                               'then a profile will be constructed and used',
+                          type=dir_path)
         self.add_argument('--pref',
-                        action='append',
-                        dest='prefs_args',
-                        help=(" A preference to set. Must be a key-value pair"
-                              " separated by a ':'."))
+                          action='append',
+                          dest='prefs_args',
+                          help="A preference to set. Must be a key-value pair separated by a ':'.")
         self.add_argument('--preferences',
-                        action='append',
-                        dest='prefs_files',
-                        help=("read preferences from a JSON or INI file. For"
-                              " INI, use 'file.ini:section' to specify a"
-                              " particular section."))
+                          action='append',
+                          dest='prefs_files',
+                          help="read preferences from a JSON or INI file. For INI, use "
+                               "'file.ini:section' to specify a particular section.")
         self.add_argument('--addon',
-                        action='append',
-                        help="addon to install; repeat for multiple addons.")
+                          action='append',
+                          help="addon to install; repeat for multiple addons.")
         self.add_argument('--repeat',
-                        type=int,
-                        default=0,
-                        help='number of times to repeat the test(s)')
+                          type=int,
+                          default=0,
+                          help='number of times to repeat the test(s)')
         self.add_argument('--testvars',
-                        action='append',
-                        help='path to a json file with any test data required')
+                          action='append',
+                          help='path to a json file with any test data required')
         self.add_argument('--symbols-path',
-                        help='absolute path to directory containing breakpad symbols, or the url of a zip file containing symbols')
+                          help='absolute path to directory containing breakpad symbols, or the '
+                               'url of a zip file containing symbols')
         self.add_argument('--timeout',
-                        type=int,
-                        help='if a --timeout value is given, it will set the default page load timeout, search timeout and script timeout to the given value. If not passed in, it will use the default values of 30000ms for page load, 0ms for search timeout and 10000ms for script timeout')
+                          type=int,
+                          help='if a --timeout value is given, it will set the default page load '
+                               'timeout, search timeout and script timeout to the given value. '
+                               'If not passed in, it will use the default values of 30000ms for '
+                               'page load, 0ms for search timeout and 10000ms for script timeout')
         self.add_argument('--startup-timeout',
-                        type=int,
-                        default=60,
-                        help='the max number of seconds to wait for a Marionette connection after launching a binary')
+                          type=int,
+                          default=60,
+                          help='the max number of seconds to wait for a Marionette connection '
+                               'after launching a binary')
         self.add_argument('--shuffle',
-                        action='store_true',
-                        default=False,
-                        help='run tests in a random order')
+                          action='store_true',
+                          default=False,
+                          help='run tests in a random order')
         self.add_argument('--shuffle-seed',
-                        type=int,
-                        default=random.randint(0, sys.maxint),
-                        help='Use given seed to shuffle tests')
+                          type=int,
+                          default=random.randint(0, sys.maxint),
+                          help='Use given seed to shuffle tests')
         self.add_argument('--total-chunks',
-                        type=int,
-                        help='how many chunks to split the tests up into')
+                          type=int,
+                          help='how many chunks to split the tests up into')
         self.add_argument('--this-chunk',
-                        type=int,
-                        help='which chunk to run')
+                          type=int,
+                          help='which chunk to run')
         self.add_argument('--sources',
-                        help='path to sources.xml (Firefox OS only)')
+                          help='path to sources.xml (Firefox OS only)')
         self.add_argument('--server-root',
-                        help='url to a webserver or path to a document root from which content '
-                        'resources are served (default: {}).'.format(os.path.join(
-                            os.path.dirname(here), 'www')))
+                          help='url to a webserver or path to a document root from which content '
+                               'resources are served (default: {}).'.format(os.path.join(
+                                   os.path.dirname(here), 'www')))
         self.add_argument('--gecko-log',
-                        help="Define the path to store log file. If the path is"
-                             " a directory, the real log file will be created"
-                             " given the format gecko-(timestamp).log. If it is"
-                             " a file, if will be used directly. '-' may be passed"
-                             " to write to stdout. Default: './gecko.log'")
+                          help="Define the path to store log file. If the path is"
+                               " a directory, the real log file will be created"
+                               " given the format gecko-(timestamp).log. If it is"
+                               " a file, if will be used directly. '-' may be passed"
+                               " to write to stdout. Default: './gecko.log'")
         self.add_argument('--logger-name',
-                        default='Marionette-based Tests',
-                        help='Define the name to associate with the logger used')
+                          default='Marionette-based Tests',
+                          help='Define the name to associate with the logger used')
         self.add_argument('--jsdebugger',
-                        action='store_true',
-                        default=False,
-                        help='Enable the jsdebugger for marionette javascript.')
+                          action='store_true',
+                          default=False,
+                          help='Enable the jsdebugger for marionette javascript.')
         self.add_argument('--pydebugger',
-                        help='Enable python post-mortem debugger when a test fails.'
-                             ' Pass in the debugger you want to use, eg pdb or ipdb.')
+                          help='Enable python post-mortem debugger when a test fails.'
+                               ' Pass in the debugger you want to use, eg pdb or ipdb.')
         self.add_argument('--socket-timeout',
-                        type=float,
-                        default=self.socket_timeout_default,
-                        help='Set the global timeout for marionette socket operations.')
+                          type=float,
+                          default=self.socket_timeout_default,
+                          help='Set the global timeout for marionette socket operations.')
         self.add_argument('--disable-e10s',
-                        action='store_false',
-                        dest='e10s',
-                        default=True,
-                        help='Disable e10s when running marionette tests.')
+                          action='store_false',
+                          dest='e10s',
+                          default=True,
+                          help='Disable e10s when running marionette tests.')
         self.add_argument('--tag',
-                        action='append', dest='test_tags',
-                        default=None,
-                        help="Filter out tests that don't have the given tag. Can be "
-                             "used multiple times in which case the test must contain "
-                             "at least one of the given tags.")
+                          action='append', dest='test_tags',
+                          default=None,
+                          help="Filter out tests that don't have the given tag. Can be "
+                               "used multiple times in which case the test must contain "
+                               "at least one of the given tags.")
         self.add_argument('--workspace',
                           action='store',
                           default=None,
                           help="Path to directory for Marionette output. "
                                "(Default: .) (Default profile dest: TMP)",
                           type=dir_path)
         self.add_argument('-v', '--verbose',
-                        action='count',
-                        help='Increase verbosity to include debug messages with -v, '
-                            'and trace messages with -vv.')
+                          action='count',
+                          help='Increase verbosity to include debug messages with -v, '
+                               'and trace messages with -vv.')
         self.register_argument_container(RemoteMarionetteArguments())
 
     def register_argument_container(self, container):
         group = self.add_argument_group(container.name)
 
         for cli, kwargs in container.args:
             group.add_argument(*cli, **kwargs)
 
@@ -383,19 +392,17 @@ class BaseMarionetteArguments(ArgumentPa
     def parse_known_args(self, args=None, namespace=None):
         args, remainder = ArgumentParser.parse_known_args(self, args, namespace)
         for container in self.argument_containers:
             if hasattr(container, 'parse_args_handler'):
                 container.parse_args_handler(args)
         return (args, remainder)
 
     def _get_preferences(self, prefs_files, prefs_args):
-        """
-        return user defined profile preferences as a dict
-        """
+        """Return user defined profile preferences as a dict."""
         # object that will hold the preferences
         prefs = mozprofile.prefs.Preferences()
 
         # add preferences files
         if prefs_files:
             for prefs_file in prefs_files:
                 prefs.add_file(prefs_file)
 
@@ -453,16 +460,17 @@ class BaseMarionetteArguments(ArgumentPa
             })
 
         for container in self.argument_containers:
             if hasattr(container, 'verify_usage_handler'):
                 container.verify_usage_handler(args)
 
         return args
 
+
 class RemoteMarionetteArguments(object):
     name = 'Remote (Emulator/Device)'
     args = [
         [['--emulator-binary'],
          {'help': 'Path to emulator binary. By default mozrunner uses `which emulator`',
           'dest': 'emulator_bin',
           }],
         [['--adb'],
@@ -483,27 +491,29 @@ class RemoteMarionetteArguments(object):
           }],
         [['--package'],
          {'help': 'Name of Android package, e.g. org.mozilla.fennec',
           'dest': 'package_name',
           }],
 
     ]
 
+
 class BaseMarionetteTestRunner(object):
 
     textrunnerclass = MarionetteTextTestRunner
     driverclass = Marionette
 
     def __init__(self, address=None,
                  app=None, app_args=None, binary=None, profile=None,
                  logger=None, logdir=None,
                  repeat=0, testvars=None,
                  symbols_path=None, timeout=None,
-                 shuffle=False, shuffle_seed=random.randint(0, sys.maxint), this_chunk=1, total_chunks=1, sources=None,
+                 shuffle=False, shuffle_seed=random.randint(0, sys.maxint), this_chunk=1,
+                 total_chunks=1, sources=None,
                  server_root=None, gecko_log=None, result_callbacks=None,
                  prefs=None, test_tags=None,
                  socket_timeout=BaseMarionetteArguments.socket_timeout_default,
                  startup_timeout=None, addons=None, workspace=None,
                  verbose=0, e10s=True, emulator=False, **kwargs):
         self.extra_kwargs = kwargs
         self.test_kwargs = deepcopy(kwargs)
         self.address = address
@@ -544,17 +554,18 @@ class BaseMarionetteTestRunner(object):
         self.verbose = verbose
         self.e10s = e10s
         self._filename_pattern = None
 
         def gather_debug(test, status):
             rv = {}
             marionette = test._marionette_weakref()
 
-            # In the event we're gathering debug without starting a session, skip marionette commands
+            # In the event we're gathering debug without starting a session,
+            # skip marionette commands
             if marionette.session is not None:
                 try:
                     with marionette.using_context(marionette.CONTEXT_CHROME):
                         rv['screenshot'] = marionette.screenshot()
                     with marionette.using_context(marionette.CONTEXT_CONTENT):
                         rv['source'] = marionette.page_source
                 except Exception:
                     logger = get_default_logger()
@@ -579,29 +590,30 @@ class BaseMarionetteTestRunner(object):
         else:
             self.gecko_log = gecko_log
 
         self.results = []
 
     @property
     def filename_pattern(self):
         if self._filename_pattern is None:
-            self._filename_pattern = re.compile("^test(((_.+?)+?\.((py)|(js)))|(([A-Z].*?)+?\.js))$")
+            self._filename_pattern = re.compile(
+                "^test(((_.+?)+?\.((py)|(js)))|(([A-Z].*?)+?\.js))$")
 
         return self._filename_pattern
 
     @property
     def testvars(self):
         if self._testvars is not None:
             return self._testvars
 
         self._testvars = {}
 
         def update(d, u):
-            """ Update a dictionary that may contain nested dictionaries. """
+            """Update a dictionary that may contain nested dictionaries."""
             for k, v in u.iteritems():
                 o = d.get(k, {})
                 if isinstance(v, dict) and isinstance(o, dict):
                     d[k] = update(d.get(k, {}), v)
                 else:
                     d[k] = u[k]
             return d
 
@@ -662,18 +674,17 @@ class BaseMarionetteTestRunner(object):
         return self._appName
 
     @property
     def bin(self):
         return self._bin
 
     @bin.setter
     def bin(self, path):
-        """
-        Set binary and reset parts of runner accordingly
+        """Set binary and reset parts of runner accordingly.
 
         Intended use: to change binary between calls to run_tests
         """
         self._bin = path
         self.tests = []
         self.cleanup()
 
     def reset_test_stats(self):
@@ -708,17 +719,17 @@ class BaseMarionetteTestRunner(object):
                 'gecko_log': self.gecko_log,
                 # ensure Marionette class takes care of starting gecko instance
                 'bin': True,
             })
 
         if self.bin:
             kwargs.update({
                 'bin': self.bin,
-        })
+            })
 
         if self.emulator:
             kwargs.update({
                 'avd_home': self.extra_kwargs.get('avd_home'),
                 'adb_path': self.extra_kwargs.get('adb_path'),
                 'emulator_binary': self.extra_kwargs.get('emulator_bin'),
                 'avd': self.extra_kwargs.get('avd'),
                 'package_name': self.extra_kwargs.get('package_name'),
@@ -731,35 +742,36 @@ class BaseMarionetteTestRunner(object):
                 'port': int(port),
             })
             if self.emulator:
                 kwargs.update({
                     'connect_to_running_emulator': True,
                 })
             if not self.bin and not self.emulator:
                 try:
-                    #establish a socket connection so we can vertify the data come back
-                    connection = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
-                    connection.connect((host,int(port)))
+                    # Establish a socket connection so we can vertify the data come back
+                    connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+                    connection.connect((host, int(port)))
                     connection.close()
                 except Exception as e:
                     exc, val, tb = sys.exc_info()
                     msg = "Connection attempt to {0}:{1} failed with error: {2}"
                     raise exc, msg.format(host, port, e), tb
         if self.workspace:
             kwargs['workspace'] = self.workspace_path
         return kwargs
 
     def launch_test_container(self):
         if self.marionette.session is None:
             self.marionette.start_session()
         self.marionette.set_context(self.marionette.CONTEXT_CONTENT)
 
         result = self.marionette.execute_async_script("""
-if((navigator.mozSettings == undefined) || (navigator.mozSettings == null) || (navigator.mozApps == undefined) || (navigator.mozApps == null)) {
+if((navigator.mozSettings == undefined) || (navigator.mozSettings == null) ||
+   (navigator.mozApps == undefined) || (navigator.mozApps == null)) {
     marionetteScriptFinished(false);
     return;
 }
 let setReq = navigator.mozSettings.createLock().set({'lockscreen.enabled': false});
 setReq.onsuccess = function() {
     let appName = 'Test Container';
     let activeApp = window.wrappedJSObject.Service.currentApp;
 
@@ -832,17 +844,18 @@ setReq.onerror = function() {
             else:
                 self.marionette.baseurl = self.server_root
                 self.logger.info("using remote content from %s" % self.marionette.baseurl)
 
     def _add_tests(self, tests):
         for test in tests:
             self.add_test(test)
 
-        invalid_tests = [t['filepath'] for t in self.tests if not self._is_filename_valid(t['filepath'])]
+        invalid_tests = [t['filepath'] for t in self.tests
+                         if not self._is_filename_valid(t['filepath'])]
         if invalid_tests:
             raise Exception("Test file names must be of the form "
                             "'test_something.py', 'test_something.js', or 'testSomething.js'."
                             " Invalid test names:\n  %s"
                             % '\n  '.join(invalid_tests))
 
     def _is_filename_valid(self, filename):
         filename = os.path.basename(filename)
@@ -868,34 +881,34 @@ setReq.onerror = function() {
 
         device_info = None
         if self.marionette.instance and self.emulator:
             try:
                 device_info = self.marionette.instance.runner.device.dm.getInfo()
             except Exception:
                 self.logger.warning('Could not get device info.')
 
-		#TODO Get version_info in Fennec case
+        # TODO: Get version_info in Fennec case
         version_info = None
         if self.bin:
             version_info = mozversion.get_version(binary=self.bin,
                                                   sources=self.sources)
 
         self.logger.info("running with e10s: {}".format(self.e10s))
 
         self.logger.suite_start(self.tests,
                                 version_info=version_info,
                                 device_info=device_info)
 
         self._log_skipped_tests()
 
         interrupted = None
         try:
             counter = self.repeat
-            while counter >=0:
+            while counter >= 0:
                 round_num = self.repeat - counter
                 if round_num > 0:
                     self.logger.info('\nREPEAT %d\n-------' % round_num)
                 self.run_test_sets()
                 counter -= 1
         except KeyboardInterrupt:
             # in case of KeyboardInterrupt during the test execution
             # we want to display current test results.
@@ -931,30 +944,31 @@ setReq.onerror = function() {
                 raise interrupted[0], interrupted[1], interrupted[2]
 
     def _print_summary(self, tests):
         self.logger.info('\nSUMMARY\n-------')
         self.logger.info('passed: %d' % self.passed)
         if self.unexpected_successes == 0:
             self.logger.info('failed: %d' % self.failed)
         else:
-            self.logger.info('failed: %d (unexpected sucesses: %d)' % (self.failed, self.unexpected_successes))
+            self.logger.info('failed: %d (unexpected sucesses: %d)' %
+                             (self.failed, self.unexpected_successes))
         if self.skipped == 0:
             self.logger.info('todo: %d' % self.todo)
         else:
             self.logger.info('todo: %d (skipped: %d)' % (self.todo, self.skipped))
 
         if self.failed > 0:
             self.logger.info('\nFAILED TESTS\n-------')
             for failed_test in self.failures:
                 self.logger.info('%s' % failed_test[0])
 
     def start_httpd(self, need_external_ip):
         warnings.warn("start_httpd has been deprecated in favour of create_httpd",
-            DeprecationWarning)
+                      DeprecationWarning)
         self.httpd = self.create_httpd(need_external_ip)
 
     def create_httpd(self, need_external_ip):
         host = "127.0.0.1"
         if need_external_ip:
             host = moznetwork.get_ip()
         root = self.server_root or os.path.join(os.path.dirname(here), "www")
         rv = httpd.FixtureServer(root, host=host)
@@ -972,39 +986,38 @@ setReq.onerror = function() {
                                     " See --help for details.")
                         relpath = os.path.relpath(os.path.join(root, filename), filepath)
                         self.logger.warning(msg_tmpl.format(relpath, filepath))
                     elif self._is_filename_valid(filename):
                         test_file = os.path.join(root, filename)
                         self.add_test(test_file)
             return
 
-
         file_ext = os.path.splitext(os.path.split(filepath)[-1])[1]
 
         if file_ext == '.ini':
             manifest = TestManifest()
             manifest.read(filepath)
 
             filters = []
             if self.test_tags:
                 filters.append(tags(self.test_tags))
-            json_path = update_mozinfo(filepath)
+            update_mozinfo(filepath)
             self.logger.info("mozinfo updated with the following: {}".format(None))
             self.logger.info("mozinfo is: {}".format(mozinfo.info))
             manifest_tests = manifest.active_tests(exists=False,
                                                    disabled=True,
                                                    filters=filters,
                                                    app=self.appName,
                                                    e10s=self.e10s,
                                                    **mozinfo.info)
             if len(manifest_tests) == 0:
-                self.logger.error("no tests to run using specified "
+                self.logger.error("No tests to run using specified "
                                   "combination of filters: {}".format(
-                                       manifest.fmt_filters()))
+                                      manifest.fmt_filters()))
 
             target_tests = []
             for test in manifest_tests:
                 if test.get('disabled'):
                     self.manifest_skipped_tests.append(test)
                 else:
                     target_tests.append(test)
 
@@ -1013,17 +1026,18 @@ setReq.onerror = function() {
                     raise IOError("test file: %s does not exist" % i["path"])
 
                 file_ext = os.path.splitext(os.path.split(i['path'])[-1])[-1]
                 test_container = None
 
                 self.add_test(i["path"], i["expected"], test_container)
             return
 
-        self.tests.append({'filepath': filepath, 'expected': expected, 'test_container': test_container})
+        self.tests.append({'filepath': filepath, 'expected': expected,
+                          'test_container': test_container})
 
     def run_test(self, filepath, expected, test_container):
 
         testloader = unittest.TestLoader()
         suite = unittest.TestSuite()
         self.test_kwargs['expected'] = expected
         self.test_kwargs['test_container'] = test_container
         mod_name = os.path.splitext(os.path.split(filepath)[-1])[0]
@@ -1051,22 +1065,24 @@ setReq.onerror = function() {
             self.results.append(results)
 
             self.failed += len(results.failures) + len(results.errors)
             if hasattr(results, 'skipped'):
                 self.skipped += len(results.skipped)
                 self.todo += len(results.skipped)
             self.passed += results.passed
             for failure in results.failures + results.errors:
-                self.failures.append((results.getInfo(failure), failure.output, 'TEST-UNEXPECTED-FAIL'))
+                self.failures.append((results.getInfo(failure), failure.output,
+                                      'TEST-UNEXPECTED-FAIL'))
             if hasattr(results, 'unexpectedSuccesses'):
                 self.failed += len(results.unexpectedSuccesses)
                 self.unexpected_successes += len(results.unexpectedSuccesses)
                 for failure in results.unexpectedSuccesses:
-                    self.failures.append((results.getInfo(failure), failure.output, 'TEST-UNEXPECTED-PASS'))
+                    self.failures.append((results.getInfo(failure), failure.output,
+                                          'TEST-UNEXPECTED-PASS'))
             if hasattr(results, 'expectedFailures'):
                 self.todo += len(results.expectedFailures)
 
             self.mixin_run_tests = []
             for result in self.results:
                 result.result_modifiers = []
 
     def run_test_set(self, tests):
--- a/testing/marionette/harness/marionette/runtests.py
+++ b/testing/marionette/harness/marionette/runtests.py
@@ -34,24 +34,25 @@ class MarionetteHarness(object):
                  testcase_class=MarionetteTestCase,
                  args=None):
         self._runner_class = runner_class
         self._parser_class = parser_class
         self._testcase_class = testcase_class
         self.args = args or self.parse_args()
 
     def parse_args(self, logger_defaults=None):
-        parser = self._parser_class(usage='%(prog)s [options] test_file_or_dir <test_file_or_dir> ...')
+        parser = self._parser_class(
+            usage='%(prog)s [options] test_file_or_dir <test_file_or_dir> ...')
         parser.add_argument('--version', action='version',
-            help="Show version information.",
-            version="%(prog)s {version}"
-                    " (using marionette-driver: {driver_version}, ".format(
-                        version=__version__,
-                        driver_version=driver_version
-                    ))
+                            help="Show version information.",
+                            version="%(prog)s {version}"
+                            " (using marionette-driver: {driver_version}, ".format(
+                                version=__version__,
+                                driver_version=driver_version
+                            ))
         mozlog.commandline.add_logging_group(parser)
         args = parser.parse_args()
         parser.verify_usage(args)
 
         logger = mozlog.commandline.setup_logging(
             args.logger_name, args, logger_defaults or {"tbpl": sys.stdout})
 
         args.logger = logger
--- a/testing/marionette/mach_test_package_commands.py
+++ b/testing/marionette/mach_test_package_commands.py
@@ -19,19 +19,18 @@ def run_marionette(context, **kwargs):
     from marionette.runtests import (
         MarionetteTestRunner,
         MarionetteHarness
     )
     from mozlog.structured import commandline
 
 
     args = argparse.Namespace(**kwargs)
-
-    if not args.binary:
-        args.binary = context.find_firefox()
+    args.binary = args.binary or context.firefox_bin
+    args.e10s = context.mozharness_config.get('e10s', args.e10s)
 
     test_root = os.path.join(context.package_root, 'marionette', 'tests')
     if not args.tests:
         args.tests = [os.path.join(test_root, 'testing', 'marionette', 'harness',
                                    'marionette', 'tests', 'unit-tests.ini')]
 
     normalize = partial(context.normalize_test_path, test_root)
     args.tests = map(normalize, args.tests)
--- a/testing/mochitest/mach_test_package_commands.py
+++ b/testing/mochitest/mach_test_package_commands.py
@@ -8,41 +8,72 @@ import os
 from argparse import Namespace
 from functools import partial
 
 from mach.decorators import (
     CommandProvider,
     Command,
 )
 
+here = os.path.abspath(os.path.dirname(__file__))
 parser = None
 
 
 def run_mochitest(context, **kwargs):
     args = Namespace(**kwargs)
+    args.e10s = context.mozharness_config.get('e10s', args.e10s)
     args.certPath = context.certs_dir
-    args.utilityPath = context.bin_dir
-    args.extraProfileFiles.append(os.path.join(context.bin_dir, 'plugins'))
-
-    if not args.app:
-        args.app = context.find_firefox()
 
     if args.test_paths:
         test_root = os.path.join(context.package_root, 'mochitest', 'tests')
         normalize = partial(context.normalize_test_path, test_root)
         args.test_paths = map(normalize, args.test_paths)
 
+    import mozinfo
+    if mozinfo.info['buildapp'] == 'mobile/android':
+        return run_mochitest_android(context, args)
+    return run_mochitest_desktop(context, args)
+
+
+def run_mochitest_desktop(context, args):
+    args.app = args.app or context.firefox_bin
+    args.utilityPath = context.bin_dir
+    args.extraProfileFiles.append(os.path.join(context.bin_dir, 'plugins'))
+
     from runtests import run_test_harness
     return run_test_harness(parser, args)
 
 
+def run_mochitest_android(context, args):
+    args.app = args.app or 'org.mozilla.fennec'
+    args.extraProfileFiles.append(os.path.join(context.package_root, 'mochitest', 'fonts'))
+    args.utilityPath = context.hostutils
+    args.xrePath = context.hostutils
+
+    config = context.mozharness_config
+    if config:
+        args.remoteWebServer = config['remote_webserver']
+        args.httpPort = config['emulator']['http_port']
+        args.sslPort = config['emulator']['ssl_port']
+        args.adbPath = config['exes']['adb'] % {'abs_work_dir': context.mozharness_workdir}
+
+    from runtestsremote import run_test_harness
+    return run_test_harness(parser, args)
+
+
 def setup_argument_parser():
+    import mozinfo
+    mozinfo.find_and_update_from_json(os.path.dirname(here))
+    app = 'generic'
+    if mozinfo.info.get('buildapp') == 'mobile/android':
+        app = 'android'
+
     from mochitest_options import MochitestArgumentParser
     global parser
-    parser = MochitestArgumentParser(app='generic')
+    parser = MochitestArgumentParser(app=app)
     return parser
 
 
 @CommandProvider
 class MochitestCommands(object):
 
     def __init__(self, context):
         self.context = context
--- a/testing/mochitest/mochitest_options.py
+++ b/testing/mochitest/mochitest_options.py
@@ -989,16 +989,22 @@ class AndroidArguments(ArgumentContainer
           "default": None,
           }],
         [["--dm_trans"],
          {"choices": ["adb", "sut"],
           "default": "adb",
           "help": "The transport to use for communication with the device [default: adb].",
           "suppress": True,
           }],
+        [["--adbpath"],
+         {"dest": "adbPath",
+          "default": None,
+          "help": "Path to adb binary.",
+          "suppress": True,
+          }],
         [["--devicePort"],
          {"dest": "devicePort",
           "type": int,
           "default": 20701,
           "help": "port of remote device to test",
           }],
         [["--remote-product-name"],
          {"dest": "remoteProductName",
@@ -1060,39 +1066,32 @@ class AndroidArguments(ArgumentContainer
     }
 
     def validate(self, parser, options, context):
         """Validate android options."""
 
         if build_obj:
             options.log_mach = '-'
 
+        device_args = {'deviceRoot': options.remoteTestRoot}
         if options.dm_trans == "adb":
+            device_args['adbPath'] = options.adbPath
             if options.deviceIP:
-                options.dm = DroidADB(
-                    options.deviceIP,
-                    options.devicePort,
-                    deviceRoot=options.remoteTestRoot)
+                device_args['host'] = options.deviceIP
+                device_args['port'] = options.devicePort
             elif options.deviceSerial:
-                options.dm = DroidADB(
-                    None,
-                    None,
-                    deviceSerial=options.deviceSerial,
-                    deviceRoot=options.remoteTestRoot)
-            else:
-                options.dm = DroidADB(deviceRoot=options.remoteTestRoot)
+                device_args['deviceSerial'] = options.deviceSerial
+            options.dm = DroidADB(**device_args)
         elif options.dm_trans == 'sut':
             if options.deviceIP is None:
                 parser.error(
                     "If --dm_trans = sut, you must provide a device IP")
-
-            options.dm = DroidSUT(
-                options.deviceIP,
-                options.devicePort,
-                deviceRoot=options.remoteTestRoot)
+            device_args['host'] = options.deviceIP
+            device_args['port'] = options.devicePort
+            options.dm = DroidSUT(**device_args)
 
         if not options.remoteTestRoot:
             options.remoteTestRoot = options.dm.deviceRoot
 
         if options.remoteWebServer is None:
             if os.name != "nt":
                 options.remoteWebServer = moznetwork.get_ip()
             else:
--- a/testing/mochitest/runtests.py
+++ b/testing/mochitest/runtests.py
@@ -2393,44 +2393,42 @@ class MochitestDesktop(MochitestBase):
 
             if options.marionette:
                 host, port = options.marionette.split(':')
                 marionette_args['host'] = host
                 marionette_args['port'] = int(port)
 
             self.log.info("runtests.py | Running with e10s: {}".format(options.e10s))
             self.log.info("runtests.py | Running tests: start.\n")
-            try:
-                status = self.runApp(testURL,
-                                     self.browserEnv,
-                                     options.app,
-                                     profile=self.profile,
-                                     extraArgs=options.browserArgs,
-                                     utilityPath=options.utilityPath,
-                                     debuggerInfo=debuggerInfo,
-                                     valgrindPath=valgrindPath,
-                                     valgrindArgs=valgrindArgs,
-                                     valgrindSuppFiles=valgrindSuppFiles,
-                                     symbolsPath=options.symbolsPath,
-                                     timeout=timeout,
-                                     detectShutdownLeaks=detectShutdownLeaks,
-                                     screenshotOnFail=options.screenshotOnFail,
-                                     bisectChunk=options.bisectChunk,
-                                     quiet=options.quiet,
-                                     marionette_args=marionette_args,
-                                     )
-            except KeyboardInterrupt:
-                self.log.info("runtests.py | Received keyboard interrupt.\n")
-                status = -1
-            except:
-                traceback.print_exc()
-                self.log.error(
-                    "Automation Error: Received unexpected exception while running application\n")
-                status = 1
-
+            status = self.runApp(testURL,
+                                 self.browserEnv,
+                                 options.app,
+                                 profile=self.profile,
+                                 extraArgs=options.browserArgs,
+                                 utilityPath=options.utilityPath,
+                                 debuggerInfo=debuggerInfo,
+                                 valgrindPath=valgrindPath,
+                                 valgrindArgs=valgrindArgs,
+                                 valgrindSuppFiles=valgrindSuppFiles,
+                                 symbolsPath=options.symbolsPath,
+                                 timeout=timeout,
+                                 detectShutdownLeaks=detectShutdownLeaks,
+                                 screenshotOnFail=options.screenshotOnFail,
+                                 bisectChunk=options.bisectChunk,
+                                 quiet=options.quiet,
+                                 marionette_args=marionette_args,
+                                 )
+        except KeyboardInterrupt:
+            self.log.info("runtests.py | Received keyboard interrupt.\n")
+            status = -1
+        except:
+            traceback.print_exc()
+            self.log.error(
+                "Automation Error: Received unexpected exception while running application\n")
+            status = 1
         finally:
             self.stopServers()
 
         mozleak.process_leak_log(
             self.leak_report_file,
             leak_thresholds=options.leakThresholds,
             ignore_missing_leaks=options.ignoreMissingLeaks,
             log=self.log,
--- a/testing/mochitest/tests/SimpleTest/EventUtils.js
+++ b/testing/mochitest/tests/SimpleTest/EventUtils.js
@@ -1867,16 +1867,38 @@ function synthesizeQueryTextRect(aOffset
     return nullptr;
   }
   return utils.sendQueryContentEvent(utils.QUERY_TEXT_RECT,
                                      aOffset, aLength, 0, 0,
                                      QUERY_CONTENT_FLAG_USE_NATIVE_LINE_BREAK);
 }
 
 /**
+ * Synthesize a query text rect array event.
+ *
+ * @param aOffset  The character offset.  0 means the first character in the
+ *                 selection root.
+ * @param aLength  The length of the text.  If the length is too long,
+ *                 the extra length is ignored.
+ * @param aWindow  Optional (If null, current |window| will be used)
+ * @return         An nsIQueryContentEventResult object.  If this failed,
+ *                 the result might be null.
+ */
+function synthesizeQueryTextRectArray(aOffset, aLength, aWindow)
+{
+  var utils = _getDOMWindowUtils(aWindow);
+  if (!utils) {
+    return nullptr;
+  }
+  return utils.sendQueryContentEvent(utils.QUERY_TEXT_RECT_ARRAY,
+                                     aOffset, aLength, 0, 0,
+                                     QUERY_CONTENT_FLAG_USE_NATIVE_LINE_BREAK);
+}
+
+/**
  * Synthesize a query editor rect event.
  *
  * @param aWindow  Optional (If null, current |window| will be used)
  * @return         An nsIQueryContentEventResult object.  If this failed,
  *                 the result might be null.
  */
 function synthesizeQueryEditorRect(aWindow)
 {
--- a/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py
+++ b/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py
@@ -30,30 +30,30 @@ class DeviceManagerADB(DeviceManager):
     _useZip = False
     _logcatNeedsRoot = False
     _pollingInterval = 0.01
     _packageName = None
     _tempDir = None
     connected = False
 
     def __init__(self, host=None, port=5555, retryLimit=5, packageName='fennec',
-                 adbPath='adb', deviceSerial=None, deviceRoot=None,
+                 adbPath=None, deviceSerial=None, deviceRoot=None,
                  logLevel=logging.ERROR, autoconnect=True, runAdbAsRoot=False,
                  serverHost=None, serverPort=None, **kwargs):
         DeviceManager.__init__(self, logLevel=logLevel,
                                deviceRoot=deviceRoot)
         self.host = host
         self.port = port
         self.retryLimit = retryLimit
 
         self._serverHost = serverHost
         self._serverPort = serverPort
 
         # the path to adb, or 'adb' to assume that it's on the PATH
-        self._adbPath = adbPath
+        self._adbPath = adbPath or 'adb'
 
         # The serial number of the device to use with adb, used in cases
         # where multiple devices are being managed by the same adb instance.
         self._deviceSerial = deviceSerial
 
         # Some devices do no start adb as root, if allowed you can use
         # this to reboot adbd on the device as root automatically
         self._runAdbAsRoot = runAdbAsRoot
--- a/testing/mozharness/configs/android/androidarm.py
+++ b/testing/mozharness/configs/android/androidarm.py
@@ -38,17 +38,16 @@ config = {
         'read-buildbot-config',
         'setup-avds',
         'start-emulator',
         'download-and-extract',
         'create-virtualenv',
         'verify-emulator',
         'install',
         'run-tests',
-        'stop-emulator',
     ],
     "emulator": {
             "name": "test-1",
             "device_id": "emulator-5554",
             "http_port": "8854", # starting http port to use for the mochitest server
             "ssl_port": "4454", # starting ssl port to use for the server
             "emulator_port": 5554,
             "sut_port1": 20701,
--- a/testing/mozharness/configs/android/androidarm_4_3.py
+++ b/testing/mozharness/configs/android/androidarm_4_3.py
@@ -48,17 +48,16 @@ config = {
         'read-buildbot-config',
         'setup-avds',
         'start-emulator',
         'download-and-extract',
         'create-virtualenv',
         'verify-emulator',
         'install',
         'run-tests',
-        'stop-emulator',
     ],
     "emulator": {
         "name": "test-1",
         "device_id": "emulator-5554",
         "http_port": "8854",  # starting http port to use for the mochitest server
         "ssl_port": "4454",  # starting ssl port to use for the server
         "emulator_port": 5554,
     },
--- a/testing/mozharness/configs/android/androidx86-tc.py
+++ b/testing/mozharness/configs/android/androidx86-tc.py
@@ -33,17 +33,16 @@ config = {
         'clobber',
         'read-buildbot-config',
         'setup-avds',
         'start-emulator',
         'download-and-extract',
         'create-virtualenv',
         'verify-emulator',
         'run-tests',
-        'stop-emulator',
     ],
     "emulator": {
         "name": "test-1",
         "device_id": "emulator-5554",
         "http_port": "8854",  # starting http port to use for the mochitest server
         "ssl_port": "4454",  # starting ssl port to use for the server
         "emulator_port": 5554,
     },
--- a/testing/mozharness/scripts/android_emulator_unittest.py
+++ b/testing/mozharness/scripts/android_emulator_unittest.py
@@ -19,17 +19,17 @@ import time
 import tempfile
 
 # load modules from parent dir
 sys.path.insert(1, os.path.dirname(sys.path[0]))
 
 from mozprocess import ProcessHandler
 
 from mozharness.base.log import FATAL
-from mozharness.base.script import BaseScript, PreScriptAction
+from mozharness.base.script import BaseScript, PreScriptAction, PostScriptAction
 from mozharness.base.vcs.vcsbase import VCSMixin
 from mozharness.mozilla.blob_upload import BlobUploadMixin, blobupload_config_options
 from mozharness.mozilla.mozbase import MozbaseMixin
 from mozharness.mozilla.testing.testbase import TestingMixin, testing_config_options
 from mozharness.mozilla.testing.unittest import EmulatorMixin
 
 
 class AndroidEmulatorTest(BlobUploadMixin, TestingMixin, EmulatorMixin, VCSMixin, BaseScript, MozbaseMixin):
@@ -80,25 +80,25 @@ class AndroidEmulatorTest(BlobUploadMixi
                          'read-buildbot-config',
                          'setup-avds',
                          'start-emulator',
                          'download-and-extract',
                          'create-virtualenv',
                          'verify-emulator',
                          'install',
                          'run-tests',
-                         'stop-emulator'],
+                        ],
             default_actions=['clobber',
                              'start-emulator',
                              'download-and-extract',
                              'create-virtualenv',
                              'verify-emulator',
                              'install',
                              'run-tests',
-                             'stop-emulator'],
+                            ],
             require_config_file=require_config_file,
             config={
                 'virtualenv_modules': self.virtualenv_modules,
                 'virtualenv_requirements': self.virtualenv_requirements,
                 'require_test_zip': True,
                 # IP address of the host as seen from the emulator
                 'remote_webserver': '10.0.2.2',
             }
@@ -719,27 +719,32 @@ class AndroidEmulatorTest(BlobUploadMixi
         self.run_command(cmd, cwd=cwd, env=env, output_parser=parser)
         tbpl_status, log_level = parser.evaluate_parser(0)
         parser.append_tinderboxprint_line(self.test_suite)
 
         self.info("##### %s log ends" % self.test_suite)
         self._dump_emulator_log()
         self.buildbot_status(tbpl_status, level=log_level)
 
-    def stop_emulator(self):
+    @PostScriptAction('run-tests')
+    def stop_emulator(self, action, success=None):
         '''
         Report emulator health, then make sure that the emulator has been stopped
         '''
         self._verify_emulator()
         self._kill_processes(self.config["emulator_process_name"])
 
     def upload_blobber_files(self):
         '''
         Override BlobUploadMixin.upload_blobber_files to ensure emulator is killed
         first (if the emulator is still running, logcat may still be running, which
         may lock the blob upload directory, causing a hang).
         '''
-        self._kill_processes(self.config["emulator_process_name"])
+        if self.config.get('blob_upload_branch'):
+            # Except on interactive workers, we want the emulator to keep running
+            # after the script is finished. So only kill it if blobber would otherwise
+            # have run anyway (it doesn't get run on interactive workers).
+            self._kill_processes(self.config["emulator_process_name"])
         super(AndroidEmulatorTest, self).upload_blobber_files()
 
 if __name__ == '__main__':
     emulatorTest = AndroidEmulatorTest()
     emulatorTest.run_and_exit()
--- a/testing/mozharness/scripts/androidx86_emulator_unittest.py
+++ b/testing/mozharness/scripts/androidx86_emulator_unittest.py
@@ -17,17 +17,17 @@ import time
 import tempfile
 
 # load modules from parent dir
 sys.path.insert(1, os.path.dirname(sys.path[0]))
 
 from mozprocess import ProcessHandler
 
 from mozharness.base.log import FATAL
-from mozharness.base.script import BaseScript, PostScriptRun, PreScriptAction
+from mozharness.base.script import BaseScript, PostScriptRun, PreScriptAction, PostScriptAction
 from mozharness.base.vcs.vcsbase import VCSMixin
 from mozharness.mozilla.blob_upload import BlobUploadMixin, blobupload_config_options
 from mozharness.mozilla.mozbase import MozbaseMixin
 from mozharness.mozilla.buildbot import TBPL_WORST_LEVEL_TUPLE
 from mozharness.mozilla.testing.testbase import TestingMixin, testing_config_options
 from mozharness.mozilla.testing.unittest import EmulatorMixin
 
 
@@ -71,24 +71,24 @@ class AndroidEmulatorTest(BlobUploadMixi
             all_actions=['clobber',
                          'read-buildbot-config',
                          'setup-avds',
                          'start-emulators',
                          'download-and-extract',
                          'create-virtualenv',
                          'install',
                          'run-tests',
-                         'stop-emulators'],
+                        ],
             default_actions=['clobber',
                              'start-emulators',
                              'download-and-extract',
                              'create-virtualenv',
                              'install',
                              'run-tests',
-                             'stop-emulators'],
+                            ],
             require_config_file=require_config_file,
             config={
                 'virtualenv_modules': self.virtualenv_modules,
                 'virtualenv_requirements': self.virtualenv_requirements,
                 'require_test_zip': True,
                 # IP address of the host as seen from the emulator
                 'remote_webserver': '10.0.2.2',
             }
@@ -817,17 +817,18 @@ class AndroidEmulatorTest(BlobUploadMixi
                 # so buildbot won't kill the process due to lack of output
                 if int(time.time()) - start_time > 5 * 60:
                     self.info('#')
                     start_time = int(time.time())
                 time.sleep(30)
 
         self.buildbot_status(joint_tbpl_status, level=joint_log_level)
 
-    def stop_emulators(self):
+    @PostScriptAction('run-tests')
+    def stop_emulators(self, action, success=None):
         '''
         Report emulator health, then make sure that every emulator has been stopped
         '''
         emulator_index = 0
         for test in self.test_suites:
             emulator = self.emulators[emulator_index]
             emulator_index += 1
             self._check_emulator(emulator)
--- a/testing/tools/mach_test_package_bootstrap.py
+++ b/testing/tools/mach_test_package_bootstrap.py
@@ -89,34 +89,43 @@ def ancestors(path, depth=0):
 
 
 def find_firefox(context):
     """Try to automagically find the firefox binary."""
     import mozinstall
     search_paths = []
 
     # Check for a mozharness setup
-    if context.mozharness_config:
-        with open(context.mozharness_config, 'r') as f:
-            config = json.load(f)
-        workdir = os.path.join(config['base_work_dir'], config['work_dir'])
-        search_paths.append(os.path.join(workdir, 'application'))
+    config = context.mozharness_config
+    if config and 'binary_path' in config:
+        return config['binary_path']
+    elif config:
+        search_paths.append(os.path.join(context.mozharness_workdir, 'application'))
 
     # Check for test-stage setup
     dist_bin = os.path.join(os.path.dirname(context.package_root), 'bin')
     if os.path.isdir(dist_bin):
         search_paths.append(dist_bin)
 
     for path in search_paths:
         try:
             return mozinstall.get_binary(path, 'firefox')
         except mozinstall.InvalidBinary:
             continue
 
 
+def find_hostutils(context):
+    workdir = context.mozharness_workdir
+    hostutils = os.path.join(workdir, 'hostutils')
+    for fname in os.listdir(hostutils):
+        fpath = os.path.join(hostutils, fname)
+        if os.path.isdir(fpath) and fname.startswith('host-utils'):
+            return fpath
+
+
 def normalize_test_path(test_root, path):
     if os.path.isabs(path) or os.path.exists(path):
         return os.path.normpath(os.path.abspath(path))
 
     for parent in ancestors(test_root):
         test_path = os.path.join(parent, path)
         if os.path.exists(test_path):
             return os.path.normpath(os.path.abspath(test_path))
@@ -132,35 +141,45 @@ def bootstrap(test_package_root):
         print('Python 2.7 or above (but not Python 3) is required to run mach.')
         print('You are running Python', platform.python_version())
         sys.exit(1)
 
     sys.path[0:0] = [os.path.join(test_package_root, path) for path in SEARCH_PATHS]
     import mach.main
 
     def populate_context(context, key=None):
-        if key is not None:
+        if key is None:
+            context.package_root = test_package_root
+            context.bin_dir = os.path.join(test_package_root, 'bin')
+            context.certs_dir = os.path.join(test_package_root, 'certs')
+            context.module_dir = os.path.join(test_package_root, 'modules')
+            context.ancestors = ancestors
+            context.normalize_test_path = normalize_test_path
             return
 
-        context.package_root = test_package_root
-        context.bin_dir = os.path.join(test_package_root, 'bin')
-        context.certs_dir = os.path.join(test_package_root, 'certs')
-        context.modules_dir = os.path.join(test_package_root, 'modules')
+        # The values for the following 'key's will be set lazily, and cached
+        # after first being invoked.
+        if key == 'firefox_bin':
+            return find_firefox(context)
+
+        if key == 'hostutils':
+            return find_hostutils(context)
 
-        context.ancestors = ancestors
-        context.find_firefox = types.MethodType(find_firefox, context)
-        context.normalize_test_path = normalize_test_path
+        if key == 'mozharness_config':
+            for dir_path in ancestors(context.package_root):
+                mozharness_config = os.path.join(dir_path, 'logs', 'localconfig.json')
+                if os.path.isfile(mozharness_config):
+                    with open(mozharness_config, 'rb') as f:
+                        return json.load(f)
+            return {}
 
-        # Search for a mozharness localconfig.json
-        context.mozharness_config = None
-        for dir_path in ancestors(test_package_root):
-            mozharness_config = os.path.join(dir_path, 'logs', 'localconfig.json')
-            if os.path.isfile(mozharness_config):
-                context.mozharness_config = mozharness_config
-                break
+        if key == 'mozharness_workdir':
+            config = context.mozharness_config
+            if config:
+                return os.path.join(config['base_work_dir'], config['work_dir'])
 
     mach = mach.main.Mach(os.getcwd())
     mach.populate_context_handler = populate_context
 
     for category, meta in CATEGORIES.items():
         mach.define_category(category, meta['short'], meta['long'],
                              meta['priority'])
 
--- a/testing/xpcshell/mach_test_package_commands.py
+++ b/testing/xpcshell/mach_test_package_commands.py
@@ -16,22 +16,21 @@ from xpcshellcommandline import parser_d
 from mach.decorators import (
     CommandProvider,
     Command,
 )
 
 
 def run_xpcshell(context, **kwargs):
     args = Namespace(**kwargs)
+    args.appPath = args.appPath or os.path.dirname(context.firefox_bin)
+    args.e10s = context.mozharness_config.get('e10s', args.e10s)
     args.utility_path = context.bin_dir
     args.testingModulesDir = context.modules_dir
 
-    if not args.appPath:
-        args.appPath = os.path.dirname(context.find_firefox())
-
     if not args.xpcshell:
         args.xpcshell = os.path.join(args.appPath, 'xpcshell')
 
     if not args.pluginsPath:
         for path in context.ancestors(args.appPath, depth=2):
             test = os.path.join(path, 'plugins')
             if os.path.isdir(test):
                 args.pluginsPath = test
--- a/toolkit/components/narrate/test/browser_narrate.js
+++ b/toolkit/components/narrate/test/browser_narrate.js
@@ -122,13 +122,16 @@ add_task(function* testNarrate() {
     // stop.
     do {
       promiseEvent = Promise.race([
         ContentTaskUtils.waitForEvent(content, "paragraphstart"),
         ContentTaskUtils.waitForEvent(content, "paragraphsdone")]);
       $(NarrateTestUtils.FORWARD).click();
     } while ((yield promiseEvent).type == "paragraphstart");
 
+    // This is to make sure we are not actively scrolling when the tab closes.
+    content.scroll(0, 0);
+
     yield ContentTaskUtils.waitForCondition(
       () => !$(NarrateTestUtils.STOP), "transitioned to stopped state");
     NarrateTestUtils.isStoppedState(content, ok);
   });
 });
--- a/toolkit/components/passwordmgr/.eslintrc
+++ b/toolkit/components/passwordmgr/.eslintrc
@@ -4,10 +4,13 @@
     // Require spacing around =>
     "arrow-spacing": 2,
 
     // No space before always a space after a comma
     "comma-spacing": [2, {"before": false, "after": true}],
 
     // Commas at the end of the line not the start
     "comma-style": 2,
+
+    // Don't allow unused local variables unless they match the pattern
+    "no-unused-vars": [2, {"args": "none", "vars": "local", "varsIgnorePattern": "^(ids|ignored|unused)$"}],
   }
 }
--- a/toolkit/components/passwordmgr/InsecurePasswordUtils.jsm
+++ b/toolkit/components/passwordmgr/InsecurePasswordUtils.jsm
@@ -96,17 +96,16 @@ this.InsecurePasswordUtils = {
       isSafePage = false;
     }
 
     let isFormSubmitHTTP = false, isFormSubmitSecure = false;
     if (aForm.rootElement instanceof Ci.nsIDOMHTMLFormElement) {
       let uri = Services.io.newURI(aForm.rootElement.action || aForm.rootElement.baseURI,
                                    null, null);
       let principal = gScriptSecurityManager.getCodebasePrincipal(uri);
-      let host = uri.host;
 
       if (uri.schemeIs("http")) {
         isFormSubmitHTTP = true;
         if (gContentSecurityManager.isOriginPotentiallyTrustworthy(principal)) {
           isFormSubmitSecure = true;
         } else if (isSafePage) {
           // Only warn about the action if we didn't already warn about the form being insecure.
           this._sendWebConsoleMessage("InsecureFormActionPasswordsPresent", domDoc);
--- a/toolkit/components/passwordmgr/LoginImport.jsm
+++ b/toolkit/components/passwordmgr/LoginImport.jsm
@@ -157,17 +157,16 @@ this.LoginImport.prototype = {
         } catch (ex) {
           Cu.reportError("Error importing login: " + ex);
         }
       }
 
       rows = yield connection.execute("SELECT * FROM moz_disabledHosts");
       for (let row of rows) {
         try {
-          let id = row.getResultByName("id");
           let hostname = row.getResultByName("hostname");
 
           this.store.data.disabledHosts.push(hostname);
         } catch (ex) {
           Cu.reportError("Error importing disabled host: " + ex);
         }
       }
     } finally {
--- a/toolkit/components/passwordmgr/LoginManagerContent.jsm
+++ b/toolkit/components/passwordmgr/LoginManagerContent.jsm
@@ -1184,17 +1184,17 @@ var LoginManagerContent = {
     let form = FormLikeFactory.createFromField(aField);
 
     let doc = aField.ownerDocument;
     let messageManager = messageManagerFromWindow(doc.defaultView);
     let recipes = messageManager.sendSyncMessage("RemoteLogins:findRecipes", {
       formOrigin: LoginUtils._getPasswordOrigin(doc.documentURI),
     })[0];
 
-    let [usernameField, newPasswordField, oldPasswordField] =
+    let [usernameField, newPasswordField] =
           this._getFormFields(form, false, recipes);
 
     // If we are not verifying a password field, we want
     // to use aField as the username field.
     if (aField.type != "password") {
       usernameField = aField;
     }
 
--- a/toolkit/components/passwordmgr/LoginRecipes.jsm
+++ b/toolkit/components/passwordmgr/LoginRecipes.jsm
@@ -186,17 +186,16 @@ var LoginRecipesContent = {
   /**
    * @param {Set} aRecipes - Possible recipes that could apply to the form
    * @param {FormLike} aForm - We use a form instead of just a URL so we can later apply
    * tests to the page contents.
    * @return {Set} a subset of recipes that apply to the form with the order preserved
    */
   _filterRecipesForForm(aRecipes, aForm) {
     let formDocURL = aForm.ownerDocument.location;
-    let host = formDocURL.host;
     let hostRecipes = aRecipes;
     let recipes = new Set();
     log.debug("_filterRecipesForForm", aRecipes);
     if (!hostRecipes) {
       return recipes;
     }
 
     for (let hostRecipe of hostRecipes) {
--- a/toolkit/components/passwordmgr/nsLoginManagerPrompter.js
+++ b/toolkit/components/passwordmgr/nsLoginManagerPrompter.js
@@ -926,17 +926,16 @@ LoginManagerPrompter.prototype = {
         Services.logins.setLoginSavingEnabled(login.hostname, false);
         browser.focus();
       }
     }] : null;
 
     let usernamePlaceholder = this._getLocalizedString("noUsernamePlaceholder");
     let togglePasswordLabel = this._getLocalizedString("togglePasswordLabel");
     let togglePasswordAccessKey = this._getLocalizedString("togglePasswordAccessKey");
-    let displayHost = this._getShortDisplayHost(login.hostname);
 
     this._getPopupNote().show(
       browser,
       "password",
       promptMsg,
       "password-notification-icon",
       mainAction,
       secondaryActions,
@@ -1279,17 +1278,16 @@ LoginManagerPrompter.prototype = {
    * Note: The caller doesn't know the username for aNewLogin, so this
    *       function fills in .username and .usernameField with the values
    *       from the login selected by the user.
    *
    * Note; XPCOM stupidity: |count| is just |logins.length|.
    */
   promptToChangePasswordWithUsernames : function (logins, count, aNewLogin) {
     this.log("promptToChangePasswordWithUsernames with count:", count);
-    const buttonFlags = Ci.nsIPrompt.STD_YES_NO_BUTTONS;
 
     var usernames = logins.map(l => l.username);
     var dialogText  = this._getLocalizedString("userSelectText");
     var dialogTitle = this._getLocalizedString("passwordChangeTitle");
     var selectedIndex = { value: null };
 
     // If user selects ok, outparam.value is set to the index
     // of the selected username.
--- a/toolkit/components/passwordmgr/storage-json.js
+++ b/toolkit/components/passwordmgr/storage-json.js
@@ -279,18 +279,16 @@ this.LoginManagerStorage_json.prototype 
    * is an array of encrypted nsLoginInfo and ids is an array of associated
    * ids in the database.
    */
   _searchLogins(matchData, aOptions = {
     schemeUpgrades: false,
   }) {
     this._store.ensureDataReady();
 
-    let conditions = [];
-
     function match(aLogin) {
       for (let field in matchData) {
         let wantedValue = matchData[field];
         switch (field) {
           case "formSubmitURL":
             if (wantedValue != null) {
               // Historical compatibility requires this special case
               if (aLogin.formSubmitURL == "") {
@@ -393,17 +391,16 @@ this.LoginManagerStorage_json.prototype 
     logins = this._decryptLogins(logins);
 
     this.log("_findLogins: returning", logins.length, "logins");
     count.value = logins.length; // needed for XPCOM
     return logins;
   },
 
   countLogins(hostname, formSubmitURL, httpRealm) {
-    let count = {};
     let loginData = {
       hostname: hostname,
       formSubmitURL: formSubmitURL,
       httpRealm: httpRealm
     };
     let matchData = { };
     for (let field of ["hostname", "formSubmitURL", "httpRealm"])
       if (loginData[field] != '')
--- a/toolkit/components/passwordmgr/test/.eslintrc
+++ b/toolkit/components/passwordmgr/test/.eslintrc
@@ -1,6 +1,9 @@
 {
   "extends": [
     "../../../../testing/mochitest/mochitest.eslintrc",
     "../../../../testing/mochitest/chrome.eslintrc"
-  ]
+  ],
+  "rules": {
+    "no-unused-vars": 0,
+  },
 }
--- a/toolkit/components/passwordmgr/test/mochitest/test_passwords_in_type_password.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_passwords_in_type_password.html
@@ -86,18 +86,16 @@ runChecksAfterCommonInit(() => startTest
 
 /** Test for Login Manager: 242956 (Stored password is inserted into a
     readable text input on a second page) **/
 
 // Make sure that pwmgr only puts passwords into type=password <input>s.
 // Might as well test the converse, too (username in password field).
 
 function startTest() {
-  var form, input;
-
   is($_(1, "uname").value, "", "Checking for unfilled username 1");
   is($_(1, "pword").value, "", "Checking for unfilled password 1");
 
   is($_(2, "uname").value, "testpass", "Checking for password not username 2");
   is($_(2, "pword").value, "", "Checking for unfilled password 2");
 
   is($_(3, "uname").value, "", "Checking for unfilled username 3");
   is($_(3, "pword").value, "testuser", "Checking for unfilled password 3");
--- a/toolkit/components/passwordmgr/test/unit/test_module_LoginImport.js
+++ b/toolkit/components/passwordmgr/test/unit/test_module_LoginImport.js
@@ -176,17 +176,16 @@ add_task(function* test_import()
 
 /**
  * Tests imports of NULL values due to a downgraded database.
  */
 add_task(function* test_import_downgraded()
 {
   let store = new LoginStore(getTempFile("test-import-downgraded.json").path);
   let loginsSqlite = getTempFile("test-logins-downgraded.sqlite").path;
-  let loginList = TestData.loginList();
 
   // Create and populate the SQLite database first.
   let connection = yield Sqlite.openConnection({ path: loginsSqlite });
   try {
     yield promiseCreateDatabaseSchema(connection);
     yield connection.setSchemaVersion(3);
     yield promiseInsertLoginInfo(connection, TestData.formLogin({
       guid: gUUIDGenerator.generateUUID().toString(),
--- a/toolkit/components/places/PlacesSyncUtils.jsm
+++ b/toolkit/components/places/PlacesSyncUtils.jsm
@@ -46,42 +46,23 @@ const BookmarkSyncUtils = PlacesSyncUtil
     QUERY: "query",
     FOLDER: "folder",
     LIVEMARK: "livemark",
     SEPARATOR: "separator",
   },
 
   /**
    * Fetches a folder's children, ordered by their position within the folder.
-   * Children without a GUID will be assigned one.
    */
   fetchChildGuids: Task.async(function* (parentGuid) {
     PlacesUtils.SYNC_BOOKMARK_VALIDATORS.guid(parentGuid);
 
     let db = yield PlacesUtils.promiseDBConnection();
     let children = yield fetchAllChildren(db, parentGuid);
-    let childGuids = [];
-    let guidsToSet = new Map();
-    for (let child of children) {
-      let guid = child.guid;
-      if (!PlacesUtils.isValidGuid(guid)) {
-        // Give the child a GUID if it doesn't have one. This shouldn't happen,
-        // but the old bookmarks engine code does this, so we'll match its
-        // behavior until we're sure this can be removed.
-        guid = yield generateGuid(db);
-        BookmarkSyncLog.warn(`fetchChildGuids: Assigning ${
-          guid} to item without GUID ${child.id}`);
-        guidsToSet.set(child.id, guid);
-      }
-      childGuids.push(guid);
-    }
-    if (guidsToSet.size > 0) {
-      yield setGuids(guidsToSet);
-    }
-    return childGuids;
+    return children.map(child => child.guid);
   }),
 
   /**
    * Reorders a folder's children, based on their order in the array of GUIDs.
    * This method is similar to `Bookmarks.reorder`, but leaves missing entries
    * in place instead of moving them to the end of the folder.
    *
    * Sync uses this method to reorder all synced children after applying all
@@ -148,70 +129,38 @@ const BookmarkSyncUtils = PlacesSyncUtil
    * mobile root.
    */
   clear: Task.async(function* (folderGuid) {
     let folderId = yield PlacesUtils.promiseItemId(folderGuid);
     PlacesUtils.bookmarks.removeFolderChildren(folderId, SOURCE_SYNC);
   }),
 
   /**
-   * Ensures an item with the |itemId| has a GUID, assigning one if necessary.
-   * We should never have a bookmark without a GUID, but the old Sync bookmarks
-   * engine code does this, so we'll match its behavior until we're sure it's
-   * not needed.
-   *
-   * This method can be removed and replaced with `PlacesUtils.promiseItemGuid`
-   * once bug 1294291 lands.
-   *
-   * @return {Promise} resolved once the GUID has been updated.
-   * @resolves to the existing or new GUID.
-   * @rejects if the item does not exist.
-   */
-  ensureGuidForId: Task.async(function* (itemId) {
-    let guid;
-    try {
-      // Use the existing GUID if it exists. `promiseItemGuid` caches the GUID
-      // as a side effect, and throws if it's invalid.
-      guid = yield PlacesUtils.promiseItemGuid(itemId);
-    } catch (ex) {
-      BookmarkSyncLog.warn(`ensureGuidForId: Error fetching GUID for ${
-        itemId}`, ex);
-      if (!isInvalidCachedGuidError(ex)) {
-        throw ex;
-      }
-      // Give the item a GUID if it doesn't have one.
-      guid = yield PlacesUtils.withConnectionWrapper(
-        "BookmarkSyncUtils: ensureGuidForId", Task.async(function* (db) {
-          let guid = yield generateGuid(db);
-          BookmarkSyncLog.warn(`ensureGuidForId: Assigning ${
-            guid} to item without GUID ${itemId}`);
-          return setGuid(db, itemId, guid);
-        })
-      );
-
-    }
-    return guid;
-  }),
-
-  /**
    * Changes the GUID of an existing item.
    *
    * @return {Promise} resolved once the GUID has been changed.
    * @resolves to the new GUID.
    * @rejects if the old GUID does not exist.
    */
   changeGuid: Task.async(function* (oldGuid, newGuid) {
     PlacesUtils.SYNC_BOOKMARK_VALIDATORS.guid(oldGuid);
+    PlacesUtils.SYNC_BOOKMARK_VALIDATORS.guid(newGuid);
 
     let itemId = yield PlacesUtils.promiseItemId(oldGuid);
     if (PlacesUtils.isRootItem(itemId)) {
       throw new Error(`Cannot change GUID of Places root ${oldGuid}`);
     }
     return PlacesUtils.withConnectionWrapper("BookmarkSyncUtils: changeGuid",
-      db => setGuid(db, itemId, newGuid));
+      Task.async(function* (db) {
+        yield db.executeCached(`UPDATE moz_bookmarks SET guid = :newGuid
+          WHERE id = :itemId`, { newGuid, itemId });
+        PlacesUtils.invalidateCachedGuidFor(itemId);
+        return newGuid;
+      })
+    );
   }),
 
   /**
    * Updates a bookmark with synced properties. Only Sync should call this
    * method; other callers should use `Bookmarks.update`.
    *
    * The following properties are supported:
    *  - kind: Optional.
@@ -345,21 +294,16 @@ function updateChildIndex(children, chil
 function notify(observers, notification, args) {
   for (let observer of observers) {
     try {
       observer[notification](...args);
     } catch (ex) {}
   }
 }
 
-function isInvalidCachedGuidError(error) {
-  return error && error.message ==
-    "Trying to update the GUIDs cache with an invalid GUID";
-}
-
 // A helper for whenever we want to know if a GUID doesn't exist in the places
 // database. Primarily used to detect orphans on incoming records.
 var GUIDMissing = Task.async(function* (guid) {
   try {
     yield PlacesUtils.promiseItemId(guid);
     return false;
   } catch (ex) {
     if (ex.message == "no item found for the given GUID") {
@@ -780,33 +724,16 @@ var updateBookmarkMetadata = Task.async(
       PlacesUtils.annotations.EXPIRE_NEVER,
       SOURCE_SYNC);
     newItem.query = updateInfo.query;
   }
 
   return newItem;
 });
 
-function generateGuid(db) {
-  return db.executeCached("SELECT GENERATE_GUID() AS guid").then(rows =>
-    rows[0].getResultByName("guid"));
-}
-
-function setGuids(guids) {
-  return PlacesUtils.withConnectionWrapper("BookmarkSyncUtils: setGuids",
-    db => db.executeTransaction(function* () {
-      let promises = [];
-      for (let [itemId, newGuid] of guids) {
-        promises.push(setGuid(db, itemId, newGuid));
-      }
-      return Promise.all(promises);
-    })
-  );
-}
-
 var setGuid = Task.async(function* (db, itemId, newGuid) {
   yield db.executeCached(`UPDATE moz_bookmarks SET guid = :newGuid
     WHERE id = :itemId`, { newGuid, itemId });
   PlacesUtils.invalidateCachedGuidFor(itemId);
   return newGuid;
 });
 
 function validateNewBookmark(info) {
--- a/toolkit/components/places/tests/unit/test_sync_utils.js
+++ b/toolkit/components/places/tests/unit/test_sync_utils.js
@@ -108,33 +108,16 @@ var populateTree = Task.async(function* 
     }
 
     guids[item.title] = guid;
   }
 
   return guids;
 });
 
-function* insertWithoutGuid(info) {
-  let item = yield PlacesUtils.bookmarks.insert(info);
-  let id = yield PlacesUtils.promiseItemId(item.guid);
-
-  // All Places methods ensure we specify a valid GUID, so we insert
-  // an item and remove its GUID by modifying the DB directly.
-  yield PlacesUtils.withConnectionWrapper(
-    "test_sync_utils: insertWithoutGuid", db => db.executeCached(
-      `UPDATE moz_bookmarks SET guid = NULL WHERE guid = :guid`,
-      { guid: item.guid }
-    )
-  );
-  PlacesUtils.invalidateCachedGuidFor(id);
-
-  return { id, item };
-}
-
 add_task(function* test_order() {
   do_print("Insert some bookmarks");
   let guids = yield populateTree(PlacesUtils.bookmarks.menuGuid, {
     kind: "bookmark",
     title: "childBmk",
     url: "http://getfirefox.com",
   }, {
     kind: "bookmark",
@@ -178,94 +161,16 @@ add_task(function* test_order() {
       PlacesUtils.bookmarks.menuGuid);
     deepEqual(childGuids, [guids.childBmk, guids.siblingBmk, guids.siblingSep,
       guids.siblingFolder], "Nonexistent children should be ignored");
   }
 
   yield PlacesUtils.bookmarks.eraseEverything();
 });
 
-add_task(function* test_fetchChildGuids_ensure_guids() {
-  let firstWithGuid = yield PlacesUtils.bookmarks.insert({
-    parentGuid: PlacesUtils.bookmarks.menuGuid,
-    type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
-    url: "https://mozilla.org",
-  });
-
-  let { item: secondWithoutGuid } = yield* insertWithoutGuid({
-    parentGuid: PlacesUtils.bookmarks.menuGuid,
-    type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
-    url: "https://example.com",
-  });
-
-  let thirdWithGuid = yield PlacesUtils.bookmarks.insert({
-    parentGuid: PlacesUtils.bookmarks.menuGuid,
-    type: PlacesUtils.bookmarks.TYPE_FOLDER,
-  });
-
-  do_print("Children without a GUID should be assigned one");
-  let childGuids = yield PlacesSyncUtils.bookmarks.fetchChildGuids(
-    PlacesUtils.bookmarks.menuGuid);
-  equal(childGuids.length, 3, "Should include all children");
-  equal(childGuids[0], firstWithGuid.guid,
-    "Should include first child GUID");
-  notEqual(childGuids[1], secondWithoutGuid.guid,
-    "Should assign new GUID to second child");
-  equal(childGuids[2], thirdWithGuid.guid,
-    "Should include third child GUID");
-
-  yield PlacesUtils.bookmarks.eraseEverything();
-});
-
-add_task(function* test_ensureGuidForId_invalid() {
-  yield rejects(PlacesSyncUtils.bookmarks.ensureGuidForId(-1),
-    "Should reject invalid item IDs");
-
-  let item = yield PlacesUtils.bookmarks.insert({
-    parentGuid: PlacesUtils.bookmarks.menuGuid,
-    type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
-    url: "https://mozilla.org",
-  });
-  let id = yield PlacesUtils.promiseItemId(item.guid);
-  yield PlacesUtils.bookmarks.remove(item);
-  yield rejects(PlacesSyncUtils.bookmarks.ensureGuidForId(id),
-    "Should reject nonexistent item IDs");
-});
-
-add_task(function* test_ensureGuidForId() {
-  do_print("Item with GUID");
-  {
-    let item = yield PlacesUtils.bookmarks.insert({
-      parentGuid: PlacesUtils.bookmarks.menuGuid,
-      type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
-      url: "https://mozilla.org",
-    });
-    let id = yield PlacesUtils.promiseItemId(item.guid);
-    let guid = yield PlacesSyncUtils.bookmarks.ensureGuidForId(id);
-    equal(guid, item.guid, "Should return GUID if one exists");
-  }
-
-  do_print("Item without GUID");
-  {
-    let { id, item } = yield* insertWithoutGuid({
-      parentGuid: PlacesUtils.bookmarks.menuGuid,
-      type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
-      url: "https://example.com",
-    });
-    let guid = yield PlacesSyncUtils.bookmarks.ensureGuidForId(id);
-    notEqual(guid, item.guid, "Should assign new GUID to item without one");
-    equal(yield PlacesUtils.promiseItemGuid(id), guid,
-      "Should map ID to new GUID");
-    equal(yield PlacesUtils.promiseItemId(guid), id,
-      "Should map new GUID to ID");
-  }
-
-  yield PlacesUtils.bookmarks.eraseEverything();
-});
-
 add_task(function* test_changeGuid_invalid() {
   yield rejects(PlacesSyncUtils.bookmarks.changeGuid(makeGuid()),
     "Should require a new GUID");
   yield rejects(PlacesSyncUtils.bookmarks.changeGuid(makeGuid(), "!@#$"),
     "Should reject invalid GUIDs");
   yield rejects(PlacesSyncUtils.bookmarks.changeGuid(makeGuid(), makeGuid()),
     "Should reject nonexistent item GUIDs");
   yield rejects(
--- a/toolkit/content/tests/browser/browser.ini
+++ b/toolkit/content/tests/browser/browser.ini
@@ -15,16 +15,18 @@ support-files =
   file_multipleAudio.html
 [browser_bug295977_autoscroll_overflow.js]
 [browser_bug451286.js]
 skip-if = !e10s
 [browser_bug594509.js]
 [browser_bug982298.js]
 [browser_bug1198465.js]
 [browser_contentTitle.js]
+[browser_crash_previous_frameloader.js]
+run-if = e10s && crashreporter
 [browser_default_image_filename.js]
 [browser_f7_caret_browsing.js]
 [browser_findbar.js]
 [browser_label_textlink.js]
 [browser_isSynthetic.js]
 support-files =
   empty.png
 [browser_keyevents_during_autoscrolling.js]
new file mode 100644
--- /dev/null
+++ b/toolkit/content/tests/browser/browser_crash_previous_frameloader.js
@@ -0,0 +1,114 @@
+"use strict";
+
+/**
+ * Cleans up the .dmp and .extra file from a crash.
+ *
+ * @param subject (nsISupports)
+ *        The subject passed through the ipc:content-shutdown
+ *        observer notification when a content process crash has
+ *        occurred.
+ */
+function cleanUpMinidump(subject) {
+  Assert.ok(subject instanceof Ci.nsIPropertyBag2,
+            "Subject needs to be a nsIPropertyBag2 to clean up properly");
+  let dumpID = subject.getPropertyAsAString("dumpID");
+
+  Assert.ok(dumpID, "There should be a dumpID");
+  if (dumpID) {
+    let dir = Services.dirsvc.get("ProfD", Ci.nsIFile);
+    dir.append("minidumps");
+
+    let file = dir.clone();
+    file.append(dumpID + ".dmp");
+    file.remove(true);
+
+    file = dir.clone();
+    file.append(dumpID + ".extra");
+    file.remove(true);
+  }
+}
+
+/**
+ * This test ensures that if a remote frameloader crashes after
+ * the frameloader owner swaps it out for a new frameloader,
+ * that a oop-browser-crashed event is not sent to the new
+ * frameloader's browser element.
+ */
+add_task(function* test_crash_in_previous_frameloader() {
+  // On debug builds, crashing tabs results in much thinking, which
+  // slows down the test and results in intermittent test timeouts,
+  // so we'll pump up the expected timeout for this test.
+  requestLongerTimeout(2);
+
+  if (!gMultiProcessBrowser) {
+    Assert.ok(false, "This test should only be run in multi-process mode.");
+    return;
+  }
+
+  yield BrowserTestUtils.withNewTab({
+    gBrowser,
+    url: "http://example.com",
+  }, function*(browser) {
+    // First, sanity check...
+    Assert.ok(browser.isRemoteBrowser,
+              "This browser needs to be remote if this test is going to " +
+              "work properly.");
+
+    // We will wait for the oop-browser-crashed event to have
+    // a chance to appear. That event is fired when TabParents
+    // are destroyed, and that occurs _before_ ContentParents
+    // are destroyed, so we'll wait on the ipc:content-shutdown
+    // observer notification, which is fired when a ContentParent
+    // goes away. After we see this notification, oop-browser-crashed
+    // events should have fired.
+    let contentProcessGone = TestUtils.topicObserved("ipc:content-shutdown");
+    let sawTabCrashed = false;
+    let onTabCrashed = () => {
+      sawTabCrashed = true;
+    };
+
+    browser.addEventListener("oop-browser-crashed", onTabCrashed);
+
+    // The name of the game is to cause a crash in a remote browser,
+    // and then immediately swap out the browser for a non-remote one.
+    yield ContentTask.spawn(browser, null, function() {
+      const Cu = Components.utils;
+      Cu.import("resource://gre/modules/ctypes.jsm");
+      Cu.import("resource://gre/modules/Timer.jsm");
+
+      let dies = function() {
+        privateNoteIntentionalCrash();
+        let zero = new ctypes.intptr_t(8);
+        let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
+        badptr.contents
+      };
+
+      // Use a timeout to give the parent a little extra time
+      // to flip the remoteness of the browser. This has the
+      // potential to be a bit race-y, since in theory, the
+      // setTimeout could complete before the parent finishes
+      // the remoteness flip, which would mean we'd get the
+      // oop-browser-crashed event, and we'll fail here.
+      // Unfortunately, I can't find a way around that right
+      // now, since you cannot send a frameloader a message
+      // once its been replaced.
+      setTimeout(() => {
+        dump("\nEt tu, Brute?\n");
+        dies();
+      }, 0);
+    });
+
+    gBrowser.updateBrowserRemoteness(browser, false);
+    info("Waiting for content process to go away.");
+    let [subject, data] = yield contentProcessGone;
+
+    // If we don't clean up the minidump, the harness will
+    // complain.
+    cleanUpMinidump(subject);
+
+    info("Content process is gone!");
+    Assert.ok(!sawTabCrashed,
+              "Should not have seen the oop-browser-crashed event.");
+    browser.removeEventListener("oop-browser-crashed", onTabCrashed);
+  });
+});
--- a/tools/lint/flake8.lint
+++ b/tools/lint/flake8.lint
@@ -126,16 +126,17 @@ def lint(files, **lintargs):
 LINTER = {
     'name': "flake8",
     'description': "Python linter",
     'include': [
         'python/mozlint',
         'taskcluster',
         'testing/firefox-ui',
         'testing/marionette/client',
+        'testing/marionette/harness',
         'testing/puppeteer',
         'testing/talos/',
         'tools/lint',
     ],
     'exclude': [],
     'extensions': EXTENSIONS,
     'type': 'external',
     'payload': lint,
--- a/widget/tests/window_composition_text_querycontent.xul
+++ b/widget/tests/window_composition_text_querycontent.xul
@@ -248,16 +248,37 @@ function checkRect(aRect, aExpectedRect,
   is(aRect.width, aExpectedRect.width, aMessage + ": width is wrong");
   is(aRect.height, aExpectedRect.height, aMessage + ": height is wrong");
   return aRect.left == aExpectedRect.left &&
          aRect.top == aExpectedRect.top &&
          aRect.width == aExpectedRect.width &&
          aRect.height == aExpectedRect.height;
 }
 
+function checkRectArray(aQueryTextRectArrayResult, aExpectedTextRectArray, aMessage)
+{
+  for (var i = 1; i < aExpectedTextRectArray.length; ++i) {
+    var rect = { left: {}, top: {}, width: {}, height: {} };
+    try {
+      aQueryTextRectArrayResult.getCharacterRect(i, rect.left, rect.top, rect.width, rect.height);
+    } catch (e) {
+      ok(false, aMessage + ": failed to retrieve " + i + "th rect (" + e + ")");
+      return false;
+    }
+    function toRect(aRect)
+    {
+      return { left: aRect.left.value, top: aRect.top.value, width: aRect.width.value, height: aRect.height.value };
+    }
+    if (!checkRect(toRect(rect), aExpectedTextRectArray[i], aMessage + " " + i + "th rect")) {
+      return false;
+    }
+  }
+  return true;
+}
+
 function checkRectContainsRect(aRect, aContainer, aMessage)
 {
   var container = { left: Math.ceil(aContainer.left),
                     top:  Math.ceil(aContainer.top),
                     width: Math.floor(aContainer.width),
                     height: Math.floor(aContainer.height) };
 
   var ret = container.left <= aRect.left &&
@@ -2277,37 +2298,58 @@ function runQueryTextRectInContentEditab
   if (!checkQueryContentResult(c, description + "rect for 'c'")) {
     return;
   }
 
   is(c.top, b.top, description + "'b' and 'c' should be at same top");
   isSimilarTo(c.left, b.left + b.width, 2, description + "left of 'c' should be at similar to right of 'b'");
   is(c.height, b.height, description + "'b' and 'c' should be same height");
 
+  // "abc" as array
+  var abcAsArray = synthesizeQueryTextRectArray(kLFLen, 3);
+  if (!checkQueryContentResult(abcAsArray, description + "rect array for 'abc'") ||
+      !checkRectArray(abcAsArray, [a, b, c], description + "query text rect array result of 'abc' should match with each query text rect result")) {
+    return;
+  }
+
   // 2nd <p> (can be computed with the rect of 'c')
   var p2 = synthesizeQueryTextRect(kLFLen + 3, 1);
   if (!checkQueryContentResult(p2, description + "rect for 2nd <p>")) {
     return;
   }
 
   is(p2.top, c.top, description + "'c' and a line breaker caused by 2nd <p> should be at same top");
   isSimilarTo(p2.left, c.left + c.width, 2, description + "left of a line breaker caused by 2nd <p> should be at similar to right of 'c'");
   is(p2.height, c.height, description + "'c' and a line breaker caused by 2nd <p> should be same height");
 
+  // 2nd <p> as array
+  var p2AsArray = synthesizeQueryTextRectArray(kLFLen + 3, 1);
+  if (!checkQueryContentResult(p2AsArray, description + "2nd <p>'s line breaker as array") ||
+      !checkRectArray(p2AsArray, [p2], description + "query text rect array result of 2nd <p> should match with each query text rect result")) {
+    return;
+  }
+
   if (kLFLen > 1) {
     // \n of \r\n
     var p2_2 = synthesizeQueryTextRect(kLFLen + 4, 1);
-    if (!checkQueryContentResult(p2_2, description + "rect for \n of \r\n caused by 2nd <p>")) {
+    if (!checkQueryContentResult(p2_2, description + "rect for \\n of \\r\\n caused by 2nd <p>")) {
       return;
     }
 
     is(p2_2.top, p2.top, description + "'\\r' and '\\n' should be at same top");
     is(p2_2.left, p2.left, description + "'\\r' and '\\n' should be at same top");
     is(p2_2.height, p2.height, description + "'\\r' and '\\n' should be same height");
     is(p2_2.width, p2.width, description + "'\\r' and '\\n' should be same width");
+
+    // \n of \r\n as array
+    var p2_2AsArray = synthesizeQueryTextRectArray(kLFLen + 4, 1);
+    if (!checkQueryContentResult(p2_2AsArray, description + "rect array for \\n of \\r\\n caused by 2nd <p>") ||
+        !checkRectArray(p2_2AsArray, [p2_2], description + "query text rect array result of \\n of \\r\\n caused by 2nd <p> should match with each query text rect result")) {
+      return;
+    }
   }
 
   // "d"
   var d = synthesizeQueryTextRect(kLFLen * 2 + 3, 1);
   if (!checkQueryContentResult(d, description + "rect for 'd'")) {
     return;
   }
 
@@ -2330,37 +2372,58 @@ function runQueryTextRectInContentEditab
   if (!checkQueryContentResult(f, description + "rect for 'f'")) {
     return;
   }
 
   is(f.top, e.top, description + "'e' and 'f' should be at same top");
   isSimilarTo(f.left, e.left + e.width, 2, description + "left of 'f' should be at similar to right of 'e'");
   is(f.height, e.height, description + "'e' and 'f' should be same height");
 
+  // "def" as array
+  var defAsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 3, 3);
+  if (!checkQueryContentResult(defAsArray, description + "rect array for 'def'") ||
+      !checkRectArray(defAsArray, [d, e, f], description + "query text rect array result of 'def' should match with each query text rect result")) {
+    return;
+  }
+
   // next of "f" (can be computed with rect of 'f')
   var next_f = synthesizeQueryTextRect(kLFLen * 2 + 6, 1);
   if (!checkQueryContentResult(next_f, description + "rect for next of 'f'")) {
     return;
   }
 
   is(next_f.top, d.top, 2, description + "'f' and next of 'f' should be at same top");
   isSimilarTo(next_f.left, f.left + f.width, 2, description + "left of next of 'f' should be at similar to right of 'f'");
   is(next_f.height, d.height, description + "'f' and next of 'f' should be same height");
 
+  // next of "f" as array
+  var next_fAsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 6, 1);
+  if (!checkQueryContentResult(next_fAsArray, description + "rect array for next of 'f'") ||
+      !checkRectArray(next_fAsArray, [next_f], description + "query text rect array result of next of 'f' should match with each query text rect result")) {
+    return;
+  }
+
   // too big offset for the editor
   var tooBigOffset = synthesizeQueryTextRect(kLFLen * 2 + 7, 1);
   if (!checkQueryContentResult(tooBigOffset, description + "rect for too big offset")) {
     return;
   }
 
   is(tooBigOffset.top, next_f.top, description + "too big offset and next of 'f' should be at same top");
   is(tooBigOffset.left, next_f.left, description + "too big offset and next of 'f' should be at same left");
   is(tooBigOffset.height, next_f.height, description + "too big offset and next of 'f' should be same height");
   is(tooBigOffset.width, next_f.width, description + "too big offset and next of 'f' should be same width");
 
+  // too big offset for the editors as array
+  var tooBigOffsetAsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 7, 1);
+  if (!checkQueryContentResult(tooBigOffsetAsArray, description + "rect array for too big offset") ||
+      !checkRectArray(tooBigOffsetAsArray, [tooBigOffset], description + "query text rect array result with too big offset should match with each query text rect result")) {
+    return;
+  }
+
   contenteditable.innerHTML = "<p>abc</p><p>def</p><p><br></p>";
                       // \n    0  123    4  567    8  9
                       // \r\n  01 234    56 789    01 23
 
   description = "runTextRectInContentEditableTest: \"" + contenteditable.innerHTML + "\", ";
 
   // "f"
   f = synthesizeQueryTextRect(kLFLen * 2 + 5, 1);
@@ -2377,74 +2440,116 @@ function runQueryTextRectInContentEditab
   if (!checkQueryContentResult(p3, description + "rect for 3rd <p>")) {
     return;
   }
 
   is(p3.top, f.top, description + "'f' and a line breaker caused by 3rd <p> should be at same top");
   is(p3.height, f.height, description + "'f' and a line breaker caused by 3rd <p> should be same height");
   isSimilarTo(p3.left, f.left + f.width, 2, description + "left of a line breaker caused by 3rd <p> should be similar to right of 'f'");
 
+  // 3rd <p> as array
+  var p3AsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 6, 1);
+  if (!checkQueryContentResult(p3AsArray, description + "3rd <p>'s line breaker as array") ||
+      !checkRectArray(p3AsArray, [p3], description + "query text rect array result of 3rd <p> should match with each query text rect result")) {
+    return;
+  }
+
   if (kLFLen > 1) {
     // \n of \r\n
     var p3_2 = synthesizeQueryTextRect(kLFLen * 2 + 7, 1);
-    if (!checkQueryContentResult(p3_2, description + "rect for \n of \r\n caused by 3rd <p>")) {
+    if (!checkQueryContentResult(p3_2, description + "rect for \\n of \\r\\n caused by 3rd <p>")) {
       return;
     }
 
     is(p3_2.top, p3.top, description + "'\\r' and '\\n' should be at same top");
     is(p3_2.left, p3.left, description + "'\\r' and '\\n' should be at same top");
     is(p3_2.height, p3.height, description + "'\\r' and '\\n' should be same height");
     is(p3_2.width, p3.width, description + "'\\r' and '\\n' should be same width");
+
+    // \n of \r\n as array
+    var p3_2AsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 7, 1);
+    if (!checkQueryContentResult(p3_2AsArray, description + "rect array for \\n of \\r\\n caused by 3rd <p>") ||
+        !checkRectArray(p3_2AsArray, [p3_2], description + "query text rect array result of \\n of \\r\\n caused by 3rd <p> should match with each query text rect result")) {
+      return;
+    }
   }
 
   // <br> in 3rd <p>
   var br = synthesizeQueryTextRect(kLFLen * 3 + 6, 1);
   if (!checkQueryContentResult(br, description + "rect for <br> in 3rd <p>")) {
     return;
   }
 
   isGreaterThan(br.top, d.top + d.height, description + "a line breaker caused by <br> in 3rd <p> should be greater than bottom of 'd'");
   isSimilarTo(br.height, d.height, 2, description + "'d' and a line breaker caused by <br> in 3rd <p> should be similar height");
   is(br.left, d.left, description + "left of a line breaker caused by <br> in 3rd <p> should be same left of 'd'");
 
+  // <br> in 3rd <p> as array
+  var brAsArray = synthesizeQueryTextRectArray(kLFLen * 3 + 6, 1);
+  if (!checkQueryContentResult(brAsArray, description + "<br> in 3rd <p> as array") ||
+      !checkRectArray(brAsArray, [br], description + "query text rect array result of <br> in 3rd <p> should match with each query text rect result")) {
+    return;
+  }
+
   if (kLFLen > 1) {
     // \n of \r\n
     var br_2 = synthesizeQueryTextRect(kLFLen * 3 + 7, 1);
-    if (!checkQueryContentResult(br_2, description + "rect for \n of \r\n caused by <br> in 3rd <p>")) {
+    if (!checkQueryContentResult(br_2, description + "rect for \\n of \\r\\n caused by <br> in 3rd <p>")) {
       return;
     }
 
     is(br_2.top, br.top, description + "'\\r' and '\\n' should be at same top");
     is(br_2.left, br.left, description + "'\\r' and '\\n' should be at same top");
     is(br_2.height, br.height, description + "'\\r' and '\\n' should be same height");
     is(br_2.width, br.width, description + "'\\r' and '\\n' should be same width");
+
+    // \n of \r\n as array
+    var br_2AsArray = synthesizeQueryTextRectArray(kLFLen * 3 + 7, 1);
+    if (!checkQueryContentResult(br_2AsArray, description + "rect array for \\n of \\r\\n caused by <br> in 3rd <p>") ||
+        !checkRectArray(br_2AsArray, [br_2], description + "query text rect array result of \\n of \\r\\n caused by <br> in 3rd <p> should match with each query text rect result")) {
+      return;
+    }
   }
 
   // next of <br> in 3rd <p>
   var next_br = synthesizeQueryTextRect(kLFLen * 4 + 6, 1);
   if (!checkQueryContentResult(next_br, description + "rect for next of <br> in 3rd <p>")) {
     return;
   }
 
   is(next_br.top, br.top, description + "next of <br> and <br> should be at same top");
   is(next_br.left, br.left, description + "next of <br> and <br> should be at same left");
   is(next_br.height, br.height, description + "next of <br> and <br> should be same height");
   is(next_br.width, br.width, description + "next of <br> and <br> should be same width");
 
+  // next of <br> in 3rd <p> as array
+  var next_brAsArray = synthesizeQueryTextRectArray(kLFLen * 4 + 6, 1);
+  if (!checkQueryContentResult(next_brAsArray, description + "rect array for next of <br> in 3rd <p>") ||
+      !checkRectArray(next_brAsArray, [next_br], description + "query text rect array result of next of <br> in 3rd <p> should match with each query text rect result")) {
+    return;
+  }
+
   // too big offset for the editor
   tooBigOffset = synthesizeQueryTextRect(kLFLen * 4 + 7, 1);
   if (!checkQueryContentResult(tooBigOffset, description + "rect for too big offset")) {
     return;
   }
 
   is(tooBigOffset.top, next_br.top, description + "too big offset and next of 3rd <p> should be at same top");
   is(tooBigOffset.left, next_br.left, description + "too big offset and next of 3rd <p> should be at same left");
   is(tooBigOffset.height, next_br.height, description + "too big offset and next of 3rd <p> should be same height");
   is(tooBigOffset.width, next_br.width, description + "too big offset and next of 3rd <p> should be same width");
 
+  // too big offset for the editors as array
+  tooBigOffsetAsArray = synthesizeQueryTextRectArray(kLFLen * 4 + 7, 1);
+  if (!checkQueryContentResult(tooBigOffsetAsArray, description + "rect array for too big offset") ||
+      !checkRectArray(tooBigOffsetAsArray, [tooBigOffset], description + "query text rect array result with too big offset should match with each query text rect result")) {
+    return;
+  }
+
   contenteditable.innerHTML = "<p>abc</p><p>def</p><p></p>";
                       // \n    0  123    4  567    8
                       // \r\n  01 234    56 789    0
 
   description = "runTextRectInContentEditableTest: \"" + contenteditable.innerHTML + "\", ";
 
   // "f"
   f = synthesizeQueryTextRect(kLFLen * 2 + 5, 1);
@@ -2452,59 +2557,87 @@ function runQueryTextRectInContentEditab
     return;
   }
 
   is(f.top, e.top, description + "'e' and 'f' should be at same top");
   isSimilarTo(f.left, e.left + e.width, 2, description + "left of 'f' should be at similar to right of 'e'");
   is(f.height, e.height, description + "'e' and 'f' should be same height");
 
   // 3rd <p> (can be computed with rect of 'f')
-  var p3 = synthesizeQueryTextRect(kLFLen * 2 + 6, 1);
+  p3 = synthesizeQueryTextRect(kLFLen * 2 + 6, 1);
   if (!checkQueryContentResult(p3, description + "rect for 3rd <p>")) {
     return;
   }
 
   is(p3.top, f.top, description + "'f' and a line breaker caused by 3rd <p> should be at same top");
   is(p3.height, f.height, description + "'f' and a line breaker caused by 3rd <p> should be same height");
   isSimilarTo(p3.left, f.left + f.width, 2, description + "left of a line breaker caused by 3rd <p> should be similar to right of 'f'");
 
+  // 3rd <p> as array
+  p3AsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 6, 1);
+  if (!checkQueryContentResult(p3AsArray, description + "3rd <p>'s line breaker as array") ||
+      !checkRectArray(p3AsArray, [p3], description + "query text rect array result of 3rd <p> should match with each query text rect result")) {
+    return;
+  }
+
   if (kLFLen > 1) {
     // \n of \r\n
     p3_2 = synthesizeQueryTextRect(kLFLen * 2 + 7, 1);
-    if (!checkQueryContentResult(p3_2, description + "rect for \n of \r\n caused by 3rd <p>")) {
+    if (!checkQueryContentResult(p3_2, description + "rect for \\n of \\r\\n caused by 3rd <p>")) {
       return;
     }
 
     is(p3_2.top, p3.top, description + "'\\r' and '\\n' should be at same top");
     is(p3_2.left, p3.left, description + "'\\r' and '\\n' should be at same top");
     is(p3_2.height, p3.height, description + "'\\r' and '\\n' should be same height");
     is(p3_2.width, p3.width, description + "'\\r' and '\\n' should be same width");
+
+    // \n of \r\n as array
+    p3_2AsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 7, 1);
+    if (!checkQueryContentResult(p3_2AsArray, description + "rect array for \\n of \\r\\n caused by 3rd <p>") ||
+        !checkRectArray(p3_2AsArray, [p3_2], description + "query text rect array result of \\n of \\r\\n caused by 3rd <p> should match with each query text rect result")) {
+      return;
+    }
   }
 
   // next of 3rd <p>
   var next_p3 = synthesizeQueryTextRect(kLFLen * 3 + 6, 1);
   if (!checkQueryContentResult(next_p3, description + "rect for next of 3rd <p>")) {
     return;
   }
 
   isGreaterThan(next_p3.top, d.top + d.height, description + "top of next of 3rd <p> should equal to or be bigger than bottom of 'd'");
   isSimilarTo(next_p3.left, d.left, 2, description + "left of next of 3rd <p> should be at similar to left of 'd'");
   isSimilarTo(next_p3.height, d.height, 2, description + "next of 3rd <p> and 'd' should be similar height");
 
+  // next of 3rd <p> as array
+  var next_p3AsArray = synthesizeQueryTextRectArray(kLFLen * 3 + 6, 1);
+  if (!checkQueryContentResult(next_p3AsArray, description + "next of 3rd <p> as array") ||
+      !checkRectArray(next_p3AsArray, [next_p3], description + "query text rect array result of next of 3rd <p> should match with each query text rect result")) {
+    return;
+  }
+
   // too big offset for the editor
   tooBigOffset = synthesizeQueryTextRect(kLFLen * 3 + 7, 1);
   if (!checkQueryContentResult(tooBigOffset, description + "rect for too big offset")) {
     return;
   }
 
   is(tooBigOffset.top, next_p3.top, description + "too big offset and next of 3rd <p> should be at same top");
   is(tooBigOffset.left, next_p3.left, description + "too big offset and next of 3rd <p> should be at same left");
   is(tooBigOffset.height, next_p3.height, description + "too big offset and next of 3rd <p> should be same height");
   is(tooBigOffset.width, next_p3.width, description + "too big offset and next of 3rd <p> should be same width");
 
+  // too big offset for the editors as array
+  tooBigOffsetAsArray = synthesizeQueryTextRectArray(kLFLen * 3 + 7, 1);
+  if (!checkQueryContentResult(tooBigOffsetAsArray, description + "rect array for too big offset") ||
+      !checkRectArray(tooBigOffsetAsArray, [tooBigOffset], description + "query text rect array result with too big offset should match with each query text rect result")) {
+    return;
+  }
+
   contenteditable.innerHTML = "abc<br>def";
                       // \n    0123   456
                       // \r\n  01234  567
 
   description = "runTextRectInContentEditableTest: \"" + contenteditable.innerHTML + "\", ";
 
   // "a"
   a = synthesizeQueryTextRect(0, 1);
@@ -2527,37 +2660,58 @@ function runQueryTextRectInContentEditab
   if (!checkQueryContentResult(c, description + "rect for 'c'")) {
     return;
   }
 
   is(c.top, b.top, description + "'b' and 'c' should be at same top");
   isSimilarTo(c.left, b.left + b.width, 2, description + "left of 'c' should be at similar to right of 'b'");
   is(c.height, b.height, description + "'b' and 'c' should be same height");
 
+  // "abc" as array
+  abcAsArray = synthesizeQueryTextRectArray(0, 3);
+  if (!checkQueryContentResult(abcAsArray, description + "rect array for 'abc'") ||
+      !checkRectArray(abcAsArray, [a, b, c], description + "query text rect array result of 'abc' should match with each query text rect result")) {
+    return;
+  }
+
   // <br> (can be computed with the rect of 'c')
   br = synthesizeQueryTextRect(3, 1);
   if (!checkQueryContentResult(br, description + "rect for <br>")) {
     return;
   }
 
   is(br.top, c.top, description + "'c' and a line breaker caused by <br> should be at same top");
   isSimilarTo(br.left, c.left + c.width, 2, description + "left of a line breaker caused by <br> should be at similar to right of 'c'");
   is(br.height, c.height, description + "'c' and a line breaker caused by <br> should be same height");
 
+  // <br> as array
+  brAsArray = synthesizeQueryTextRectArray(3, 1);
+  if (!checkQueryContentResult(brAsArray, description + "<br>'s line breaker as array") ||
+      !checkRectArray(brAsArray, [br], description + "query text rect array result of <br> should match with each query text rect result")) {
+    return;
+  }
+
   if (kLFLen > 1) {
     // \n of \r\n
     var br_2 = synthesizeQueryTextRect(4, 1);
     if (!checkQueryContentResult(br_2, description + "rect for \n of \r\n caused by <br>")) {
       return;
     }
 
     is(br_2.top, br.top, description + "'\\r' and '\\n' should be at same top");
     is(br_2.left, br.left, description + "'\\r' and '\\n' should be at same top");
     is(br_2.height, br.height, description + "'\\r' and '\\n' should be same height");
     is(br_2.width, br.width, description + "'\\r' and '\\n' should be same width");
+
+    // \n of \r\n as array
+    var br_2AsArray = synthesizeQueryTextRectArray(4, 1);
+    if (!checkQueryContentResult(br_2AsArray, description + "rect array for \\n of \\r\\n caused by <br>") ||
+        !checkRectArray(br_2AsArray, [br_2], description + "query text rect array result of \\n of \\r\\n caused by <br> should match with each query text rect result")) {
+      return;
+    }
   }
 
   // "d"
   d = synthesizeQueryTextRect(kLFLen + 3, 1);
   if (!checkQueryContentResult(d, description + "rect for 'd'")) {
     return;
   }
 
@@ -2580,37 +2734,58 @@ function runQueryTextRectInContentEditab
   if (!checkQueryContentResult(f, description + "rect for 'f'")) {
     return;
   }
 
   is(f.top, e.top, description + "'e' and 'f' should be at same top");
   isSimilarTo(f.left, e.left + e.width, 2, description + "left of 'f' should be at similar to right of 'e'");
   is(f.height, e.height, description + "'e' and 'f' should be same height");
 
+  // "def" as array
+  defAsArray = synthesizeQueryTextRectArray(kLFLen + 3, 3);
+  if (!checkQueryContentResult(defAsArray, description + "rect array for 'def'") ||
+      !checkRectArray(defAsArray, [d, e, f], description + "query text rect array result of 'def' should match with each query text rect result")) {
+    return;
+  }
+
   // next of "f" (can be computed with rect of 'f')
   next_f = synthesizeQueryTextRect(kLFLen + 6, 1);
   if (!checkQueryContentResult(next_f, description + "rect for next of 'f'")) {
     return;
   }
 
   is(next_f.top, d.top, 2, description + "'f' and next of 'f' should be at same top");
   isSimilarTo(next_f.left, f.left + f.width, 2, description + "left of next of 'f' should be at similar to right of 'f'");
   is(next_f.height, d.height, description + "'f' and next of 'f' should be same height");
 
+  // next of "f" as array
+  next_fAsArray = synthesizeQueryTextRectArray(kLFLen + 6, 1);
+  if (!checkQueryContentResult(next_fAsArray, description + "rect array for next of 'f'") ||
+      !checkRectArray(next_fAsArray, [next_f], description + "query text rect array result of next of 'f' should match with each query text rect result")) {
+    return;
+  }
+
   // too big offset for the editor
   tooBigOffset = synthesizeQueryTextRect(kLFLen + 7, 1);
   if (!checkQueryContentResult(tooBigOffset, description + "rect for too big offset")) {
     return;
   }
 
   is(tooBigOffset.top, next_f.top, description + "too big offset and next of 'f' should be at same top");
   is(tooBigOffset.left, next_f.left, description + "too big offset and next of 'f' should be at same left");
   is(tooBigOffset.height, next_f.height, description + "too big offset and next of 'f' should be same height");
   is(tooBigOffset.width, next_f.width, description + "too big offset and next of 'f' should be same width");
 
+  // too big offset for the editors as array
+  tooBigOffsetAsArray = synthesizeQueryTextRectArray(kLFLen + 7, 1);
+  if (!checkQueryContentResult(tooBigOffsetAsArray, description + "rect array for too big offset") ||
+      !checkRectArray(tooBigOffsetAsArray, [tooBigOffset], description + "query text rect array result with too big offset should match with each query text rect result")) {
+    return;
+  }
+
   // Note that this case does not have an empty line at the end.
   contenteditable.innerHTML = "abc<br>def<br>";
                       // \n    0123   4567
                       // \r\n  01234  56789
 
   description = "runTextRectInContentEditableTest: \"" + contenteditable.innerHTML + "\", ";
 
   // "f"
@@ -2628,51 +2803,79 @@ function runQueryTextRectInContentEditab
   if (!checkQueryContentResult(br2, description + "rect for 2nd <br>")) {
     return;
   }
 
   is(br2.top, f.top, description + "'f' and a line breaker caused by 2nd <br> should be at same top");
   is(br2.height, f.height, description + "'f' and a line breaker caused by 2nd <br> should be same height");
   isSimilarTo(br2.left, f.left + f.width, 2, description + "left of a line breaker caused by 2nd <br> should be similar to right of 'f'");
 
+  // 2nd <br> as array
+  var br2AsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 6, 1);
+  if (!checkQueryContentResult(br2AsArray, description + "2nd <br>'s line breaker as array") ||
+      !checkRectArray(br2AsArray, [br2], description + "query text rect array result of 2nd <br> should match with each query text rect result")) {
+    return;
+  }
+
   if (kLFLen > 1) {
     // \n of \r\n
     var br2_2 = synthesizeQueryTextRect(kLFLen + 7, 1);
-    if (!checkQueryContentResult(br2_2, description + "rect for \n of \r\n caused by 2nd <br>")) {
+    if (!checkQueryContentResult(br2_2, description + "rect for \\n of \\r\\n caused by 2nd <br>")) {
       return;
     }
 
     is(br2_2.top, br2.top, description + "'\\r' and '\\n' should be at same top");
     is(br2_2.left, br2.left, description + "'\\r' and '\\n' should be at same top");
     is(br2_2.height, br2.height, description + "'\\r' and '\\n' should be same height");
     is(br2_2.width, br2.width, description + "'\\r' and '\\n' should be same width");
+
+    // \n of \r\n as array
+    var br2_2AsArray = synthesizeQueryTextRectArray(kLFLen + 7, 1);
+    if (!checkQueryContentResult(br2_2AsArray, description + "rect array for \\n of \\r\\n caused by 2nd <br>") ||
+        !checkRectArray(br2_2AsArray, [br2_2], description + "query text rect array result of \\n of \\r\\n caused by 2nd <br> should match with each query text rect result")) {
+      return;
+    }
   }
 
   // next of 2nd <br>
   var next_br2 = synthesizeQueryTextRect(kLFLen * 2 + 6, 1);
   if (!checkQueryContentResult(next_br2, description + "rect for next of 2nd <br>")) {
     return;
   }
 
   is(next_br2.top, br2.top, description + "2nd <br> and next of 2nd <br> should be at same top");
   is(next_br2.left, br2.left, description + "2nd <br> and next of 2nd <br> should be at same top");
   is(next_br2.height, br2.height, description + "2nd <br> and next of 2nd <br> should be same height");
   is(next_br2.width, br2.width, description + "2nd <br> and next of 2nd <br> should be same width");
 
+  // next of 2nd <br> as array
+  var next_br2AsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 6, 1);
+  if (!checkQueryContentResult(next_br2AsArray, description + "rect array for next of 2nd <br>") ||
+      !checkRectArray(next_br2AsArray, [next_br2], description + "query text rect array result of next of 2nd <br> should match with each query text rect result")) {
+    return;
+  }
+
   // too big offset for the editor
   tooBigOffset = synthesizeQueryTextRect(kLFLen * 2 + 7, 1);
   if (!checkQueryContentResult(tooBigOffset, description + "rect for too big offset")) {
     return;
   }
 
   is(tooBigOffset.top, next_br2.top, description + "too big offset and next of 2nd <br> should be at same top");
   is(tooBigOffset.left, next_br2.left, description + "too big offset and next of 2nd <br> should be at same left");
   is(tooBigOffset.height, next_br2.height, description + "too big offset and next of 2nd <br> should be same height");
   is(tooBigOffset.width, next_br2.width, description + "too big offset and next of 2nd <br> should be same width");
 
+  // too big offset for the editors as array
+  tooBigOffsetAsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 7, 1);
+  if (!checkQueryContentResult(tooBigOffsetAsArray, description + "rect array for too big offset") ||
+      !checkRectArray(tooBigOffsetAsArray, [tooBigOffset], description + "query text rect array result with too big offset should match with each query text rect result")) {
+    return;
+  }
+
   contenteditable.innerHTML = "abc<br>def<br><br>";
                       // \n    0123   4567   8
                       // \r\n  01234  56789  01
 
   description = "runTextRectInContentEditableTest: \"" + contenteditable.innerHTML + "\", ";
 
   // "f"
   f = synthesizeQueryTextRect(kLFLen + 5, 1);
@@ -2689,73 +2892,115 @@ function runQueryTextRectInContentEditab
   if (!checkQueryContentResult(br2, description + "rect for 2nd <br>")) {
     return;
   }
 
   is(br2.top, f.top, description + "'f' and a line breaker caused by 2nd <br> should be at same top");
   is(br2.height, f.height, description + "'f' and a line breaker caused by 2nd <br> should be same height");
   ok(f.left < br2.left, description + "left of a line breaker caused by 2nd <br> should be bigger than left of 'f', f.left=" + f.left + ", br2.left=" + br2.left);
 
+  // 2nd <br> as array
+  br2AsArray = synthesizeQueryTextRectArray(kLFLen + 6, 1);
+  if (!checkQueryContentResult(br2AsArray, description + "2nd <br>'s line breaker as array") ||
+      !checkRectArray(br2AsArray, [br2], description + "query text rect array result of 2nd <br> should match with each query text rect result")) {
+    return;
+  }
+
   if (kLFLen > 1) {
     // \n of \r\n
     br2_2 = synthesizeQueryTextRect(kLFLen + 7, 1);
-    if (!checkQueryContentResult(br2_2, description + "rect for \n of \r\n caused by 2nd <br>")) {
+    if (!checkQueryContentResult(br2_2, description + "rect for \\n of \\r\\n caused by 2nd <br>")) {
       return;
     }
 
     is(br2_2.top, br2.top, description + "'\\r' and '\\n' should be at same top");
     is(br2_2.left, br2.left, description + "'\\r' and '\\n' should be at same top");
     is(br2_2.height, br2.height, description + "'\\r' and '\\n' should be same height");
     is(br2_2.width, br2.width, description + "'\\r' and '\\n' should be same width");
+
+    // \n of \r\n as array
+    var br2_2AsArray = synthesizeQueryTextRectArray(kLFLen + 7, 1);
+    if (!checkQueryContentResult(br2_2AsArray, description + "rect array for \\n of \\r\\n caused by 2nd <br>") ||
+        !checkRectArray(br2_2AsArray, [br2_2], description + "query text rect array result of \\n of \\r\\n caused by 2nd <br> should match with each query text rect result")) {
+      return;
+    }
   }
 
   // 3rd <br>
   var br3 = synthesizeQueryTextRect(kLFLen * 2 + 7, 1);
   if (!checkQueryContentResult(br3, description + "rect for next of 3rd <br>")) {
     return;
   }
 
   isSimilarTo(br3.top, d.top + d.height, 3, description + "top of next of 3rd <br> should at similar to bottom of 'd'");
   isSimilarTo(br3.left, d.left, 2, description + "left of next of 3rd <br> should be at similar to left of 'd'");
   isSimilarTo(br3.height, d.height, 2, description + "next of 3rd <br> and 'd' should be similar height");
 
+  // 3rd <br> as array
+  var br3AsArray = synthesizeQueryTextRectArray(kLFLen + 6, 1);
+  if (!checkQueryContentResult(br3AsArray, description + "3rd <br>'s line breaker as array") ||
+      !checkRectArray(br3AsArray, [br3], description + "query text rect array result of 3rd <br> should match with each query text rect result")) {
+    return;
+  }
+
   if (kLFLen > 1) {
     // \n of \r\n
     var br3_2 = synthesizeQueryTextRect(kLFLen * 2 + 7, 1);
-    if (!checkQueryContentResult(br3_2, description + "rect for \n of \r\n caused by 3rd <br>")) {
+    if (!checkQueryContentResult(br3_2, description + "rect for \\n of \\r\\n caused by 3rd <br>")) {
       return;
     }
 
     is(br3_2.top, br3.top, description + "'\\r' and '\\n' should be at same top");
     is(br3_2.left, br3.left, description + "'\\r' and '\\n' should be at same left");
     is(br3_2.height, br3.height, description + "'\\r' and '\\n' should be same height");
     is(br3_2.width, br3.width, description + "'\\r' and '\\n' should be same width");
+
+    // \n of \r\n as array
+    var br2_2AsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 7, 1);
+    if (!checkQueryContentResult(br2_2AsArray, description + "rect array for \\n of \\r\\n caused by 3rd <br>") ||
+        !checkRectArray(br2_2AsArray, [br2_2], description + "query text rect array result of \\n of \\r\\n caused by 3rd <br> should match with each query text rect result")) {
+      return;
+    }
   }
 
   // next of 3rd <br>
   var next_br3 = synthesizeQueryTextRect(kLFLen * 3 + 6, 1);
   if (!checkQueryContentResult(next_br3, description + "rect for next of 3rd <br>")) {
     return;
   }
 
   is(next_br3.top, br3.top, description + "3rd <br> and next of 3rd <br> should be at same top");
   is(next_br3.left, br3.left, description + "3rd <br> and next of 3rd <br> should be at same left");
   is(next_br3.height, br3.height, description + "3rd <br> and next of 3rd <br> should be same height");
   is(next_br3.width, br3.width, description + "3rd <br> and next of 3rd <br> should be same width");
 
+  // next of 3rd <br> as array
+  var next_br3AsArray = synthesizeQueryTextRectArray(kLFLen * 3 + 6, 1);
+  if (!checkQueryContentResult(next_br3AsArray, description + "rect array for next of 3rd <br>") ||
+      !checkRectArray(next_br3AsArray, [next_br3], description + "query text rect array result of next of 3rd <br> should match with each query text rect result")) {
+    return;
+  }
+
   // too big offset for the editor
   tooBigOffset = synthesizeQueryTextRect(kLFLen * 3 + 7, 1);
   if (!checkQueryContentResult(tooBigOffset, description + "rect for too big offset")) {
     return;
   }
 
   is(tooBigOffset.top, next_br3.top, description + "too big offset and next of 3rd <br> should be at same top");
   is(tooBigOffset.left, next_br3.left, description + "too big offset and next of 3rd <br> should be at same left");
   is(tooBigOffset.height, next_br3.height, description + "too big offset and next of 3rd <br> should be same height");
   is(tooBigOffset.width, next_br3.width, description + "too big offset and next of 3rd <br> should be same width");
+
+  // too big offset for the editors as array
+  tooBigOffsetAsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 7, 1);
+  if (!checkQueryContentResult(tooBigOffsetAsArray, description + "rect array for too big offset") ||
+      !checkRectArray(tooBigOffsetAsArray, [tooBigOffset], description + "query text rect array result with too big offset should match with each query text rect result")) {
+    return;
+  }
 }
 
 function runCharAtPointTest(aFocusedEditor, aTargetName)
 {
   aFocusedEditor.value = "This is a test of the\nContent Events";
                        // 012345678901234567890  12345678901234
                        // 0         1         2           3    
 
@@ -4247,51 +4492,105 @@ function runCSSTransformTest()
     textarea.style.transform = "translate(10px, 15px)";
     function movedRect(aRect, aX, aY)
     {
       return { left: aRect.left + aX, top: aRect.top + aY, width: aRect.width, height: aRect.height }
     }
 
     var editorRectTranslated = synthesizeQueryEditorRect();
     if (!checkQueryContentResult(editorRectTranslated,
-          "runCSSTransformTest: editorRectTranslated") ||
+          "runCSSTransformTest: editorRectTranslated, " + textarea.style.transform) ||
         !checkRect(editorRectTranslated, movedRect(editorRect, 10, 15),
-          "runCSSTransformTest: editorRectTranslated")) {
+          "runCSSTransformTest: editorRectTranslated, " + textarea.style.transform)) {
       return;
     }
     var firstCharRectTranslated = synthesizeQueryTextRect(0, 1);
     if (!checkQueryContentResult(firstCharRectTranslated,
-          "runCSSTransformTest: firstCharRectTranslated") ||
+          "runCSSTransformTest: firstCharRectTranslated, " + textarea.style.transform) ||
         !checkRect(firstCharRectTranslated, movedRect(firstCharRect, 10, 15),
-          "runCSSTransformTest: firstCharRectTranslated")) {
+          "runCSSTransformTest: firstCharRectTranslated, " + textarea.style.transform)) {
       return;
     }
     var lastCharRectTranslated = synthesizeQueryTextRect(textarea.value.length - 1, textarea.value.length);
     if (!checkQueryContentResult(lastCharRectTranslated,
-          "runCSSTransformTest: lastCharRectTranslated") ||
+          "runCSSTransformTest: lastCharRectTranslated, " + textarea.style.transform) ||
         !checkRect(lastCharRectTranslated, movedRect(lastCharRect, 10, 15),
-          "runCSSTransformTest: lastCharRectTranslated")) {
+          "runCSSTransformTest: lastCharRectTranslated, " + textarea.style.transform)) {
       return;
     }
     var caretRectTranslated = synthesizeQueryCaretRect(textarea.selectionStart);
     if (!checkQueryContentResult(caretRectTranslated,
-          "runCSSTransformTest: caretRectTranslated") ||
+          "runCSSTransformTest: caretRectTranslated, " + textarea.style.transform) ||
         !checkRect(caretRectTranslated, movedRect(caretRect, 10, 15),
-          "runCSSTransformTest: caretRectTranslated")) {
+          "runCSSTransformTest: caretRectTranslated, " + textarea.style.transform)) {
       return;
     }
     var caretRectBeforeFirstCharTranslated = synthesizeQueryCaretRect(0);
     if (!checkQueryContentResult(caretRectBeforeFirstCharTranslated,
-          "runCSSTransformTest: caretRectBeforeFirstCharTranslated") ||
+          "runCSSTransformTest: caretRectBeforeFirstCharTranslated, " + textarea.style.transform) ||
         !checkRect(caretRectBeforeFirstCharTranslated, movedRect(caretRectBeforeFirstChar, 10, 15),
-          "runCSSTransformTest: caretRectBeforeFirstCharTranslated")) {
+          "runCSSTransformTest: caretRectBeforeFirstCharTranslated, " + textarea.style.transform)) {
+      return;
+    }
+    var firstCharRectTranslatedAsArray = synthesizeQueryTextRectArray(0, 1);
+    if (!checkQueryContentResult(firstCharRectTranslatedAsArray, "runCSSTransformTest: firstCharRectTranslatedAsArray, " + textarea.style.transform) ||
+        !checkRectArray(firstCharRectTranslatedAsArray, [firstCharRectTranslated], "runCSSTransformTest: firstCharRectTranslatedAsArray, " + textarea.style.transform)) {
+      return;
+    }
+    var lastCharRectTranslatedAsArray = synthesizeQueryTextRectArray(textarea.value.length - 1, textarea.value.length);
+    if (!checkQueryContentResult(lastCharRectTranslatedAsArray, "runCSSTransformTest: lastCharRectTranslatedAsArray, " + textarea.style.transform) ||
+        !checkRectArray(lastCharRectTranslatedAsArray, [lastCharRectTranslated], "runCSSTransformTest: lastCharRectTranslatedAsArray, " + textarea.style.transform)) {
       return;
     }
 
     // XXX It's too difficult to check the result with scale and rotate...
+    //     For now, let's check if query text rect and query text rect array returns same rect.
+    textarea.style.transform = "scale(1.5)";
+    firstCharRectTranslated = synthesizeQueryTextRect(0, 1);
+    if (!checkQueryContentResult(firstCharRectTranslated,
+          "runCSSTransformTest: firstCharRectTranslated, " + textarea.style.transform)) {
+      return;
+    }
+    lastCharRectTranslated = synthesizeQueryTextRect(textarea.value.length - 1, textarea.value.length);
+    if (!checkQueryContentResult(lastCharRectTranslated,
+          "runCSSTransformTest: lastCharRectTranslated, " + textarea.style.transform)) {
+      return;
+    }
+    firstCharRectTranslatedAsArray = synthesizeQueryTextRectArray(0, 1);
+    if (!checkQueryContentResult(firstCharRectTranslatedAsArray, "runCSSTransformTest: firstCharRectTranslatedAsArray, " + textarea.style.transform) ||
+        !checkRectArray(firstCharRectTranslatedAsArray, [firstCharRectTranslated], "runCSSTransformTest: firstCharRectTranslatedAsArray, " + textarea.style.transform)) {
+      return;
+    }
+    lastCharRectTranslatedAsArray = synthesizeQueryTextRectArray(textarea.value.length - 1, textarea.value.length);
+    if (!checkQueryContentResult(lastCharRectTranslatedAsArray, "runCSSTransformTest: lastCharRectTranslatedAsArray, " + textarea.style.transform) ||
+        !checkRectArray(lastCharRectTranslatedAsArray, [lastCharRectTranslated], "runCSSTransformTest: lastCharRectTranslatedAsArray, " + textarea.style.transform)) {
+      return;
+    }
+
+    textarea.style.transform = "rotate(30deg)";
+    firstCharRectTranslated = synthesizeQueryTextRect(0, 1);
+    if (!checkQueryContentResult(firstCharRectTranslated,
+          "runCSSTransformTest: firstCharRectTranslated, " + textarea.style.transform)) {
+      return;
+    }
+    lastCharRectTranslated = synthesizeQueryTextRect(textarea.value.length - 1, textarea.value.length);
+    if (!checkQueryContentResult(lastCharRectTranslated,
+          "runCSSTransformTest: lastCharRectTranslated, " + textarea.style.transform)) {
+      return;
+    }
+    firstCharRectTranslatedAsArray = synthesizeQueryTextRectArray(0, 1);
+    if (!checkQueryContentResult(firstCharRectTranslatedAsArray, "runCSSTransformTest: firstCharRectTranslatedAsArray, " + textarea.style.transform) ||
+        !checkRectArray(firstCharRectTranslatedAsArray, [firstCharRectTranslated], "runCSSTransformTest: firstCharRectTranslatedAsArray, " + textarea.style.transform)) {
+      return;
+    }
+    lastCharRectTranslatedAsArray = synthesizeQueryTextRectArray(textarea.value.length - 1, textarea.value.length);
+    if (!checkQueryContentResult(lastCharRectTranslatedAsArray, "runCSSTransformTest: lastCharRectTranslatedAsArray, " + textarea.style.transform) ||
+        !checkRectArray(lastCharRectTranslatedAsArray, [lastCharRectTranslated], "runCSSTransformTest: lastCharRectTranslatedAsArray, " + textarea.style.transform)) {
+      return;
+    }
   } finally {
     textarea.style.transform = "";
   }
 }
 
 function runBug722639Test()
 {
   textarea.focus();
@@ -4300,59 +4599,84 @@ function runBug722639Test()
   textarea.value += textarea.value; // 80 characters
 
   var firstLine = synthesizeQueryTextRect(0, 1);
   if (!checkQueryContentResult(firstLine,
         "runBug722639Test: firstLine")) {
     return;
   }
   ok(true, "runBug722639Test: 1st line, top=" + firstLine.top + ", left=" + firstLine.left);
+  var firstLineAsArray = synthesizeQueryTextRectArray(0, 1);
+  if (!checkQueryContentResult(firstLineAsArray, "runBug722639Test: 1st line as array") ||
+      !checkRectArray(firstLineAsArray, [firstLine], "runBug722639Test: 1st line as array should match with text rect result")) {
+    return;
+  }
   if (kLFLen > 1) {
     var firstLineLF = synthesizeQueryTextRect(1, 1);
     if (!checkQueryContentResult(firstLineLF,
           "runBug722639Test: firstLineLF")) {
       return;
     }
     is(firstLineLF.top, firstLine.top, "runBug722639Test: 1st line's \\n rect should be same as 1st line's \\r rect");
     is(firstLineLF.left, firstLine.left, "runBug722639Test: 1st line's \\n rect should be same as 1st line's \\r rect");
     is(firstLineLF.height, firstLine.height, "runBug722639Test: 1st line's \\n rect should be same as 1st line's \\r rect");
     is(firstLineLF.width, firstLine.width, "runBug722639Test: 1st line's \\n rect should be same as 1st line's \\r rect");
+    var firstLineLFAsArray = synthesizeQueryTextRectArray(1, 1);
+    if (!checkQueryContentResult(firstLineLFAsArray, "runBug722639Test: 1st line's \\n rect as array") ||
+        !checkRectArray(firstLineLFAsArray, [firstLineLF], "runBug722639Test: 1st line's rect as array should match with text rect result")) {
+      return;
+    }
   }
   var secondLine = synthesizeQueryTextRect(kLFLen, 1);
   if (!checkQueryContentResult(secondLine,
         "runBug722639Test: secondLine")) {
     return;
   }
   ok(true, "runBug722639Test: 2nd line, top=" + secondLine.top + ", left=" + secondLine.left);
+  var secondLineAsArray = synthesizeQueryTextRectArray(kLFLen, 1);
+  if (!checkQueryContentResult(secondLineAsArray, "runBug722639Test: 2nd line as array") ||
+      !checkRectArray(secondLineAsArray, [secondLine], "runBug722639Test: 2nd line as array should match with text rect result")) {
+    return;
+  }
   if (kLFLen > 1) {
     var secondLineLF = synthesizeQueryTextRect(kLFLen + 1, 1);
     if (!checkQueryContentResult(secondLineLF,
           "runBug722639Test: secondLineLF")) {
       return;
     }
     is(secondLineLF.top, secondLine.top, "runBug722639Test: 2nd line's \\n rect should be same as 2nd line's \\r rect");
     is(secondLineLF.left, secondLine.left, "runBug722639Test: 2nd line's \\n rect should be same as 2nd line's \\r rect");
     is(secondLineLF.height, secondLine.height, "runBug722639Test: 2nd line's \\n rect should be same as 2nd line's \\r rect");
     is(secondLineLF.width, secondLine.width, "runBug722639Test: 2nd line's \\n rect should be same as 2nd line's \\r rect");
+    var secondLineLFAsArray = synthesizeQueryTextRectArray(kLFLen + 1, 1);
+    if (!checkQueryContentResult(secondLineLFAsArray, "runBug722639Test: 2nd line's \\n rect as array") ||
+        !checkRectArray(secondLineLFAsArray, [secondLineLF], "runBug722639Test: 2nd line's rect as array should match with text rect result")) {
+      return;
+    }
   }
   var lineHeight = secondLine.top -  firstLine.top;
   ok(lineHeight > 0,
      "runBug722639Test: lineHeight must be positive");
   is(secondLine.left, firstLine.left,
      "runBug722639Test: the left value must be always same value");
   is(secondLine.height, firstLine.height,
      "runBug722639Test: the height must be always same value");
   var previousTop = secondLine.top;
   for (var i = 3; i <= textarea.value.length + 1; i++) {
     var currentLine = synthesizeQueryTextRect(kLFLen * (i - 1), 1);
     if (!checkQueryContentResult(currentLine,
            "runBug722639Test: " + i + "th currentLine")) {
       return;
     }
     ok(true, "runBug722639Test: " + i + "th line, top=" + currentLine.top + ", left=" + currentLine.left);
+    var currentLineAsArray = synthesizeQueryTextRectArray(kLFLen * (i - 1), 1);
+    if (!checkQueryContentResult(currentLineAsArray, "runBug722639Test: " + i + "th line as array") ||
+        !checkRectArray(currentLineAsArray, [currentLine], "runBug722639Test: " + i + "th line as array should match with text rect result")) {
+      return;
+    }
     // NOTE: the top position may be 1px larger or smaller than other lines
     //       due to sub pixel positioning.
     if (Math.abs(currentLine.top - (previousTop + lineHeight)) <= 1) {
       ok(true, "runBug722639Test: " + i + "th line's top is expected");
     } else {
       is(currentLine.top, previousTop + lineHeight,
          "runBug722639Test: " + i + "th line's top is unexpected");
     }
@@ -4365,16 +4689,21 @@ function runBug722639Test()
       if (!checkQueryContentResult(currentLineLF,
             "runBug722639Test: " + i + "th currentLineLF")) {
         return;
       }
       is(currentLineLF.top, currentLine.top, "runBug722639Test: " + i + "th line's \\n rect should be same as same line's \\r rect");
       is(currentLineLF.left, currentLine.left, "runBug722639Test: " + i + "th line's \\n rect should be same as same line's \\r rect");
       is(currentLineLF.height, currentLine.height, "runBug722639Test: " + i + "th line's \\n rect should be same as same line's \\r rect");
       is(currentLineLF.width, currentLine.width, "runBug722639Test: " + i + "th line's \\n rect should be same as same line's \\r rect");
+      var currentLineLFAsArray = synthesizeQueryTextRectArray(kLFLen * (i - 1) + 1, 1);
+      if (!checkQueryContentResult(currentLineLFAsArray, "runBug722639Test: " + i + "th line's \\n rect as array") ||
+          !checkRectArray(currentLineLFAsArray, [currentLineLF], "runBug722639Test: " + i + "th line's rect as array should match with text rect result")) {
+        return;
+      }
     }
     previousTop = currentLine.top;
   }
 }
 
 function runForceCommitTest()
 {
   var events;