Merge m-c to inbound. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 31 Jul 2015 15:05:02 -0400
changeset 287341 aeb85029c3b3b594d96a9c72d04b8e971e1bdf5b
parent 287340 eefa8e4699d25f050b7abdd7e7207ca84d093e85 (current diff)
parent 287284 afa67b6957bb2ce63bd5eb3a4125c111335d4a2f (diff)
child 287342 48734d39c71ab79d7b71cfb191a032adde6f0333
child 287354 6fe323a36bb26b126bbf9ca4338ffbaa73501237
child 287383 466ad45c683129be0274da16a505e6245293ab31
child 287393 67761c9de5410a874a34c31c638c987f109253d0
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone42.0a1
first release with
nightly linux32
aeb85029c3b3 / 42.0a1 / 20150801030211 / files
nightly linux64
aeb85029c3b3 / 42.0a1 / 20150801030211 / files
nightly mac
aeb85029c3b3 / 42.0a1 / 20150801030211 / files
nightly win32
aeb85029c3b3 / 42.0a1 / 20150801030211 / files
nightly win64
aeb85029c3b3 / 42.0a1 / 20150801030211 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to inbound. a=merge CLOSED TREE
dom/tests/mochitest/general/test_interfaces.html
dom/webidl/moz.build
mobile/android/base/ZoomedView.java
--- a/b2g/config/aries/sources.xml
+++ b/b2g/config/aries/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8502d07cd7e68da79303471acf64eea48b3dce24"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="2ca27bbdd84526c6a3b198d9cf10f2caff1dadde"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8502d07cd7e68da79303471acf64eea48b3dce24"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="2ca27bbdd84526c6a3b198d9cf10f2caff1dadde"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="1b0db93fb6b870b03467aff50d6419771ba0d88c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="8502d07cd7e68da79303471acf64eea48b3dce24"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="2ca27bbdd84526c6a3b198d9cf10f2caff1dadde"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="8bc59310552179f9a8bc6cdd0188e2475df52fb7"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="9d0e5057ee5404a31ec1bf76131cb11336a7c3b6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="660169a3d7e034a892359e39135e8c2785a6ad6f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8502d07cd7e68da79303471acf64eea48b3dce24"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="2ca27bbdd84526c6a3b198d9cf10f2caff1dadde"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8502d07cd7e68da79303471acf64eea48b3dce24"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="2ca27bbdd84526c6a3b198d9cf10f2caff1dadde"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
--- a/b2g/config/emulator-l/sources.xml
+++ b/b2g/config/emulator-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="07c383a786f188904311a37f6062c2cb84c9b61d">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8502d07cd7e68da79303471acf64eea48b3dce24"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="2ca27bbdd84526c6a3b198d9cf10f2caff1dadde"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="1b0db93fb6b870b03467aff50d6419771ba0d88c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="8502d07cd7e68da79303471acf64eea48b3dce24"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="2ca27bbdd84526c6a3b198d9cf10f2caff1dadde"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="8bc59310552179f9a8bc6cdd0188e2475df52fb7"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="9d0e5057ee5404a31ec1bf76131cb11336a7c3b6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8502d07cd7e68da79303471acf64eea48b3dce24"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="2ca27bbdd84526c6a3b198d9cf10f2caff1dadde"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "8502d07cd7e68da79303471acf64eea48b3dce24", 
+        "git_revision": "2ca27bbdd84526c6a3b198d9cf10f2caff1dadde", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "b328a35941a9934c652a2afbc064b69f23a35fc3", 
+    "revision": "b5e9a95e8f13e775ae2e3828a6e389e5231bef32", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="660169a3d7e034a892359e39135e8c2785a6ad6f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8502d07cd7e68da79303471acf64eea48b3dce24"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="2ca27bbdd84526c6a3b198d9cf10f2caff1dadde"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="07c383a786f188904311a37f6062c2cb84c9b61d">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8502d07cd7e68da79303471acf64eea48b3dce24"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="2ca27bbdd84526c6a3b198d9cf10f2caff1dadde"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
--- a/browser/base/content/browser-fxaccounts.js
+++ b/browser/base/content/browser-fxaccounts.js
@@ -234,20 +234,23 @@ let gFxAccounts = {
     } catch (e) { }
 
     // Bail out if FxA is disabled.
     if (!this.weave.fxAccountsEnabled) {
       // When migration transitions from needs-verification to the null state,
       // fxAccountsEnabled is false because migration has not yet finished.  In
       // that case, hide the button.  We'll get another notification with a null
       // state once migration is complete.
+      this.panelUIFooter.hidden = true;
       this.panelUIFooter.removeAttribute("fxastatus");
       return;
     }
 
+    this.panelUIFooter.hidden = false;
+
     // Make sure the button is disabled in customization mode.
     if (this._inCustomizationMode) {
       this.panelUIStatus.setAttribute("disabled", "true");
       this.panelUILabel.setAttribute("disabled", "true");
       this.panelUIAvatar.setAttribute("disabled", "true");
       this.panelUIIcon.setAttribute("disabled", "true");
     } else {
       this.panelUIStatus.removeAttribute("disabled");
--- a/browser/components/loop/content/css/panel.css
+++ b/browser/components/loop/content/css/panel.css
@@ -1,11 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+html {
+  font-size: 10px;
+  font-family: menu;
+}
 
 body {
   background: none;
 }
 
 /* Panel styles */
 
 .panel {
@@ -172,17 +176,17 @@ body {
   /* The header shouldn't be indented if the tabs are present. */
   -moz-padding-start: 0;
 }
 
 .content-area label {
   display: block;
   width: 100%;
   margin-top: 10px;
-  font-size: 10px;
+  font-size: 1rem;
   color: #777;
 }
 
 .content-area input {
   display: block;
   width: 100%;
   outline: none;
   border-radius: 2px;
@@ -206,59 +210,69 @@ body {
   min-height: 100px;
   padding: 0 1rem;
 }
 
 .rooms > h1 {
   font-weight: bold;
   color: #999;
   padding: .5rem 0;
+  height: 3rem;
+  line-height: 3rem;
+}
+
+.new-room-view > .context-checkbox-checked {
+  background-color: #dbf7ff;
 }
 
 .new-room-view > .context {
-  margin: .5rem 0 0;
-  background-color: #dbf7ff;
+  margin: .5rem 0 .5rem;
   border-radius: 3px 3px 0 0;
-  padding: .5rem;
+  padding: .5rem 1rem ;
+  margin-left: -1rem;
+  margin-right: -1rem;
 }
 
 .new-room-view > .context > .context-enabled {
   margin-bottom: .5rem;
   display: block;
 }
 
 .new-room-view > .context > .context-enabled > input {
   -moz-margin-start: 0;
 }
 
 .new-room-view > .context > .checkbox-wrapper {
+  height: 2rem;
   margin-bottom: .5em;
+  line-height: 2rem;
 }
 
 .new-room-view > .context > .checkbox-wrapper > .checkbox {
-  border-color: #0096dd;
+  border-color: #d8d8d8;
   background-color: #fff;
 }
 
 .new-room-view > .context > .checkbox-wrapper > .checkbox.checked {
   background-image: url("../shared/img/check.svg#check-blue");
 }
 
 .new-room-view > .context > .checkbox-wrapper > label {
   color: #333;
-  font-weight: 700;
+  font-size: 1.1rem;
 }
 
 .new-room-view > .btn {
+  height: 3rem;
   display: block;
-  font-size: 1rem;
-  margin: 0 auto .5rem;
+  font-size: 1.2rem;
+  margin: 0 auto 1rem;
   width: 100%;
   padding: .5rem 1rem;
-  border-radius: 0 0 3px 3px;
+  border-radius: 4px;
 }
 
 /* Remove when bug 1142671 is backed out. */
 .new-room-view > .context.hide + .new-room-button {
   border-radius: 3px;
   margin-top: 0.5rem;
 }
 
@@ -272,24 +286,24 @@ body {
   margin-right: -1rem;
 }
 
 .room-list:empty {
   border-bottom-width: 0;
 }
 
 .room-list > .room-entry {
-  padding: .5rem 1rem;
+  padding: .2rem 1rem;
   /* Always show the default pointer, even over the text part of the entry. */
   cursor: default;
 }
 
 .room-list > .room-entry > h2 {
   display: inline-block;
-  font-size: .85rem;
+  font-size: 1rem;
   color: #777;
   /* See .room-entry-context-item for the margin/size reductions. */
   width: calc(100% - 1rem - 16px);
 }
 
 .room-list > .room-entry.room-active > h2 {
   font-weight: bold;
   color: #000;
@@ -304,21 +318,17 @@ body {
   -moz-margin-end: .3rem;
 }
 
 .room-list > .room-entry.room-active > h2 > .room-notification {
   display: inline-block;
 }
 
 .room-list > .room-entry:hover {
-  background: #f1f1f1;
-}
-
-.room-list > .room-entry:not(:last-child) {
-  border-bottom: 1px solid #ccc;
+  background: #dbf7ff;
 }
 
 .room-list > .room-entry > p {
   margin: 0;
   padding: .2rem 0;
 }
 
 .room-list > .room-entry > p > a {
@@ -419,17 +429,17 @@ body {
 
 .button {
   padding: 2px 5px;
   background-color: #fbfbfb;
   color: #333;
   border: 1px solid #c1c1c1;
   border-radius: 2px;
   min-height: 26px;
-  font-size: 12px;
+  font-size: 1.2rem;
 }
 
 .button > .button-caption {
   vertical-align: middle;
 }
 
 .button:hover {
   background-color: #ebebeb;
@@ -614,17 +624,17 @@ html[dir="rtl"] .generate-url-spinner {
 
 .terms-service {
   padding-left: 1rem;
   padding-right: 1rem;
   padding-bottom: .5rem;
 }
 
 .terms-service > a {
-  color: #00caee;
+  color: #00a9dc;
 }
 
 /* DnD menu */
 
 .dnd-status {
   border: 1px solid transparent;
   padding: 2px 4px;
   margin: 0;
--- a/browser/components/loop/content/js/panel.js
+++ b/browser/components/loop/content/js/panel.js
@@ -769,16 +769,17 @@ loop.panel = (function(_, mozL10n) {
       try {
         hostname = new URL(this.state.url).hostname;
       } catch (ex) {
         // Empty catch - if there's an error, then we won't show the context.
       }
 
       var contextClasses = React.addons.classSet({
         context: true,
+        "context-checkbox-checked": this.state.checked,
         hide: !hostname ||
           !this.props.mozLoop.getLoopPref("contextInConversations.enabled")
       });
 
       return (
         React.createElement("div", {className: "new-room-view"}, 
           React.createElement("div", {className: contextClasses}, 
             React.createElement(Checkbox, {checked: this.state.checked, 
--- a/browser/components/loop/content/js/panel.jsx
+++ b/browser/components/loop/content/js/panel.jsx
@@ -769,16 +769,17 @@ loop.panel = (function(_, mozL10n) {
       try {
         hostname = new URL(this.state.url).hostname;
       } catch (ex) {
         // Empty catch - if there's an error, then we won't show the context.
       }
 
       var contextClasses = React.addons.classSet({
         context: true,
+        "context-checkbox-checked": this.state.checked,
         hide: !hostname ||
           !this.props.mozLoop.getLoopPref("contextInConversations.enabled")
       });
 
       return (
         <div className="new-room-view">
           <div className={contextClasses}>
             <Checkbox checked={this.state.checked}
--- a/browser/components/loop/content/shared/css/common.css
+++ b/browser/components/loop/content/shared/css/common.css
@@ -95,29 +95,29 @@ p {
 
 .btn.btn-constrained {
   overflow: hidden;
   text-overflow: ellipsis;
   white-space: nowrap;
 }
 
 .btn-info {
-  background-color: #0096dd;
-  border: 1px solid #0095dd;
+  background-color: #00a9dc;
+  border: 1px solid #00a9dc;
   color: #fff;
 }
 
   .btn-info:hover {
-    background-color: #008acb;
-    border: 1px solid #008acb;
+    background-color: #5cccee;
+    border: 1px solid #5cccee;
   }
 
   .btn-info:active {
-    background-color: #006b9d;
-    border: 1px solid #006b9d;
+    background-color: #5cccee;
+    border: 1px solid #5cccee;
   }
 
 .btn-accept,
 .btn-success,
 .btn-accept + .btn-chevron {
   background-color: #5bc0a4;
   border: 1px solid #5bc0a4;
 }
@@ -463,18 +463,18 @@ html[dir="rtl"] .dropdown-menu {
 
 .checkbox-wrapper {
   -moz-user-select: none;
   user-select: none;
 }
 
 .checkbox {
   float: left;
-  width: 1em;
-  height: 1em;
+  width: 2rem;
+  height: 2rem;
   -moz-margin-end: .5em;
   margin-top: .1em;
   border: 1px solid #999;
   border-radius: 3px;
   cursor: pointer;
   background-color: transparent;
   background-position: center center;
   background-repeat: no-repeat;
@@ -516,25 +516,29 @@ html[dir="rtl"] .context-content {
 .context-content > p {
   font-weight: bold;
   margin-bottom: .8em;
   margin-top: 0;
 }
 
 .context-wrapper {
   border: 1px solid #5cccee;
-  border-radius: 3px;
+  border-radius: 4px;
   background: #fff;
   padding: .8em;
   /* Use the flex row mode to position the elements next to each other. */
   display: flex;
   flex-flow: row nowrap;
   line-height: 1.1em;
 }
 
+.context-wrapper:hover {
+  background-color: #dbf7ff;
+}
+
 .context-wrapper > .context-preview {
   float: left;
   /* 16px is standard height/width for a favicon */
   width: 16px;
   max-height: 16px;
   margin-right: .8em;
   flex: 0 0 auto;
 }
@@ -551,12 +555,12 @@ html[dir="rtl"] .context-wrapper > .cont
   color: black;
   /* 16px for the preview, plus its .8em margin */
   max-width: calc(100% - 16px - .8em);
   word-wrap: break-word;
 }
 
 .context-wrapper > .context-description > .context-url {
   display: block;
-  color: #59A1D7;
+  color: #00a9dc;
   font-weight: 700;
   clear: both;
 }
--- a/browser/components/preferences/in-content/preferences.xul
+++ b/browser/components/preferences/in-content/preferences.xul
@@ -195,18 +195,18 @@
   </hbox>
 
     <vbox id="dialogOverlay" align="center" pack="center">
       <groupbox id="dialogBox"
                 orient="vertical"
                 pack="end"
                 role="dialog"
                 aria-labelledby="dialogTitle">
-        <caption class="titlebar" flex="1" align="center">
-          <label id="dialogTitle" class="title" flex="1"></label>
+        <caption flex="1" align="center">
+          <label id="dialogTitle" flex="1"></label>
           <button id="dialogClose"
                   class="close-icon"
                   aria-label="&preferencesCloseButton.label;"/>
         </caption>
         <browser id="dialogFrame"
                  name="dialogFrame"
                  autoscroll="false"
                  disablehistory="true"/>
--- a/browser/devtools/framework/sidebar.js
+++ b/browser/devtools/framework/sidebar.js
@@ -324,45 +324,32 @@ ToolSidebar.prototype = {
       panel.remove();
     }
 
     this._tabs.delete(tabId);
     this.emit("tab-unregistered", tabId);
   }),
 
   /**
-   * Show or hide a specific tab and tabpanel.
+   * Show or hide a specific tab.
    * @param {Boolean} isVisible True to show the tab/tabpanel, False to hide it.
    * @param {String} id The ID of the tab to be hidden.
-   * @param {String} tabPanelId Optionally pass the ID for the tabPanel if it
-   * can't be retrieved using the tab ID. This is useful when tabs and tabpanels
-   * existed before the widget was created.
    */
-  toggleTab: function(isVisible, id, tabPanelId) {
+  toggleTab: function(isVisible, id) {
     // Toggle the tab.
     let tab = this.getTab(id);
     if (!tab) {
       return;
     }
     tab.hidden = !isVisible;
 
     // Toggle the item in the allTabs menu.
     if (this._allTabsBtn) {
       this._allTabsBtn.querySelector("#sidebar-alltabs-item-" + id).hidden = !isVisible;
     }
-
-    // Toggle the corresponding tabPanel, if one can be found either with the id
-    // or the provided tabPanelId.
-    let tabPanel = this.getTabPanel(id);
-    if (!tabPanel && tabPanelId) {
-      tabPanel = this.getTabPanel(tabPanelId);
-    }
-    if (tabPanel) {
-      tabPanel.hidden = !isVisible;
-    }
   },
 
   /**
    * Select a specific tab.
    */
   select: function(id) {
     let tab = this.getTab(id);
     if (tab) {
--- a/browser/devtools/netmonitor/netmonitor-view.js
+++ b/browser/devtools/netmonitor/netmonitor-view.js
@@ -2470,24 +2470,24 @@ NetworkDetailsView.prototype = {
     $("#response-content-json-box").hidden = true;
     $("#response-content-textarea-box").hidden = true;
     $("#raw-headers").hidden = true;
     $("#response-content-image-box").hidden = true;
 
     let isHtml = RequestsMenuView.prototype.isHtml({ attachment: aData });
 
     // Show the "Preview" tabpanel only for plain HTML responses.
-    this.sidebar.toggleTab(isHtml, "preview-tab", "preview-tabpanel");
+    this.sidebar.toggleTab(isHtml, "preview-tab");
 
     // Show the "Security" tab only for requests that
     //   1) are https (state != insecure)
     //   2) come from a target that provides security information.
     let hasSecurityInfo = aData.securityState &&
                           aData.securityState !== "insecure";
-    this.sidebar.toggleTab(hasSecurityInfo, "security-tab", "security-tabpanel");
+    this.sidebar.toggleTab(hasSecurityInfo, "security-tab");
 
     // Switch to the "Headers" tabpanel if the "Preview" previously selected
     // and this is not an HTML response or "Security" was selected but this
     // request has no security information.
 
     if (!isHtml && this.widget.selectedPanel === $("#preview-tabpanel") ||
         !hasSecurityInfo && this.widget.selectedPanel === $("#security-tabpanel")) {
       this.widget.selectedIndex = 0;
--- a/browser/devtools/netmonitor/test/browser_net_html-preview.js
+++ b/browser/devtools/netmonitor/test/browser_net_html-preview.js
@@ -17,46 +17,44 @@ function test() {
     waitForNetworkEvents(aMonitor, 6).then(() => {
       EventUtils.sendMouseEvent({ type: "mousedown" },
         document.getElementById("details-pane-toggle"));
 
       is($("#event-details-pane").selectedIndex, 0,
         "The first tab in the details pane should be selected.");
       is($("#preview-tab").hidden, true,
         "The preview tab should be hidden for non html responses.");
-      is($("#preview-tabpanel").hidden, true,
-        "The preview tabpanel should be hidden for non html responses.");
+      is($("#preview-tabpanel").hidden, false,
+        "The preview tabpanel is not hidden for non html responses.");
 
       RequestsMenu.selectedIndex = 4;
       NetMonitorView.toggleDetailsPane({ visible: true, animated: false }, 6);
 
       is($("#event-details-pane").selectedIndex, 6,
         "The sixth tab in the details pane should be selected.");
       is($("#preview-tab").hidden, false,
         "The preview tab should be visible now.");
-      is($("#preview-tabpanel").hidden, false,
-        "The preview tabpanel should be visible now.");
 
       waitFor(aMonitor.panelWin, EVENTS.RESPONSE_HTML_PREVIEW_DISPLAYED).then(() => {
         let iframe = $("#response-preview");
         ok(iframe,
           "There should be a response preview iframe available.");
         ok(iframe.contentDocument,
           "The iframe's content document should be available.");
         is(iframe.contentDocument.querySelector("blink").textContent, "Not Found",
           "The iframe's content document should be loaded and correct.");
 
         RequestsMenu.selectedIndex = 5;
 
         is($("#event-details-pane").selectedIndex, 0,
           "The first tab in the details pane should be selected again.");
         is($("#preview-tab").hidden, true,
           "The preview tab should be hidden again for non html responses.");
-        is($("#preview-tabpanel").hidden, true,
-          "The preview tabpanel should be hidden again for non html responses.");
+        is($("#preview-tabpanel").hidden, false,
+          "The preview tabpanel is not hidden again for non html responses.");
 
         teardown(aMonitor).then(finish);
       });
     });
 
     aDebuggee.performRequests();
   });
 }
--- a/browser/devtools/netmonitor/test/browser_net_security-tab-visibility.js
+++ b/browser/devtools/netmonitor/test/browser_net_security-tab-visibility.js
@@ -40,49 +40,56 @@ add_task(function* () {
     info("Testing Security tab visibility for " + testcase.desc);
     let onNewItem = monitor.panelWin.once(EVENTS.NETWORK_EVENT);
     let onSecurityInfo = monitor.panelWin.once(EVENTS.RECEIVED_SECURITY_INFO);
     let onComplete = testcase.isBroken ?
                        waitForSecurityBrokenNetworkEvent() :
                        waitForNetworkEvents(monitor, 1);
 
     let tab = $("#security-tab");
+    let tabpanel = $("#security-tabpanel");
 
     info("Performing a request to " + testcase.uri);
     debuggee.performRequests(1, testcase.uri);
 
     info("Waiting for new network event.");
     yield onNewItem;
 
     info("Selecting the request.");
     RequestsMenu.selectedIndex = 0;
 
     is(RequestsMenu.selectedItem.attachment.securityState, undefined,
        "Security state has not yet arrived.");
     is(tab.hidden, !testcase.visibleOnNewEvent,
        "Security tab is " +
         (testcase.visibleOnNewEvent ? "visible" : "hidden") +
        " after new request was added to the menu.");
+    is(tabpanel.hidden, false,
+      "#security-tabpanel is visible after new request was added to the menu.");
 
     info("Waiting for security information to arrive.");
     yield onSecurityInfo;
 
     ok(RequestsMenu.selectedItem.attachment.securityState,
        "Security state arrived.");
     is(tab.hidden, !testcase.visibleOnSecurityInfo,
        "Security tab is " +
         (testcase.visibleOnSecurityInfo ? "visible" : "hidden") +
        " after security information arrived.");
+    is(tabpanel.hidden, false,
+      "#security-tabpanel is visible after security information arrived.");
 
     info("Waiting for request to complete.");
     yield onComplete;
     is(tab.hidden, !testcase.visibleOnceComplete,
        "Security tab is " +
         (testcase.visibleOnceComplete ? "visible" : "hidden") +
        " after request has been completed.");
+    is(tabpanel.hidden, false,
+      "#security-tabpanel is visible after request is complete.");
 
     info("Clearing requests.");
     RequestsMenu.clear();
   }
 
   yield teardown(monitor);
 
   /**
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -180,16 +180,17 @@ browser.jar:
   skin/classic/browser/downloads/download-notification-start.png (downloads/download-notification-start.png)
   skin/classic/browser/downloads/download-summary.png (downloads/download-summary.png)
   skin/classic/browser/downloads/downloads.css        (downloads/downloads.css)
   skin/classic/browser/feeds/feedIcon.png             (feeds/feedIcon.png)
   skin/classic/browser/feeds/feedIcon16.png           (feeds/feedIcon16.png)
   skin/classic/browser/feeds/subscribe.css            (feeds/subscribe.css)
   skin/classic/browser/feeds/subscribe-ui.css         (feeds/subscribe-ui.css)
 * skin/classic/browser/newtab/newTab.css              (newtab/newTab.css)
+  skin/classic/browser/newtab/close.png               (../shared/newtab/close.png)
   skin/classic/browser/newtab/controls.svg            (../shared/newtab/controls.svg)
   skin/classic/browser/newtab/whimsycorn.png          (../shared/newtab/whimsycorn.png)
   skin/classic/browser/panic-panel/header.png         (../shared/panic-panel/header.png)
   skin/classic/browser/panic-panel/header@2x.png      (../shared/panic-panel/header@2x.png)
   skin/classic/browser/panic-panel/header-small.png   (../shared/panic-panel/header-small.png)
   skin/classic/browser/panic-panel/header-small@2x.png (../shared/panic-panel/header-small@2x.png)
   skin/classic/browser/panic-panel/icons.png          (../shared/panic-panel/icons.png)
   skin/classic/browser/panic-panel/icons@2x.png       (../shared/panic-panel/icons@2x.png)
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -237,16 +237,17 @@ browser.jar:
   skin/classic/browser/downloads/download-summary.png       (downloads/download-summary.png)
   skin/classic/browser/downloads/download-summary@2x.png    (downloads/download-summary@2x.png)
   skin/classic/browser/downloads/downloads.css              (downloads/downloads.css)
   skin/classic/browser/feeds/subscribe.css                  (feeds/subscribe.css)
   skin/classic/browser/feeds/subscribe-ui.css               (feeds/subscribe-ui.css)
   skin/classic/browser/feeds/feedIcon.png                   (feeds/feedIcon.png)
   skin/classic/browser/feeds/feedIcon16.png                 (feeds/feedIcon16.png)
 * skin/classic/browser/newtab/newTab.css                    (newtab/newTab.css)
+  skin/classic/browser/newtab/close.png                     (../shared/newtab/close.png)
   skin/classic/browser/newtab/controls.svg                  (../shared/newtab/controls.svg)
   skin/classic/browser/newtab/whimsycorn.png                (../shared/newtab/whimsycorn.png)
   skin/classic/browser/setDesktopBackground.css
   skin/classic/browser/monitor.png
   skin/classic/browser/monitor_16-10.png
   skin/classic/browser/panic-panel/header.png               (../shared/panic-panel/header.png)
   skin/classic/browser/panic-panel/header@2x.png            (../shared/panic-panel/header@2x.png)
   skin/classic/browser/panic-panel/header-small.png         (../shared/panic-panel/header-small.png)
--- a/browser/themes/shared/customizableui/panelUIOverlay.inc.css
+++ b/browser/themes/shared/customizableui/panelUIOverlay.inc.css
@@ -515,17 +515,17 @@ toolbarpaletteitem[place="palette"] > to
 
 #PanelUI-fxa-status {
   display: flex;
   flex: 1 1 0%;
   width: 1px;
 }
 
 #PanelUI-footer-inner,
-#PanelUI-footer-fxa {
+#PanelUI-footer-fxa:not([hidden]) {
   display: flex;
   border-top: 1px solid hsla(210,4%,10%,.14);
 }
 
 #PanelUI-multiView[viewtype="subview"] #PanelUI-footer-inner,
 #PanelUI-multiView[viewtype="subview"] #PanelUI-footer-fxa {
   position: relative;
 }
--- a/browser/themes/shared/identity-block/identity-block.inc.css
+++ b/browser/themes/shared/identity-block/identity-block.inc.css
@@ -44,49 +44,40 @@
   color: var(--identity-box-chrome-color);
 }
 
 #identity-icon-labels {
   padding-inline-start: 2px;
 }
 
 #notification-popup-box:not([hidden]) + #identity-box {
-  -moz-padding-start: 10px;
+  padding-inline-start: 10px;
   border-radius: 0;
 }
 
 @conditionalForwardWithUrlbar@ > #urlbar > #identity-box {
   border-radius: 0;
 }
 
 @conditionalForwardWithUrlbar@:not([switchingtabs]) > #urlbar > #identity-box {
   transition: padding-left, padding-right;
 }
 
-@conditionalForwardWithUrlbar@ > #forward-button[disabled] + #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(ltr) {
-  padding-left: calc(var(--backbutton-urlbar-overlap) + 4px);
-}
-
-@conditionalForwardWithUrlbar@ > #forward-button[disabled] + #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(rtl) {
-  padding-right: calc(var(--backbutton-urlbar-overlap) + 4px);
+@conditionalForwardWithUrlbar@ > #forward-button[disabled] + #urlbar > #notification-popup-box[hidden] + #identity-box {
+  padding-inline-start: calc(var(--backbutton-urlbar-overlap) + 4px);
 }
 
 @conditionalForwardWithUrlbar@:hover:not([switchingtabs]) > #forward-button[disabled] + #urlbar > #notification-popup-box[hidden] + #identity-box {
   /* forward button hiding is delayed when hovered */
   transition-delay: 100s;
 }
 
-@conditionalForwardWithUrlbar@:not(:hover) > #forward-button[disabled] + #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(ltr) {
+@conditionalForwardWithUrlbar@:not(:hover) > #forward-button[disabled] + #urlbar > #notification-popup-box[hidden] + #identity-box {
   /* when not hovered anymore, trigger a new non-delayed transition to react to the forward button hiding */
-  padding-left: calc(var(--backbutton-urlbar-overlap) + 4.01px);
-}
-
-@conditionalForwardWithUrlbar@:not(:hover) > #forward-button[disabled] + #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(rtl) {
-  /* when not hovered anymore, trigger a new non-delayed transition to react to the forward button hiding */
-  padding-right: calc(var(--backbutton-urlbar-overlap) + 4.01px);
+  padding-inline-start: calc(var(--backbutton-urlbar-overlap) + 4.01px);
 }
 
 /* TRACKING PROTECTION ICON */
 
 #tracking-protection-icon {
   width: 16px;
   height: 16px;
   margin-inline-end: 2px;
--- a/browser/themes/shared/incontentprefs/preferences.inc.css
+++ b/browser/themes/shared/incontentprefs/preferences.inc.css
@@ -328,19 +328,20 @@ description > html|a {
   text-align: center;
   -moz-user-select: none;
 }
 
 .close-icon {
   background-color: transparent !important;
   border: none;
   box-shadow: none;
-  height: 18px;
   padding: 0;
-  min-width: 18px;
+  height: auto;
+  min-height: 16px;
+  min-width: 0;
 }
 
 #dialogBox > .groupbox-body {
   -moz-appearance: none;
   padding: 20px;
 }
 
 #dialogFrame {
copy from toolkit/themes/windows/global/icons/close-XPVista7.png
copy to browser/themes/shared/newtab/close.png
--- a/browser/themes/shared/newtab/newTab.inc.css
+++ b/browser/themes/shared/newtab/newTab.inc.css
@@ -50,26 +50,26 @@
 #newtab-undo-close-button {
   -moz-appearance: none;
   padding: 0;
   border: none;
   height: 16px;
   width: 16px;
   float: right;
   right: 0;
-  background-image: -moz-image-rect(url(chrome://global/skin/icons/close.png), 0, 16, 16, 0);
+  background-image: -moz-image-rect(url(chrome://browser/skin/newtab/close.png), 0, 16, 16, 0);
   background-color: transparent;
 }
 
 #newtab-undo-close-button:hover {
-  background-image: -moz-image-rect(url(chrome://global/skin/icons/close.png), 0, 32, 16, 16);
+  background-image: -moz-image-rect(url(chrome://browser/skin/newtab/close.png), 0, 32, 16, 16);
 }
 
-#newtab-undo-close-button:active {
-  background-image: -moz-image-rect(url(chrome://global/skin/icons/close.png), 0, 48, 16, 32);
+#newtab-undo-close-button:hover:active {
+  background-image: -moz-image-rect(url(chrome://browser/skin/newtab/close.png), 0, 48, 16, 32);
 }
 
 /* CUSTOMIZE */
 #newtab-customize-button,
 .newtab-customize {
   background-image: -moz-image-rect(url(chrome://browser/skin/newtab/controls.svg), 0, 32, 32, 0);
   background-size: 28px;
   height: 38px;
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -243,16 +243,17 @@ browser.jar:
         skin/classic/browser/downloads/downloads.css                 (downloads/downloads.css)
         skin/classic/browser/feeds/feedIcon.png                      (feeds/feedIcon.png)
         skin/classic/browser/feeds/feedIcon16.png                    (feeds/feedIcon16.png)
         skin/classic/browser/feeds/feedIcon-XP.png                   (feeds/feedIcon-XP.png)
         skin/classic/browser/feeds/feedIcon16-XP.png                 (feeds/feedIcon16-XP.png)
         skin/classic/browser/feeds/subscribe.css                     (feeds/subscribe.css)
         skin/classic/browser/feeds/subscribe-ui.css                  (feeds/subscribe-ui.css)
 *       skin/classic/browser/newtab/newTab.css                       (newtab/newTab.css)
+        skin/classic/browser/newtab/close.png                        (../shared/newtab/close.png)
         skin/classic/browser/newtab/controls.svg                     (../shared/newtab/controls.svg)
         skin/classic/browser/newtab/whimsycorn.png                   (../shared/newtab/whimsycorn.png)
         skin/classic/browser/panic-panel/header.png                  (../shared/panic-panel/header.png)
         skin/classic/browser/panic-panel/header@2x.png               (../shared/panic-panel/header@2x.png)
         skin/classic/browser/panic-panel/header-small.png            (../shared/panic-panel/header-small.png)
         skin/classic/browser/panic-panel/header-small@2x.png         (../shared/panic-panel/header-small@2x.png)
         skin/classic/browser/panic-panel/icons.png                   (../shared/panic-panel/icons.png)
         skin/classic/browser/panic-panel/icons@2x.png                (../shared/panic-panel/icons@2x.png)
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -169,16 +169,20 @@ DOMInterfaces = {
 'BluetoothGattCharacteristic': {
     'nativeType': 'mozilla::dom::bluetooth::BluetoothGattCharacteristic',
 },
 
 'BluetoothGattDescriptor': {
     'nativeType': 'mozilla::dom::bluetooth::BluetoothGattDescriptor',
 },
 
+'BluetoothGattServer': {
+    'nativeType': 'mozilla::dom::bluetooth::BluetoothGattServer',
+},
+
 'BluetoothGattService': {
     'nativeType': 'mozilla::dom::bluetooth::BluetoothGattService',
 },
 
 'BluetoothLeDeviceEvent': {
     'nativeType': 'mozilla::dom::bluetooth::BluetoothLeDeviceEvent',
 },
 
--- a/dom/bluetooth/bluetooth2/BluetoothAdapter.cpp
+++ b/dom/bluetooth/bluetooth2/BluetoothAdapter.cpp
@@ -17,16 +17,17 @@
 #include "mozilla/dom/BluetoothStatusChangedEvent.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/File.h"
 
 #include "mozilla/dom/bluetooth/BluetoothAdapter.h"
 #include "mozilla/dom/bluetooth/BluetoothClassOfDevice.h"
 #include "mozilla/dom/bluetooth/BluetoothDevice.h"
 #include "mozilla/dom/bluetooth/BluetoothDiscoveryHandle.h"
+#include "mozilla/dom/bluetooth/BluetoothGattServer.h"
 #include "mozilla/dom/bluetooth/BluetoothPairingListener.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 USING_BLUETOOTH_NAMESPACE
 
@@ -356,16 +357,37 @@ BluetoothAdapter::Cleanup()
       nsRefPtr<BluetoothVoidReplyRunnable> results =
         new BluetoothVoidReplyRunnable(nullptr);
       bs->StopLeScanInternal(uuid, results);
     }
     mLeScanHandleArray.Clear();
   }
 }
 
+BluetoothGattServer*
+BluetoothAdapter::GetGattServer()
+{
+  /* Only expose GATT server if the adapter is enabled. It would be worth
+   * noting that the enabling state and the disabling state are just
+   * intermediate states, and the adapter would change into the enabled state
+   * or the disabled state sooner or later. So we invalidate and nullify the
+   * created GATT server object only when the adapter changes to a steady
+   * state, i.e., the disabled state.
+   */
+  if (mState != BluetoothAdapterState::Enabled) {
+    return nullptr;
+  }
+
+  if (!mGattServer) {
+    mGattServer = new BluetoothGattServer(GetOwner());
+  }
+
+  return mGattServer;
+}
+
 void
 BluetoothAdapter::GetPairedDeviceProperties(
   const nsTArray<nsString>& aDeviceAddresses)
 {
   BluetoothService* bs = BluetoothService::Get();
   NS_ENSURE_TRUE_VOID(bs);
 
   nsRefPtr<BluetoothVoidReplyRunnable> results =
@@ -386,16 +408,20 @@ BluetoothAdapter::SetPropertyByValue(con
   if (name.EqualsLiteral("State")) {
     mState = value.get_bool() ? BluetoothAdapterState::Enabled
                               : BluetoothAdapterState::Disabled;
 
     // Clear saved devices and LE scan handles when state changes to disabled
     if (mState == BluetoothAdapterState::Disabled) {
       mDevices.Clear();
       mLeScanHandleArray.Clear();
+      if (mGattServer) {
+        mGattServer->Invalidate();
+        mGattServer = nullptr;
+      }
     }
   } else if (name.EqualsLiteral("Name")) {
     mName = value.get_nsString();
   } else if (name.EqualsLiteral("Address")) {
     mAddress = value.get_nsString();
   } else if (name.EqualsLiteral("Discoverable")) {
     mDiscoverable = value.get_bool();
   } else if (name.EqualsLiteral("Discovering")) {
@@ -969,16 +995,23 @@ void
 BluetoothAdapter::SetAdapterState(BluetoothAdapterState aState)
 {
   if (mState == aState) {
     return;
   }
 
   mState = aState;
 
+  if (mState == BluetoothAdapterState::Disabled) {
+    if (mGattServer) {
+      mGattServer->Invalidate();
+      mGattServer = nullptr;
+    }
+  }
+
   // Fire BluetoothAttributeEvent for changed adapter state
   Sequence<nsString> types;
   BT_APPEND_ENUM_STRING_FALLIBLE(types,
                                  BluetoothAdapterAttribute,
                                  BluetoothAdapterAttribute::State);
   DispatchAttributeEvent(types);
 }
 
--- a/dom/bluetooth/bluetooth2/BluetoothAdapter.h
+++ b/dom/bluetooth/bluetooth2/BluetoothAdapter.h
@@ -23,16 +23,17 @@ struct MediaMetaData;
 struct MediaPlayStatus;
 }
 }
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothDevice;
 class BluetoothDiscoveryHandle;
+class BluetoothGattServer;
 class BluetoothNamedValue;
 class BluetoothPairingListener;
 class BluetoothSignal;
 class BluetoothValue;
 
 class BluetoothAdapter : public DOMEventTargetHelper
                        , public BluetoothSignalObserver
 {
@@ -72,16 +73,18 @@ public:
     return mDiscoverable;
   }
 
   BluetoothPairingListener* PairingReqs() const
   {
     return mPairingReqs;
   }
 
+  BluetoothGattServer* GetGattServer();
+
   /****************************************************************************
    * Event Handlers
    ***************************************************************************/
   IMPL_EVENT_HANDLER(attributechanged);
   IMPL_EVENT_HANDLER(devicepaired);
   IMPL_EVENT_HANDLER(deviceunpaired);
   IMPL_EVENT_HANDLER(pairingaborted);
   IMPL_EVENT_HANDLER(a2dpstatuschanged);
@@ -355,16 +358,28 @@ private:
   bool mDiscoverable;
 
   /**
    * Whether this adapter is discovering nearby devices.
    */
   bool mDiscovering;
 
   /**
+   * GATT server object of this adapter.
+   *
+   * A new GATT server object will be created at the first time when
+   * |GetGattServer| is called after the adapter has been enabled. If the
+   * adapter has been disabled later on, the created GATT server will be
+   * discard by the adapter, and this GATT object should stop working till the
+   * end of its life. When |GetGattServer| is called after the adapter has been
+   * enabled again, a new GATT server object will be created.
+   */
+  nsRefPtr<BluetoothGattServer> mGattServer;
+
+  /**
    * Handle to fire pairing requests of different pairing types.
    */
   nsRefPtr<BluetoothPairingListener> mPairingReqs;
 
   /**
    * Handle to fire 'ondevicefound' event handler for discovered device.
    *
    * This variable is set to the latest discovery handle when adapter just
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth/bluetooth2/BluetoothGattServer.cpp
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "BluetoothGattServer.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+USING_BLUETOOTH_NAMESPACE
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(BluetoothGattServer,
+                                      mOwner)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(BluetoothGattServer)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(BluetoothGattServer)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BluetoothGattServer)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+BluetoothGattServer::BluetoothGattServer(nsPIDOMWindow* aOwner)
+  : mOwner(aOwner)
+  , mValid(true)
+{
+}
+
+BluetoothGattServer::~BluetoothGattServer()
+{
+  Invalidate();
+}
+
+JSObject*
+BluetoothGattServer::WrapObject(JSContext* aContext,
+                                JS::Handle<JSObject*> aGivenProto)
+{
+  return BluetoothGattServerBinding::Wrap(aContext, this, aGivenProto);
+}
+
+void
+BluetoothGattServer::Invalidate()
+{
+  mValid = false;
+
+  /* TODO: add tear down stuff here */
+
+  return;
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth/bluetooth2/BluetoothGattServer.h
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef mozilla_dom_bluetooth_bluetoothgattserver_h__
+#define mozilla_dom_bluetooth_bluetoothgattserver_h__
+
+#include "mozilla/dom/BluetoothGattServerBinding.h"
+#include "mozilla/dom/bluetooth/BluetoothCommon.h"
+#include "nsCOMPtr.h"
+#include "nsPIDOMWindow.h"
+#include "nsWrapperCache.h"
+
+BEGIN_BLUETOOTH_NAMESPACE
+
+class BluetoothGattServer final : public nsISupports
+                                , public nsWrapperCache
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(BluetoothGattServer)
+
+  /****************************************************************************
+   * Attribute Getters
+   ***************************************************************************/
+
+  /****************************************************************************
+   * Event Handlers
+   ***************************************************************************/
+
+  /****************************************************************************
+   * Methods (Web API Implementation)
+   ***************************************************************************/
+
+  /****************************************************************************
+   * Others
+   ***************************************************************************/
+  nsPIDOMWindow* GetParentObject() const
+  {
+     return mOwner;
+  }
+
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aGivenProto) override;
+
+  BluetoothGattServer(nsPIDOMWindow* aOwner);
+
+  /* Invalidate the GATT server.
+   * If the BluetoothAdapter turns off, existing BluetoothGattServer instances
+   * should stop working till the end of life.
+   */
+  void Invalidate();
+
+private:
+  ~BluetoothGattServer();
+
+  /****************************************************************************
+   * Variables
+   ***************************************************************************/
+  nsCOMPtr<nsPIDOMWindow> mOwner;
+
+  bool mValid;
+};
+
+END_BLUETOOTH_NAMESPACE
+
+#endif
--- a/dom/bluetooth/moz.build
+++ b/dom/bluetooth/moz.build
@@ -45,16 +45,17 @@ if CONFIG['MOZ_B2G_BT']:
         SOURCES += [
             'bluetooth2/BluetoothAdapter.cpp',
             'bluetooth2/BluetoothClassOfDevice.cpp',
             'bluetooth2/BluetoothDevice.cpp',
             'bluetooth2/BluetoothDiscoveryHandle.cpp',
             'bluetooth2/BluetoothGatt.cpp',
             'bluetooth2/BluetoothGattCharacteristic.cpp',
             'bluetooth2/BluetoothGattDescriptor.cpp',
+            'bluetooth2/BluetoothGattServer.cpp',
             'bluetooth2/BluetoothGattService.cpp',
             'bluetooth2/BluetoothLeDeviceEvent.cpp',
             'bluetooth2/BluetoothManager.cpp',
             'bluetooth2/BluetoothPairingHandle.cpp',
             'bluetooth2/BluetoothPairingListener.cpp',
             'bluetooth2/BluetoothProfileController.cpp',
             'bluetooth2/BluetoothReplyRunnable.cpp',
             'bluetooth2/BluetoothService.cpp',
@@ -174,16 +175,17 @@ else:
     EXPORTS.mozilla.dom.bluetooth += [
         'bluetooth2/BluetoothAdapter.h',
         'bluetooth2/BluetoothClassOfDevice.h',
         'bluetooth2/BluetoothDevice.h',
         'bluetooth2/BluetoothDiscoveryHandle.h',
         'bluetooth2/BluetoothGatt.h',
         'bluetooth2/BluetoothGattCharacteristic.h',
         'bluetooth2/BluetoothGattDescriptor.h',
+        'bluetooth2/BluetoothGattServer.h',
         'bluetooth2/BluetoothGattService.h',
         'bluetooth2/BluetoothLeDeviceEvent.h',
         'bluetooth2/BluetoothManager.h',
         'bluetooth2/BluetoothPairingHandle.h',
         'bluetooth2/BluetoothPairingListener.h',
     ]
     IPDL_SOURCES += [
         'bluetooth2/ipc/BluetoothTypes.ipdlh',
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -204,16 +204,18 @@ var interfaceNamesInGlobalScope =
     {name: "BluetoothGattCharacteristic", b2g: true,
      permission: ["bluetooth"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "BluetoothGattCharacteristicEvent", b2g: true,
      permission: ["bluetooth"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "BluetoothGattDescriptor", b2g: true, permission: ["bluetooth"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "BluetoothGattServer", b2g: true, permission: ["bluetooth"]},
+// IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "BluetoothGattService", b2g: true, permission: ["bluetooth"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "BluetoothLeDeviceEvent", b2g: true, permission: ["bluetooth"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "BluetoothManager", b2g: true, permission: ["bluetooth"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "BluetoothPairingEvent", b2g: true, permission: ["bluetooth"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/webidl/BluetoothAdapter2.webidl
+++ b/dom/webidl/BluetoothAdapter2.webidl
@@ -35,16 +35,17 @@ dictionary MediaPlayStatus
 [CheckAnyPermissions="bluetooth"]
 interface BluetoothAdapter : EventTarget {
   readonly attribute BluetoothAdapterState  state;
   [AvailableIn=CertifiedApps]
   readonly attribute DOMString              address;
   readonly attribute DOMString              name;
   readonly attribute boolean                discoverable;
   readonly attribute boolean                discovering;
+  readonly attribute BluetoothGattServer?   gattServer;
 
   [AvailableIn=CertifiedApps]
   readonly attribute BluetoothPairingListener pairingReqs;
 
   // Fired when attribute(s) of BluetoothAdapter changed
            attribute EventHandler   onattributechanged;
 
   // Fired when a remote device gets paired with the adapter
new file mode 100644
--- /dev/null
+++ b/dom/webidl/BluetoothGattServer.webidl
@@ -0,0 +1,13 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ */
+
+[CheckAnyPermissions="bluetooth"]
+interface BluetoothGattServer
+{
+  /* The implementation of BluetoothGattServer will come later.
+   * (see dependent bugs of bug 933358)
+   */
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -672,16 +672,17 @@ if CONFIG['MOZ_B2G_BT']:
         WEBIDL_FILES += [
             'BluetoothAdapter2.webidl',
             'BluetoothClassOfDevice.webidl',
             'BluetoothDevice2.webidl',
             'BluetoothDiscoveryHandle.webidl',
             'BluetoothGatt.webidl',
             'BluetoothGattCharacteristic.webidl',
             'BluetoothGattDescriptor.webidl',
+            'BluetoothGattServer.webidl',
             'BluetoothGattService.webidl',
             'BluetoothLeDeviceEvent.webidl',
             'BluetoothManager2.webidl',
             'BluetoothPairingHandle.webidl',
             'BluetoothPairingListener.webidl',
         ]
 
 if CONFIG['MOZ_SIMPLEPUSH']:
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -410,17 +410,19 @@ pref("devtools.remote.usb.enabled", fals
 pref("devtools.remote.wifi.enabled", false);
 
 pref("font.size.inflation.minTwips", 0);
 
 // When true, zooming will be enabled on all sites, even ones that declare user-scalable=no.
 pref("browser.ui.zoom.force-user-scalable", false);
 
 pref("ui.zoomedview.disabled", false);
-pref("ui.zoomedview.limitReadableSize", 8);  // value in layer pixels
+pref("ui.zoomedview.limitReadableSize", 8); // value in layer pixels
+pref("ui.zoomedview.defaultZoomFactor", 2);
+pref("ui.zoomedview.simplified", true); // Do not display all the zoomed view controls
 
 pref("ui.touch.radius.enabled", false);
 pref("ui.touch.radius.leftmm", 3);
 pref("ui.touch.radius.topmm", 5);
 pref("ui.touch.radius.rightmm", 3);
 pref("ui.touch.radius.bottommm", 2);
 pref("ui.touch.radius.visitedWeight", 120);
 
--- a/mobile/android/base/ZoomedView.java
+++ b/mobile/android/base/ZoomedView.java
@@ -5,16 +5,17 @@
 
 package org.mozilla.gecko;
 
 import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
 import org.mozilla.gecko.gfx.LayerView;
 import org.mozilla.gecko.gfx.PanZoomController;
 import org.mozilla.gecko.gfx.PointUtils;
 import org.mozilla.gecko.mozglue.DirectBufferAllocator;
+import org.mozilla.gecko.PrefsHelper;
 import org.mozilla.gecko.util.GeckoEventListener;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import org.json.JSONException;
 import org.json.JSONObject;
 
 import android.content.Context;
 import android.content.res.Resources;
@@ -46,27 +47,32 @@ import android.widget.TextView;
 
 import java.nio.ByteBuffer;
 import java.text.DecimalFormat;
 
 public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChangedListener,
         LayerView.ZoomedViewListener, GeckoEventListener {
     private static final String LOGTAG = "Gecko" + ZoomedView.class.getSimpleName();
 
-    private static final float[] ZOOM_FACTORS_LIST = {2.0f, 3.0f, 1.5f};
+    private static final float[] ZOOM_FACTORS_LIST = {2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 1.5f};
     private static final int W_CAPTURED_VIEW_IN_PERCENT = 50;
     private static final int H_CAPTURED_VIEW_IN_PERCENT = 50;
     private static final int MINIMUM_DELAY_BETWEEN_TWO_RENDER_CALLS_NS = 1000000;
     private static final int DELAY_BEFORE_NEXT_RENDER_REQUEST_MS = 2000;
     private static final int OPENING_ANIMATION_DURATION_MS = 250;
     private static final int CLOSING_ANIMATION_DURATION_MS = 150;
     private static final float OVERSHOOT_INTERPOLATOR_TENSION = 1.5f;
 
     private float zoomFactor;
     private int currentZoomFactorIndex;
+    private boolean isSimplifiedUI;
+    private int defaultZoomFactor;
+    private int prefDefaultZoomFactorObserverId;
+    private int prefSimplifiedUIObserverId;
+
     private ImageView zoomedImageView;
     private LayerView layerView;
     private int viewWidth;
     private int viewHeight; // Only the zoomed view height, no toolbar, no shadow ...
     private int viewContainerWidth;
     private int viewContainerHeight; // Zoomed view height with toolbar and other elements like shadow, ...
     private int containterSize; // shadow, margin, ...
     private Point lastPosition;
@@ -208,78 +214,89 @@ public class ZoomedView extends FrameLay
     }
 
     public ZoomedView(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
     public ZoomedView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
+        isSimplifiedUI = true;
+        getPrefs();
+        currentZoomFactorIndex = 0;
         returnValue = new PointF();
         animationStart = new PointF();
-        currentZoomFactorIndex = 0;
-        zoomFactor = ZOOM_FACTORS_LIST[currentZoomFactorIndex];
         requestRenderRunnable = new Runnable() {
             @Override
             public void run() {
                 requestZoomedViewRender();
             }
         };
         touchListener = new ZoomedViewTouchListener();
         EventDispatcher.getInstance().registerGeckoThreadListener(this,
                 "Gesture:clusteredLinksClicked", "Window:Resize", "Content:LocationChange",
                 "Gesture:CloseZoomedView");
     }
 
     void destroy() {
+        PrefsHelper.removeObserver(prefDefaultZoomFactorObserverId);
+        PrefsHelper.removeObserver(prefSimplifiedUIObserverId);
         ThreadUtils.removeCallbacksFromUiThread(requestRenderRunnable);
         EventDispatcher.getInstance().unregisterGeckoThreadListener(this,
                 "Gesture:clusteredLinksClicked", "Window:Resize", "Content:LocationChange",
                 "Gesture:CloseZoomedView");
     }
 
     // This method (onFinishInflate) is called only when the zoomed view class is used inside
     // an xml structure <org.mozilla.gecko.ZoomedView ...
     // It won't be called if the class is used from java code like "new  ZoomedView(context);"
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
         closeButton = (ImageView) findViewById(R.id.dialog_close);
         changeZoomFactorButton = (TextView) findViewById(R.id.change_zoom_factor);
         zoomedImageView = (ImageView) findViewById(R.id.zoomed_image_view);
 
-        setTextInZoomFactorButton(ZOOM_FACTORS_LIST[0]);
+        setTextInZoomFactorButton(zoomFactor);
 
         toolbarHeight = getResources().getDimensionPixelSize(R.dimen.zoomed_view_toolbar_height);
         containterSize = getResources().getDimensionPixelSize(R.dimen.drawable_dropshadow_size);
         cornerRadius = getResources().getDimensionPixelSize(R.dimen.button_corner_radius);
 
         moveToolbar(true);
     }
 
     private void setListeners() {
         closeButton.setOnClickListener(new View.OnClickListener() {
             public void onClick(View view) {
                 stopZoomDisplay(true);
             }
         });
 
-        changeZoomFactorButton.setOnClickListener(new View.OnClickListener() {
-            public void onClick(View view) {
-                changeZoomFactor();
+        changeZoomFactorButton.setOnTouchListener(new  OnTouchListener() {
+            public boolean onTouch(View v, MotionEvent event) {
+
+                if (event.getAction() == MotionEvent.ACTION_UP) {
+                    if (event.getX() >= (changeZoomFactorButton.getLeft() + changeZoomFactorButton.getWidth() / 2)) {
+                        changeZoomFactor(true);
+                    } else {
+                        changeZoomFactor(false);
+                    }
+                }
+                return true;
             }
         });
 
         setOnTouchListener(touchListener);
     }
 
     private void removeListeners() {
         closeButton.setOnClickListener(null);
 
-        changeZoomFactorButton.setOnClickListener(null);
+        changeZoomFactorButton.setOnTouchListener(null);
 
         setOnTouchListener(null);
     }
     /*
      * Convert a click from ZoomedView. Return the position of the click in the
      * LayerView
      */
     private PointF getUnzoomedPositionFromPointInZoomedView(float x, float y) {
@@ -474,17 +491,57 @@ public class ZoomedView extends FrameLay
     private void shouldBlockUpdate(boolean shouldBlockUpdate) {
         stopUpdateView = shouldBlockUpdate;
     }
 
     private Bitmap.Config getBitmapConfig() {
         return (GeckoAppShell.getScreenDepth() == 24) ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
     }
 
-    private void startZoomDisplay(LayerView aLayerView, final int leftFromGecko, final int topFromGecko) {
+    private void getPrefs() {
+        prefSimplifiedUIObserverId = PrefsHelper.getPref("ui.zoomedview.simplified", new PrefsHelper.PrefHandlerBase() {
+            @Override
+            public void prefValue(String pref, boolean simplified) {
+                isSimplifiedUI = simplified;
+                if (simplified) {
+                    changeZoomFactorButton.setVisibility(View.INVISIBLE);
+                    zoomFactor = (float) defaultZoomFactor;
+                } else {
+                    zoomFactor = ZOOM_FACTORS_LIST[currentZoomFactorIndex];
+                    setTextInZoomFactorButton(zoomFactor);
+                    changeZoomFactorButton.setVisibility(View.VISIBLE);
+                }
+            }
+
+            @Override
+            public boolean isObserver() {
+                return true;
+            }
+        });
+
+        prefDefaultZoomFactorObserverId = PrefsHelper.getPref("ui.zoomedview.defaultZoomFactor", new PrefsHelper.PrefHandlerBase() {
+            @Override
+            public void prefValue(String pref, int defaultZoomFactorFromSettings) {
+                defaultZoomFactor = defaultZoomFactorFromSettings;
+                if (isSimplifiedUI) {
+                    zoomFactor = (float) defaultZoomFactor;
+                } else {
+                    zoomFactor = ZOOM_FACTORS_LIST[currentZoomFactorIndex];
+                    setTextInZoomFactorButton(zoomFactor);
+                }
+            }
+
+            @Override
+            public boolean isObserver() {
+                return true;
+            }
+        });
+    }
+
+private void startZoomDisplay(LayerView aLayerView, final int leftFromGecko, final int topFromGecko) {
         if (layerView == null) {
             layerView = aLayerView;
             layerView.addZoomedViewListener(this);
             layerView.setOnMetricsChangedZoomedViewportListener(this);
             ImmutableViewportMetrics metrics = layerView.getViewportMetrics();
             setCapturedSize(metrics);
         }
         startTimeReRender = 0;
@@ -509,32 +566,36 @@ public class ZoomedView extends FrameLay
             if (layerView != null) {
                 layerView.setOnMetricsChangedZoomedViewportListener(null);
                 layerView.removeZoomedViewListener(this);
                 layerView = null;
             }
         }
     }
 
-    private void changeZoomFactor() {
-        if (currentZoomFactorIndex < ZOOM_FACTORS_LIST.length - 1) {
+    private void changeZoomFactor(boolean zoomIn) {
+        if (zoomIn && currentZoomFactorIndex < ZOOM_FACTORS_LIST.length - 1) {
             currentZoomFactorIndex++;
+        } else if (zoomIn && currentZoomFactorIndex >= ZOOM_FACTORS_LIST.length - 1) {
+            currentZoomFactorIndex = 0;
+        } else if (!zoomIn && currentZoomFactorIndex > 0) {
+            currentZoomFactorIndex--;
         } else {
-            currentZoomFactorIndex = 0;
+            currentZoomFactorIndex = ZOOM_FACTORS_LIST.length - 1;
         }
         zoomFactor = ZOOM_FACTORS_LIST[currentZoomFactorIndex];
 
         ImmutableViewportMetrics metrics = layerView.getViewportMetrics();
         refreshZoomedViewSize(metrics);
         setTextInZoomFactorButton(zoomFactor);
     }
 
     private void setTextInZoomFactorButton(float zoom) {
         final String percentageValue = Integer.toString((int) (100*zoom));
-        changeZoomFactorButton.setText(getResources().getString(R.string.percent, percentageValue));
+        changeZoomFactorButton.setText("- " + getResources().getString(R.string.percent, percentageValue) + " +");
     }
 
     @Override
     public void handleMessage(final String event, final JSONObject message) {
         ThreadUtils.postToUiThread(new Runnable() {
             @Override
             public void run() {
                 try {
--- a/mobile/android/base/home/HistoryPanel.java
+++ b/mobile/android/base/home/HistoryPanel.java
@@ -5,28 +5,31 @@
 
 package org.mozilla.gecko.home;
 
 import java.util.Date;
 import java.util.EnumSet;
 
 import org.json.JSONException;
 import org.json.JSONObject;
+
 import org.mozilla.gecko.EventDispatcher;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoEvent;
 import org.mozilla.gecko.GeckoProfile;
 import org.mozilla.gecko.R;
+import org.mozilla.gecko.RestrictedProfiles;
 import org.mozilla.gecko.Telemetry;
 import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.db.BrowserContract.Combined;
 import org.mozilla.gecko.db.BrowserContract.History;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.home.HomeContextMenuInfo.RemoveItemType;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
+import org.mozilla.gecko.restrictions.Restriction;
 
 import android.app.AlertDialog;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.database.Cursor;
 import android.graphics.Typeface;
 import android.os.Bundle;
@@ -34,16 +37,17 @@ import android.support.v4.content.Loader
 import android.text.SpannableStringBuilder;
 import android.text.TextPaint;
 import android.text.method.LinkMovementMethod;
 import android.text.style.ClickableSpan;
 import android.text.style.StyleSpan;
 import android.text.style.UnderlineSpan;
 import android.util.Log;
 import android.util.SparseArray;
+import android.view.ContextMenu;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewStub;
 import android.widget.AdapterView;
 import android.widget.ImageView;
 import android.widget.TextView;
 
@@ -157,16 +161,25 @@ public class HistoryPanel extends HomeFr
                 });
 
                 dialogBuilder.show();
             }
         });
     }
 
     @Override
+    public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
+        super.onCreateContextMenu(menu, view, menuInfo);
+
+        if (!RestrictedProfiles.isAllowed(getActivity(), Restriction.DISALLOW_CLEAR_HISTORY)) {
+            menu.findItem(R.id.home_remove).setVisible(false);
+        }
+    }
+
+    @Override
     public void onDestroyView() {
         super.onDestroyView();
         mList = null;
         mEmptyView = null;
         mClearHistoryButton = null;
     }
 
     @Override
@@ -201,17 +214,19 @@ public class HistoryPanel extends HomeFr
         public Cursor loadCursor() {
             final ContentResolver cr = getContext().getContentResolver();
             return mDB.getRecentHistory(cr, HISTORY_LIMIT);
         }
     }
 
     private void updateUiFromCursor(Cursor c) {
         if (c != null && c.getCount() > 0) {
-            mClearHistoryButton.setVisibility(View.VISIBLE);
+            if (RestrictedProfiles.isAllowed(getActivity(), Restriction.DISALLOW_CLEAR_HISTORY)) {
+                mClearHistoryButton.setVisibility(View.VISIBLE);
+            }
             return;
         }
 
         // Cursor is empty, so hide the "Clear browsing history" button,
         // and set the empty view if it hasn't been set already.
         mClearHistoryButton.setVisibility(View.GONE);
 
         if (mEmptyView == null) {
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -696,8 +696,14 @@ just addresses the organization to follo
 <!ENTITY restriction_disallow_devtools_title "Disallow developer tools">
 <!ENTITY restriction_disallow_devtools_description "Disallow usage of developer tools.">
 <!ENTITY restriction_disallow_customize_home_title "Disallow customizing home">
 <!ENTITY restriction_disallow_customize_home_description "Disallow customizing home panels.">
 <!ENTITY restriction_disallow_private_browsing_title "Disallow private browsing">
 <!ENTITY restriction_disallow_private_browsing_description "Disallow private browsing mode.">
 <!ENTITY restriction_disallow_location_services_title "Disallow location services">
 <!ENTITY restriction_disallow_location_services_description "Disallow sharing of location data to improve geolocation service.">
+<!ENTITY restriction_disallow_display_settings_title "Disallow display settings">
+<!ENTITY restriction_disallow_display_settings_description "Disallow changing of advanced display settings.">
+<!ENTITY restriction_disallow_clear_history_title "Disallow clearing history">
+<!ENTITY restriction_disallow_clear_history_description "Disallow clearing of browser history.">
+<!ENTITY restriction_disallow_master_password_title "Disallow master password">
+<!ENTITY restriction_disallow_master_password_description "Disallow setting a master password for logins.">
--- a/mobile/android/base/preferences/GeckoPreferences.java
+++ b/mobile/android/base/preferences/GeckoPreferences.java
@@ -130,16 +130,17 @@ OnSharedPreferenceChangeListener
     private static final String PREFS_SYNC = NON_PREF_PREFIX + "sync";
     public static final String PREFS_OPEN_URLS_IN_PRIVATE = NON_PREF_PREFIX + "openExternalURLsPrivately";
     public static final String PREFS_VOICE_INPUT_ENABLED = NON_PREF_PREFIX + "voice_input_enabled";
     public static final String PREFS_QRCODE_ENABLED = NON_PREF_PREFIX + "qrcode_enabled";
     private static final String PREFS_DEVTOOLS = NON_PREF_PREFIX + "devtools.enabled";
     private static final String PREFS_CUSTOMIZE_HOME = NON_PREF_PREFIX + "customize_home";
     private static final String PREFS_TRACKING_PROTECTION_PRIVATE_BROWSING = "privacy.trackingprotection.pbmode.enabled";
     private static final String PREFS_TRACKING_PROTECTION_LEARN_MORE = NON_PREF_PREFIX + "trackingprotection.learn_more";
+    private static final String PREFS_CATEGORY_PRIVATE_DATA = NON_PREF_PREFIX + "category_private_data";
 
     private static final String ACTION_STUMBLER_UPLOAD_PREF = AppConstants.ANDROID_PACKAGE_NAME + ".STUMBLER_PREF";
 
 
     // This isn't a Gecko pref, even if it looks like one.
     private static final String PREFS_BROWSER_LOCALE = "locale";
 
     public static final String PREFS_RESTORE_SESSION = NON_PREF_PREFIX + "restoreSession3";
@@ -486,16 +487,18 @@ OnSharedPreferenceChangeListener
 
                 if (header.id == R.id.pref_header_language && !localeSwitchingIsEnabled) {
                     // If locale switching is disabled, remove the section
                     // entirely. This logic will need to be extended when
                     // content language selection (Bug 881510) is implemented.
                     iterator.remove();
                 } else if (header.id == R.id.pref_header_devtools && !RestrictedProfiles.isAllowed(this, Restriction.DISALLOW_DEVELOPER_TOOLS)) {
                     iterator.remove();
+                } else if (header.id == R.id.pref_header_display && !RestrictedProfiles.isAllowed(this, Restriction.DISALLOW_DISPLAY_SETTINGS)) {
+                    iterator.remove();
                 }
             }
         }
     }
 
     @Override
     public void onWindowFocusChanged(boolean hasFocus) {
         if (!hasFocus || mInitialized)
@@ -710,16 +713,23 @@ OnSharedPreferenceChangeListener
 
                 if (PREFS_CUSTOMIZE_HOME.equals(key)) {
                     if (!RestrictedProfiles.isAllowed(this, Restriction.DISALLOW_CUSTOMIZE_HOME)) {
                         preferences.removePreference(pref);
                         i--;
                         continue;
                     }
                 }
+                if (PREFS_CATEGORY_PRIVATE_DATA.equals(key)) {
+                    if (!RestrictedProfiles.isAllowed(this, Restriction.DISALLOW_CLEAR_HISTORY)) {
+                        preferences.removePreference(pref);
+                        i--;
+                        continue;
+                    }
+                }
 
                 setupPreferences((PreferenceGroup) pref, prefs);
             } else {
                 pref.setOnPreferenceChangeListener(this);
                 if (PREFS_UPDATER_AUTODOWNLOAD.equals(key)) {
                     if (!AppConstants.MOZ_UPDATER) {
                         preferences.removePreference(pref);
                         i--;
@@ -857,16 +867,22 @@ OnSharedPreferenceChangeListener
                         continue;
                     }
                 } else if (PREFS_TRACKING_PROTECTION_LEARN_MORE.equals(key)) {
                     if (!RestrictedProfiles.isAllowed(this, Restriction.DISALLOW_PRIVATE_BROWSING)) {
                         preferences.removePreference(pref);
                         i--;
                         continue;
                     }
+                } else if (PREFS_MP_ENABLED.equals(key)) {
+                    if (!RestrictedProfiles.isAllowed(this, Restriction.DISALLOW_MASTER_PASSWORD)) {
+                        preferences.removePreference(pref);
+                        i--;
+                        continue;
+                    }
                 }
 
                 // Some Preference UI elements are not actually preferences,
                 // but they require a key to work correctly. For example,
                 // "Clear private data" requires a key for its state to be
                 // saved when the orientation changes. It uses the
                 // "android.not_a_preference.privacy.clear" key - which doesn't
                 // exist in Gecko - to satisfy this requirement.
--- a/mobile/android/base/resources/layout/zoomed_view.xml
+++ b/mobile/android/base/resources/layout/zoomed_view.xml
@@ -15,25 +15,30 @@
     android:visibility="gone">
 
     <RelativeLayout
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_alignParentLeft="true"
         android:layout_alignParentTop="true"
         android:background="@drawable/toolbar_grey_round">
+        <!--
+           Zoom factor button is invisible by default. Set ui.zoomedview.simplified to false
+           in order to display the button in the zoomed view tool bar
+        -->
         <TextView
             android:id="@+id/change_zoom_factor"
             android:layout_width="wrap_content"
             android:layout_height="@dimen/zoomed_view_toolbar_height"
             android:background="@android:color/transparent"
             android:padding="12dip"
             android:layout_alignLeft="@+id/zoomed_image_view"
             android:textSize="16sp"
-            android:textColor="@color/text_and_tabs_tray_grey"/>
+            android:textColor="@color/text_and_tabs_tray_grey"
+            android:visibility="invisible"/>
         <ImageView
             android:id="@+id/dialog_close"
             android:scaleType="center"
             android:layout_width="@dimen/zoomed_view_toolbar_height"
             android:layout_height="@dimen/zoomed_view_toolbar_height"
             android:layout_alignRight="@id/zoomed_image_view"
             android:src="@drawable/close_edit_mode_selector"/>
         <ImageView
--- a/mobile/android/base/resources/xml-v11/preference_headers.xml
+++ b/mobile/android/base/resources/xml-v11/preference_headers.xml
@@ -11,17 +11,18 @@
 
     <header android:fragment="org.mozilla.gecko.preferences.GeckoPreferenceFragment"
             android:title="@string/pref_header_customize">
         <extra android:name="resource"
                android:value="preferences_customize_tablet"/>
     </header>
 
     <header android:fragment="org.mozilla.gecko.preferences.GeckoPreferenceFragment"
-            android:title="@string/pref_header_display">
+            android:title="@string/pref_header_display"
+            android:id="@+id/pref_header_display">
         <extra android:name="resource"
                android:value="preferences_display"/>
     </header>
 
     <header android:fragment="org.mozilla.gecko.preferences.GeckoPreferenceFragment"
             android:title="@string/pref_header_privacy_short">
         <extra android:name="resource"
                android:value="preferences_privacy"/>
--- a/mobile/android/base/resources/xml/preference_headers.xml
+++ b/mobile/android/base/resources/xml/preference_headers.xml
@@ -8,9 +8,12 @@
 
 <preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
     <header android:fragment="org.mozilla.gecko.preferences.GeckoPreferenceFragment"
             android:id="@+id/pref_header_language">
     </header>
     <header android:fragment="org.mozilla.gecko.preferences.GeckoPreferenceFragment"
             android:id="@+id/pref_header_devtools">
     </header>
+    <header android:fragment="org.mozilla.gecko.preferences.GeckoPreferenceFragment"
+            android:id="@+id/pref_header_display">
+    </header>
 </preference-headers>
--- a/mobile/android/base/resources/xml/preferences_privacy.xml
+++ b/mobile/android/base/resources/xml/preferences_privacy.xml
@@ -48,17 +48,17 @@
 
         <CheckBoxPreference android:key="privacy.masterpassword.enabled"
                             android:title="@string/pref_use_master_password"
                             android:persistent="false" />
 
     </PreferenceCategory>
 
     <!-- keys prefixed with "android.not_a_preference." are not synced with Gecko -->
-    <PreferenceCategory android:title="@string/pref_clear_private_data_category">
+    <PreferenceCategory android:key="android.not_a_preference.category_private_data" android:title="@string/pref_clear_private_data_category">
 
         <org.mozilla.gecko.preferences.PrivateDataPreference
                             android:key="android.not_a_preference.privacy.clear"
                             android:title="@string/pref_clear_private_data"
                             android:persistent="true"
                             android:positiveButtonText="@string/button_clear_data"
                             gecko:entries="@array/pref_private_data_entries"
                             gecko:entryValues="@array/pref_private_data_values"
--- a/mobile/android/base/restrictions/RestrictedProfileConfiguration.java
+++ b/mobile/android/base/restrictions/RestrictedProfileConfiguration.java
@@ -19,21 +19,25 @@ public class RestrictedProfileConfigurat
             Restriction.DISALLOW_INSTALL_EXTENSION,
             Restriction.DISALLOW_INSTALL_APPS,
             Restriction.DISALLOW_TOOLS_MENU,
             Restriction.DISALLOW_REPORT_SITE_ISSUE,
             Restriction.DISALLOW_IMPORT_SETTINGS,
             Restriction.DISALLOW_DEVELOPER_TOOLS,
             Restriction.DISALLOW_CUSTOMIZE_HOME,
             Restriction.DISALLOW_PRIVATE_BROWSING,
-            Restriction.DISALLOW_LOCATION_SERVICE
+            Restriction.DISALLOW_LOCATION_SERVICE,
+            Restriction.DISALLOW_DISPLAY_SETTINGS,
+            Restriction.DISALLOW_CLEAR_HISTORY,
+            Restriction.DISALLOW_MASTER_PASSWORD
     );
 
     private static final String ABOUT_ADDONS = "about:addons";
     private static final String ABOUT_PRIVATE_BROWSING = "about:privatebrowsing";
+    private static final String ABOUT_CONFIG = "about:config";
 
     private Context context;
 
     public RestrictedProfileConfiguration(Context context) {
         this.context = context.getApplicationContext();
     }
 
     @Override
@@ -54,16 +58,21 @@ public class RestrictedProfileConfigurat
         if (!isAllowed(Restriction.DISALLOW_INSTALL_EXTENSION) && url.toLowerCase().startsWith(ABOUT_ADDONS)) {
             return false;
         }
 
         if (!isAllowed(Restriction.DISALLOW_PRIVATE_BROWSING) && url.toLowerCase().startsWith(ABOUT_PRIVATE_BROWSING)) {
             return false;
         }
 
+        if (url.toLowerCase().startsWith(ABOUT_CONFIG)) {
+            // Always block access to about:config to prevent circumventing restrictions (Bug 1189233)
+            return false;
+        }
+
         return true;
     }
 
     @Override
     public boolean isRestricted() {
         return true;
     }
 
--- a/mobile/android/base/restrictions/Restriction.java
+++ b/mobile/android/base/restrictions/Restriction.java
@@ -83,16 +83,34 @@ public enum Restriction {
             R.string.restriction_disallow_private_browsing_title,
             R.string.restriction_disallow_private_browsing_description
     ),
 
     DISALLOW_LOCATION_SERVICE(
             17, "no_location_service",
             R.string.restriction_disallow_location_services_title,
             R.string.restriction_disallow_location_services_description
+    ),
+
+    DISALLOW_DISPLAY_SETTINGS(
+            18, "no_display_settings",
+            R.string.restriction_disallow_display_settings_title,
+            R.string.restriction_disallow_display_settings_description
+    ),
+
+    DISALLOW_CLEAR_HISTORY(
+            19, "no_clear_history",
+            R.string.restriction_disallow_clear_history_title,
+            R.string.restriction_disallow_clear_history_description
+    ),
+
+    DISALLOW_MASTER_PASSWORD(
+            20, "no_master_password",
+            R.string.restriction_disallow_master_password_title,
+            R.string.restriction_disallow_master_password_description
     );
 
     public final int id;
     public final String name;
     public final int titleResource;
     public final int descriptionResource;
 
     Restriction(final int id, final String name, int titleResource, int descriptionResource) {
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -552,16 +552,22 @@
   <string name="restriction_disallow_devtools_title">&restriction_disallow_devtools_title;</string>
   <string name="restriction_disallow_devtools_description">&restriction_disallow_devtools_description;</string>
   <string name="restriction_disallow_customize_home_title">&restriction_disallow_customize_home_title;</string>
   <string name="restriction_disallow_customize_home_description">&restriction_disallow_customize_home_description;</string>
   <string name="restriction_disallow_private_browsing_title">&restriction_disallow_private_browsing_title;</string>
   <string name="restriction_disallow_private_browsing_description">&restriction_disallow_private_browsing_description;</string>
   <string name="restriction_disallow_location_services_title">&restriction_disallow_location_services_title;</string>
   <string name="restriction_disallow_location_services_description">&restriction_disallow_location_services_description;</string>
+  <string name="restriction_disallow_display_settings_title">&restriction_disallow_display_settings_title;</string>
+  <string name="restriction_disallow_display_settings_description">&restriction_disallow_display_settings_description;</string>
+  <string name="restriction_disallow_clear_history_title">&restriction_disallow_clear_history_title;</string>
+  <string name="restriction_disallow_clear_history_description">&restriction_disallow_clear_history_description;</string>
+  <string name="restriction_disallow_master_password_title">&restriction_disallow_master_password_title;</string>
+  <string name="restriction_disallow_master_password_description">&restriction_disallow_master_password_description;</string>
 
   <!-- Miscellaneous -->
   <string name="ellipsis">&ellipsis;</string>
 
   <string name="colon">&colon;</string>
 
   <string name="percent">&percent;</string>
 
--- a/mobile/android/base/toolbar/ToolbarEditLayout.java
+++ b/mobile/android/base/toolbar/ToolbarEditLayout.java
@@ -12,16 +12,18 @@ import android.content.Intent;
 import android.speech.RecognizerIntent;
 import android.widget.Button;
 import android.widget.ImageButton;
 import org.mozilla.gecko.ActivityHandlerHelper;
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoSharedPrefs;
 import org.mozilla.gecko.R;
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.animation.PropertyAnimator;
 import org.mozilla.gecko.animation.PropertyAnimator.PropertyAnimationListener;
 import org.mozilla.gecko.preferences.GeckoPreferences;
 import org.mozilla.gecko.toolbar.BrowserToolbar.OnCommitListener;
 import org.mozilla.gecko.toolbar.BrowserToolbar.OnDismissListener;
 import org.mozilla.gecko.toolbar.BrowserToolbar.OnFilterListener;
 import org.mozilla.gecko.toolbar.BrowserToolbar.TabEditingState;
 import org.mozilla.gecko.util.ActivityResultHandler;
@@ -222,26 +224,28 @@ public class ToolbarEditLayout extends T
         if (!voiceIsSupported) {
             return false;
         }
         return GeckoSharedPrefs.forApp(context)
                 .getBoolean(GeckoPreferences.PREFS_VOICE_INPUT_ENABLED, true);
     }
 
     private void launchVoiceRecognizer() {
+        Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.ACTIONBAR, "voice_input_launch");
         final Intent intent = InputOptionsUtils.createVoiceRecognizerIntent(getResources().getString(R.string.voicesearch_prompt));
 
         Activity activity = GeckoAppShell.getGeckoInterface().getActivity();
         ActivityHandlerHelper.startIntentForActivity(activity, intent, new ActivityResultHandler() {
             @Override
             public void onActivityResult(int resultCode, Intent data) {
                 if (resultCode != Activity.RESULT_OK) {
                     return;
                 }
 
+                Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.ACTIONBAR, "voice_input_success");
                 // We have RESULT_OK, not RESULT_NO_MATCH so it should be safe to assume that
                 // we have at least one match. We only need one: this will be
                 // used for showing the user search engines with this search term in it.
                 List<String> voiceStrings = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
                 String text = voiceStrings.get(0);
                 mEditText.setText(text);
                 mEditText.setSelection(0, text.length());
 
@@ -257,25 +261,27 @@ public class ToolbarEditLayout extends T
         if (!qrCodeIsSupported) {
             return false;
         }
         return GeckoSharedPrefs.forApp(context)
                 .getBoolean(GeckoPreferences.PREFS_QRCODE_ENABLED, true);
     }
 
     private void launchQRCodeReader() {
+        Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.ACTIONBAR, "qrcode_input_launch");
         final Intent intent = InputOptionsUtils.createQRCodeReaderIntent();
 
         Activity activity = GeckoAppShell.getGeckoInterface().getActivity();
         ActivityHandlerHelper.startIntentForActivity(activity, intent, new ActivityResultHandler() {
             @Override
             public void onActivityResult(int resultCode, Intent intent) {
                 if (resultCode == Activity.RESULT_OK) {
                     String text = intent.getStringExtra("SCAN_RESULT");
                     if (!StringUtils.isSearchQuery(text, false)) {
+                        Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.ACTIONBAR, "qrcode_input_success");
                         mEditText.setText(text);
                         mEditText.selectAll();
 
                         // Queuing up the keyboard show action.
                         // At this point the app has not resumed yet, and trying to show
                         // the keyboard will fail.
                         showKeyboardOnFocus = true;
                     }
--- a/mobile/android/tests/browser/robocop/robocop.ini
+++ b/mobile/android/tests/browser/robocop/robocop.ini
@@ -133,16 +133,17 @@ skip-if = android_version == "18"
 [testOrderedBroadcast.java]
 [testOSLocale.java]
 # disabled on 2.3 and 4.3: Bug 1124494
 skip-if = android_version == "10" || android_version == "18"
 [testReaderView.java]
 [testReadingListCache.java]
 [testResourceSubstitutions.java]
 [testRestrictedProfiles.java]
+[testRestrictions.java]
 [testSessionFormData.java]
 [testSharedPreferences.java]
 [testSimpleDiscovery.java]
 [testTrackingProtection.java]
 # disabled on 4.3, bug 1158363
 skip-if = android_version == "18"
 [testUITelemetry.java]
 [testVideoControls.java]
new file mode 100644
--- /dev/null
+++ b/mobile/android/tests/browser/robocop/testRestrictions.java
@@ -0,0 +1,24 @@
+/* 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.tests;
+
+import static org.mozilla.gecko.tests.helpers.AssertionHelper.fAssertTrue;
+
+import org.mozilla.gecko.RestrictedProfiles;
+import org.mozilla.gecko.restrictions.Restriction;
+import org.mozilla.gecko.tests.helpers.GeckoHelper;
+
+public class testRestrictions extends UITest {
+    public void testRestrictions() {
+        GeckoHelper.blockForReady();
+
+        // No restrictions should be enforced when using a normal profile
+        for (Restriction restriction : Restriction.values()) {
+            fAssertTrue(String.format("Restriction %s is not enforced", restriction.name),
+                RestrictedProfiles.isAllowed(getActivity(), restriction)
+            );
+        }
+    }
+}
--- a/toolkit/components/parentalcontrols/nsIParentalControlsService.idl
+++ b/toolkit/components/parentalcontrols/nsIParentalControlsService.idl
@@ -6,17 +6,17 @@
 
 #include "nsISupports.idl"
 
 interface nsIURI;
 interface nsIFile;
 interface nsIInterfaceRequestor;
 interface nsIArray;
 
-[scriptable, uuid(4ee714a7-e9a8-43ed-a061-60155b63e290)]
+[scriptable, uuid(30ff7af7-ae52-4bd6-88c0-4a8ce4f37bdc)]
 interface nsIParentalControlsService : nsISupports
 {
   /**
    * Action types that can be blocked for users.
    */
   const short DOWNLOAD = 1; // Downloading files
   const short INSTALL_EXTENSION = 2; // Installing extensions
   const short INSTALL_APP = 3; // Installing webapps
@@ -27,16 +27,19 @@ interface nsIParentalControlsService : n
   const short SET_IMAGE = 8; // Setting images as wall paper
   const short MODIFY_ACCOUNTS = 9; // Modifying system accounts
   const short REMOTE_DEBUGGING = 10; // Remote debugging
   const short IMPORT_SETTINGS = 11; // Importing settings from other apps
   const short TOOLS_MENU = 12; // Hide tools menu entry
   const short REPORT_SITE_ISSUE = 13; // Hide "Report Site Issue" menu entry
   const short PRIVATE_BROWSING = 16; // Disallow usage of private browsing
   const short LOCATION_SERVICE = 17; // Sharing of location data to location service
+  const short DISPLAY_SETTINGS = 18; // Website display settings
+  const short CLEAR_HISTORY = 19; // Clear browsing history
+  const short MASTER_PASSWORD = 20; // Setting master password for logins
 
   /**
    * @returns true if the current user account has parental controls
    * restrictions enabled.
    */
   readonly attribute boolean parentalControlsEnabled;
 
   /**
--- a/toolkit/components/passwordmgr/LoginHelper.jsm
+++ b/toolkit/components/passwordmgr/LoginHelper.jsm
@@ -255,16 +255,53 @@ this.LoginHelper = {
 
     // Throws if there are bogus values.
     this.checkLoginValues(newLogin);
 
     return newLogin;
   },
 
   /**
+   * Removes duplicates from a list of logins.
+   *
+   * @param {nsILoginInfo[]} logins
+   *        A list of logins we want to deduplicate.
+   *
+   * @param {string[] = ["username", "password"]} uniqueKeys
+   *        A list of login attributes to use as unique keys for the deduplication.
+   *
+   * @returns {nsILoginInfo[]} list of unique logins.
+   */
+  dedupeLogins(logins, uniqueKeys = ["username", "password"]) {
+    const KEY_DELIMITER = ":";
+
+    // Generate a unique key string from a login.
+    function getKey(login, uniqueKeys) {
+      return uniqueKeys.reduce((prev, key) => prev + KEY_DELIMITER + login[key], "");
+    }
+
+    // We use a Map to easily lookup logins by their unique keys.
+    let loginsByKeys = new Map();
+    for (let login of logins) {
+      let key = getKey(login, uniqueKeys);
+      // If we find a more recently used login for the same key, replace the existing one.
+      if (loginsByKeys.has(key)) {
+        let loginDate = login.QueryInterface(Ci.nsILoginMetaInfo).timeLastUsed;
+        let storedLoginDate = loginsByKeys.get(key).QueryInterface(Ci.nsILoginMetaInfo).timeLastUsed;
+        if (loginDate < storedLoginDate) {
+          continue;
+        }
+      }
+      loginsByKeys.set(key, login);
+    }
+    // Return the map values in the form of an array.
+    return [...loginsByKeys.values()];
+  },
+
+  /**
    * Open the password manager window.
    *
    * @param {Window} window
    *                 the window from where we want to open the dialog
    *
    * @param {string} [filterString=""]
    *                 the filterString parameter to pass to the login manager dialog
    */
--- a/toolkit/components/passwordmgr/test/unit/head.js
+++ b/toolkit/components/passwordmgr/test/unit/head.js
@@ -12,16 +12,17 @@
 ////////////////////////////////////////////////////////////////////////////////
 //// Globals
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/LoginRecipes.jsm");
+Cu.import("resource://gre/modules/LoginHelper.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "DownloadPaths",
                                   "resource://gre/modules/DownloadPaths.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
                                   "resource://gre/modules/FileUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "OS",
                                   "resource://gre/modules/osfile.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
--- a/toolkit/components/passwordmgr/test/unit/test_logins_change.js
+++ b/toolkit/components/passwordmgr/test/unit/test_logins_change.js
@@ -47,16 +47,39 @@ function checkLoginInvalid(aLoginInfo, a
     passwordField: aLoginInfo.passwordField,
   })), aExpectedError);
 
   // Verify that no data was stored by the previous calls.
   LoginTestUtils.checkLogins([testLogin]);
   Services.logins.removeLogin(testLogin);
 }
 
+/**
+ * Verifies that two objects are not the same instance
+ * but have equal attributes.
+ *
+ * @param {Object} objectA
+ *        An object to compare.
+ *
+ * @param {Object} objectB
+ *        Another object to compare.
+ *
+ * @param {string[]} attributes
+ *        Attributes to compare.
+ *
+ * @return true if all passed attributes are equal for both objects, false otherwise.
+ */
+function compareAttributes(objectA, objectB, attributes) {
+  // If it's the same object, we want to return false.
+  if (objectA == objectB) {
+    return false;
+  }
+  return attributes.every(attr => objectA[attr] == objectB[attr]);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 //// Tests
 
 /**
  * Tests that adding logins to the database works.
  */
 add_task(function test_addLogin_removeLogin()
 {
@@ -290,8 +313,74 @@ add_task(function test_modifyLogin_nsIPr
   // Modifying a login to match an existing one should not be possible.
   Assert.throws(
          () => Services.logins.modifyLogin(loginInfo, differentLoginProperties),
          /already exists/);
   LoginTestUtils.checkLogins([loginInfo, differentLoginInfo]);
 
   LoginTestUtils.clearData();
 });
+
+/**
+ * Tests the login deduplication function.
+ */
+add_task(function test_deduplicate_logins() {
+  // Different key attributes combinations and the amount of unique
+  // results expected for the TestData login list.
+  let keyCombinations = [
+    {
+      keyset: ["username", "password"],
+      results: 13,
+    },
+    {
+      keyset: ["hostname", "username"],
+      results: 17,
+    },
+    {
+      keyset: ["hostname", "username", "password"],
+      results: 18,
+    },
+    {
+      keyset: ["hostname", "username", "password", "formSubmitURL"],
+      results: 22,
+    },
+  ];
+
+  let logins = TestData.loginList();
+
+  for (let testCase of keyCombinations) {
+    // Deduplicate the logins using the current testcase keyset.
+    let deduped = LoginHelper.dedupeLogins(logins, testCase.keyset);
+    Assert.equal(deduped.length, testCase.results, "Correct amount of results.");
+
+    // Checks that every login after deduping is unique.
+    Assert.ok(deduped.every(loginA =>
+      deduped.every(loginB => !compareAttributes(loginA, loginB, testCase.keyset))
+    ), "Every login is unique.");
+  }
+});
+
+/**
+ * Ensure that the login deduplication function keeps the most recent login.
+ */
+add_task(function test_deduplicate_keeps_most_recent() {
+  // Logins to deduplicate.
+  let logins = [
+    TestData.formLogin({timeLastUsed: Date.UTC(2004, 11, 4, 0, 0, 0)}),
+    TestData.formLogin({formSubmitURL: "http://example.com", timeLastUsed: Date.UTC(2015, 11, 4, 0, 0, 0)}),
+  ];
+
+  // Deduplicate the logins.
+  let deduped = LoginHelper.dedupeLogins(logins);
+  Assert.equal(deduped.length, 1, "Deduplicated the logins array.");
+
+  // Verify that the remaining login have the most recent date.
+  let loginTimeLastUsed = deduped[0].QueryInterface(Ci.nsILoginMetaInfo).timeLastUsed;
+  Assert.equal(loginTimeLastUsed, Date.UTC(2015, 11, 4, 0, 0, 0), "Most recent login was kept.");
+
+  // Deduplicate the reverse logins array.
+  deduped = LoginHelper.dedupeLogins(logins.reverse());
+  Assert.equal(deduped.length, 1, "Deduplicated the reversed logins array.");
+
+  // Verify that the remaining login have the most recent date.
+  loginTimeLastUsed = deduped[0].QueryInterface(Ci.nsILoginMetaInfo).timeLastUsed;
+  Assert.equal(loginTimeLastUsed, Date.UTC(2015, 11, 4, 0, 0, 0), "Most recent login was kept.");
+});
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -4650,16 +4650,28 @@
   "TELEMETRY_ARCHIVE_EVICTING_OVER_QUOTA_MS": {
     "alert_emails": ["telemetry-client-dev@mozilla.com"],
     "expires_in_version": "never",
     "kind": "exponential",
     "high": "300000",
     "n_buckets": 20,
     "description": "Time (ms) it takes for evicting over-quota pings"
   },
+  "TELEMETRY_PENDING_LOAD_FAILURE_READ": {
+    "alert_emails": ["telemetry-client-dev@mozilla.com"],
+    "expires_in_version": "never",
+    "kind": "count",
+    "description": "Number of pending Telemetry pings that failed to load from the disk"
+  },
+  "TELEMETRY_PENDING_LOAD_FAILURE_PARSE": {
+    "alert_emails": ["telemetry-client-dev@mozilla.com"],
+    "expires_in_version": "never",
+    "kind": "count",
+    "description": "Number of pending Telemetry pings that failed to parse once loaded from the disk"
+  },
   "TELEMETRY_PENDING_PINGS_SIZE_MB": {
     "alert_emails": ["telemetry-client-dev@mozilla.com"],
     "expires_in_version": "never",
     "kind": "linear",
     "high": "17",
     "n_buckets": 16,
     "description": "The size of the Telemetry pending pings directory (MB). The special value 17 is used to indicate over quota pings."
   },
@@ -4740,16 +4752,22 @@
   },
   "TELEMETRY_INVALID_PING_TYPE_SUBMITTED": {
     "alert_emails": ["telemetry-client-dev@mozilla.com"],
     "expires_in_version": "never",
     "kind": "count",
     "keyed": true,
     "description": "Count of individual invalid ping types that were submitted to Telemetry."
   },
+  "TELEMETRY_PING_EVICTED_FOR_SERVER_ERRORS": {
+    "alert_emails": ["telemetry-client-dev@mozilla.com"],
+    "expires_in_version": "never",
+    "kind": "count",
+    "description": "Number of Telemetry ping files evicted due to server errors (4XX HTTP code received)"
+  },
   "TELEMETRY_TEST_FLAG": {
     "expires_in_version": "never",
     "kind": "flag",
     "description": "a testing histogram; not meant to be touched"
   },
   "TELEMETRY_TEST_COUNT": {
     "expires_in_version": "never",
     "kind": "count",
--- a/toolkit/components/telemetry/TelemetrySend.jsm
+++ b/toolkit/components/telemetry/TelemetrySend.jsm
@@ -896,16 +896,17 @@ let TelemetrySendImpl = {
       if (statusClass === 200) {
         // We can treat all 2XX as success.
         this._log.info("_doPing - successfully loaded, status: " + status);
         success = true;
       } else if (statusClass === 400) {
         // 4XX means that something with the request was broken.
         this._log.error("_doPing - error submitting to " + url + ", status: " + status
                         + " - ping request broken?");
+        Telemetry.getHistogramById("TELEMETRY_PING_EVICTED_FOR_SERVER_ERRORS").add();
         // TODO: we should handle this better, but for now we should avoid resubmitting
         // broken requests by pretending success.
         success = true;
       } else if (statusClass === 500) {
         // 5XX means there was a server-side error and we should try again later.
         this._log.error("_doPing - error submitting to " + url + ", status: " + status
                         + " - server error, should retry later");
       } else {
--- a/toolkit/components/telemetry/TelemetryStorage.jsm
+++ b/toolkit/components/telemetry/TelemetryStorage.jsm
@@ -64,16 +64,44 @@ const ARCHIVE_SIZE_PROBE_SPECIAL_VALUE =
 
 // This special value is submitted when the pending pings is outside of the quota, as
 // we don't know the size of the pings above the quota.
 const PENDING_PINGS_SIZE_PROBE_SPECIAL_VALUE = 17;
 
 const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
 
 /**
+ * This is thrown by |TelemetryStorage.loadPingFile| when reading the ping
+ * from the disk fails.
+ */
+function PingReadError(message="Error reading the ping file") {
+  Error.call(this, message);
+  let error = new Error();
+  this.name = "PingReadError";
+  this.message = message;
+  this.stack = error.stack;
+}
+PingReadError.prototype = Object.create(Error.prototype);
+PingReadError.prototype.constructor = PingReadError;
+
+/**
+ * This is thrown by |TelemetryStorage.loadPingFile| when parsing the ping JSON
+ * content fails.
+ */
+function PingParseError(message="Error parsing ping content") {
+  Error.call(this, message);
+  let error = new Error();
+  this.name = "PingParseError";
+  this.message = message;
+  this.stack = error.stack;
+}
+PingParseError.prototype = Object.create(Error.prototype);
+PingParseError.prototype.constructor = PingParseError;
+
+/**
  * This is a policy object used to override behavior for testing.
  */
 let Policy = {
   now: () => new Date(),
   getArchiveQuota: () => ARCHIVE_QUOTA_BYTES,
   getPendingPingsQuota: () => (AppConstants.platform in ["android", "gonk"])
                                 ? PENDING_PINGS_QUOTA_BYTES_MOBILE
                                 : PENDING_PINGS_QUOTA_BYTES_DESKTOP,
@@ -1166,17 +1194,27 @@ let TelemetryStorageImpl = {
   loadPendingPing: function(id) {
     this._log.trace("loadPendingPing - id: " + id);
     let info = this._pendingPings.get(id);
     if (!info) {
       this._log.trace("loadPendingPing - unknown id " + id);
       return Promise.reject(new Error("TelemetryStorage.loadPendingPing - no ping with id " + id));
     }
 
-    return this.loadPingFile(info.path, false);
+    return this.loadPingFile(info.path, false).catch(e => {
+      // If we failed to load the ping, check what happened and update the histogram.
+      // Then propagate the rejection.
+      if (e instanceof PingReadError) {
+        Telemetry.getHistogramById("TELEMETRY_PENDING_LOAD_FAILURE_READ").add();
+      } else if (e instanceof PingParseError) {
+        Telemetry.getHistogramById("TELEMETRY_PENDING_LOAD_FAILURE_PARSE").add();
+      }
+
+      return Promise.reject(e);
+    });
   },
 
   removePendingPing: function(id) {
     let info = this._pendingPings.get(id);
     if (!info) {
       this._log.trace("removePendingPing - unknown id " + id);
       return Promise.resolve();
     }
@@ -1314,31 +1352,45 @@ let TelemetryStorageImpl = {
   },
 
   /**
    * Loads a ping file.
    * @param {String} aFilePath The path of the ping file.
    * @param {Boolean} [aCompressed=false] If |true|, expects the file to be compressed using lz4.
    * @return {Promise<Object>} A promise resolved with the ping content or rejected if the
    *                           ping contains invalid data.
+   * @throws {PingReadError} There was an error while reading the ping file from the disk.
+   * @throws {PingParseError} There was an error while parsing the JSON content of the ping file.
    */
   loadPingFile: Task.async(function* (aFilePath, aCompressed = false) {
     let options = {};
     if (aCompressed) {
       options.compression = "lz4";
     }
-    let array = yield OS.File.read(aFilePath, options);
+
+    let array;
+    try {
+      array = yield OS.File.read(aFilePath, options);
+    } catch(e) {
+      throw new PingReadError(e.message);
+    };
+
     let decoder = new TextDecoder();
     let string = decoder.decode(array);
+    let ping;
+    try {
+      ping = JSON.parse(string);
+      // The ping's payload used to be stringified JSON.  Deal with that.
+      if (typeof(ping.payload) == "string") {
+        ping.payload = JSON.parse(ping.payload);
+      }
+    } catch (e) {
+      throw new PingParseError(e.message);
+    }
 
-    let ping = JSON.parse(string);
-    // The ping's payload used to be stringified JSON.  Deal with that.
-    if (typeof(ping.payload) == "string") {
-      ping.payload = JSON.parse(ping.payload);
-    }
     return ping;
   }),
 
   /**
    * Archived pings are saved with file names of the form:
    * "<timestamp>.<uuid>.<type>.[json|jsonlz4]"
    * This helper extracts that data from a given filename.
    *
--- a/toolkit/components/telemetry/tests/unit/test_TelemetrySend.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetrySend.js
@@ -9,16 +9,18 @@
 Cu.import("resource://gre/modules/TelemetryController.jsm", this);
 Cu.import("resource://gre/modules/TelemetrySession.jsm", this);
 Cu.import("resource://gre/modules/TelemetrySend.jsm", this);
 Cu.import("resource://gre/modules/TelemetryUtils.jsm", this);
 Cu.import("resource://gre/modules/Services.jsm", this);
 Cu.import("resource://gre/modules/Preferences.jsm", this);
 Cu.import("resource://gre/modules/osfile.jsm", this);
 
+const Telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);
+
 const PREF_TELEMETRY_ENABLED = "toolkit.telemetry.enabled";
 const PREF_TELEMETRY_SERVER = "toolkit.telemetry.server";
 
 const MS_IN_A_MINUTE = 60 * 1000;
 
 function countPingTypes(pings) {
   let countByType = new Map();
   for (let p of pings) {
@@ -231,16 +233,52 @@ add_task(function* test_backoffTimeout()
   Assert.equal(countByType.get(TEST_TYPE_C), 1, "Should have received the correct amount of type C pings");
   Assert.equal(countByType.get(TEST_TYPE_D), 1, "Should have received the correct amount of type D pings");
   Assert.equal(countByType.get(TEST_TYPE_E), 1, "Should have received the correct amount of type E pings");
 
   yield TelemetrySend.testWaitOnOutgoingPings();
   Assert.equal(TelemetrySend.pendingPingCount, 0, "Should have no pending pings left");
 });
 
+add_task(function* test_evictedOnServerErrors() {
+  const TEST_TYPE = "test-evicted";
+
+  yield TelemetrySend.reset();
+
+  // Write a custom ping handler which will return 403. This will trigger ping eviction
+  // on client side.
+  PingServer.registerPingHandler((req, res) => {
+    res.setStatusLine(null, 403, "Forbidden");
+    res.processAsync();
+    res.finish();
+  });
+
+  // Clear the histogram and submit a ping.
+  Telemetry.getHistogramById("TELEMETRY_PING_EVICTED_FOR_SERVER_ERRORS").clear();
+  let pingId = yield TelemetryController.submitExternalPing(TEST_TYPE, {});
+  yield TelemetrySend.testWaitOnOutgoingPings();
+
+  let h = Telemetry.getHistogramById("TELEMETRY_PING_EVICTED_FOR_SERVER_ERRORS").snapshot();
+  Assert.equal(h.sum, 1, "Telemetry must report a ping evicted due to server errors");
+
+  // The ping should not be persisted.
+  yield Assert.rejects(TelemetryStorage.loadPendingPing(pingId), "The ping must not be persisted.");
+
+  // Reset the ping handler and submit a new ping.
+  PingServer.resetPingHandler();
+  pingId = yield TelemetryController.submitExternalPing(TEST_TYPE, {});
+
+  let ping = yield PingServer.promiseNextPings(1);
+  Assert.equal(ping[0].id, pingId, "The correct ping must be received");
+
+  // We should not have updated the error histogram.
+  h = Telemetry.getHistogramById("TELEMETRY_PING_EVICTED_FOR_SERVER_ERRORS").snapshot();
+  Assert.equal(h.sum, 1, "Telemetry must report a ping evicted due to server errors");
+});
+
 // Test that the current, non-persisted pending pings are properly saved on shutdown.
 add_task(function* test_persistCurrentPingsOnShutdown() {
   const TEST_TYPE = "test-persistCurrentPingsOnShutdown";
   const PING_COUNT = 5;
   yield TelemetrySend.reset();
   PingServer.stop();
   Assert.equal(TelemetrySend.pendingPingCount, 0, "Should have no pending pings yet");
 
--- a/toolkit/components/telemetry/tests/unit/test_TelemetrySendOldPings.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetrySendOldPings.js
@@ -268,16 +268,64 @@ add_task(function* test_overdue_old_form
 
   // |TelemetryStorage.cleanup| doesn't know how to remove a ping with no slug or id,
   // so remove it manually so that the next test doesn't fail.
   yield OS.File.remove(PING_FILES_PATHS[3]);
 
   yield clearPendingPings();
 });
 
+add_task(function* test_corrupted_pending_pings() {
+  const TEST_TYPE = "test_corrupted";
+
+  Telemetry.getHistogramById("TELEMETRY_PENDING_LOAD_FAILURE_READ").clear();
+  Telemetry.getHistogramById("TELEMETRY_PENDING_LOAD_FAILURE_PARSE").clear();
+
+  // Save a pending ping and get its id.
+  let pendingPingId = yield TelemetryController.addPendingPing(TEST_TYPE, {}, {});
+
+  // Try to load it: there should be no error.
+  yield TelemetryStorage.loadPendingPing(pendingPingId);
+
+  let h = Telemetry.getHistogramById("TELEMETRY_PENDING_LOAD_FAILURE_READ").snapshot();
+  Assert.equal(h.sum, 0, "Telemetry must not report a pending ping load failure");
+  h = Telemetry.getHistogramById("TELEMETRY_PENDING_LOAD_FAILURE_PARSE").snapshot();
+  Assert.equal(h.sum, 0, "Telemetry must not report a pending ping parse failure");
+
+  // Delete it from the disk, so that its id will be kept in the cache but it will
+  // fail loading the file.
+  yield OS.File.remove(getSavePathForPingId(pendingPingId));
+
+  // Try to load a pending ping which isn't there anymore.
+  yield Assert.rejects(TelemetryStorage.loadPendingPing(pendingPingId),
+                       "Telemetry must fail loading a ping which isn't there");
+
+  h = Telemetry.getHistogramById("TELEMETRY_PENDING_LOAD_FAILURE_READ").snapshot();
+  Assert.equal(h.sum, 1, "Telemetry must report a pending ping load failure");
+  h = Telemetry.getHistogramById("TELEMETRY_PENDING_LOAD_FAILURE_PARSE").snapshot();
+  Assert.equal(h.sum, 0, "Telemetry must not report a pending ping parse failure");
+
+  // Save a new ping, so that it gets in the pending pings cache.
+  pendingPingId = yield TelemetryController.addPendingPing(TEST_TYPE, {}, {});
+  // Overwrite it with a corrupted JSON file and then try to load it.
+  const INVALID_JSON = "{ invalid,JSON { {1}";
+  yield OS.File.writeAtomic(getSavePathForPingId(pendingPingId), INVALID_JSON, { encoding: "utf-8" });
+
+  // Try to load the ping with the corrupted JSON content.
+  yield Assert.rejects(TelemetryStorage.loadPendingPing(pendingPingId),
+                       "Telemetry must fail loading a corrupted ping");
+
+  h = Telemetry.getHistogramById("TELEMETRY_PENDING_LOAD_FAILURE_READ").snapshot();
+  Assert.equal(h.sum, 1, "Telemetry must report a pending ping load failure");
+  h = Telemetry.getHistogramById("TELEMETRY_PENDING_LOAD_FAILURE_PARSE").snapshot();
+  Assert.equal(h.sum, 1, "Telemetry must report a pending ping parse failure");
+
+  yield clearPendingPings();
+});
+
 /**
  * Create some recent and overdue pings and verify that they get sent.
  */
 add_task(function* test_overdue_pings_trigger_send() {
   let pingTypes = [
     { num: RECENT_PINGS },
     { num: OVERDUE_PINGS, age: OVERDUE_PING_FILE_AGE },
   ];
--- a/toolkit/devtools/server/actors/script.js
+++ b/toolkit/devtools/server/actors/script.js
@@ -13,17 +13,17 @@ const { ObjectActor, createValueGrip, lo
 const { DebuggerServer } = require("devtools/server/main");
 const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 const { dbg_assert, dumpn, update, fetch } = DevToolsUtils;
 const { dirname, joinURI } = require("devtools/toolkit/path");
 const promise = require("promise");
 const PromiseDebugging = require("PromiseDebugging");
 const xpcInspector = require("xpcInspector");
 const ScriptStore = require("./utils/ScriptStore");
-const {DevToolsWorker} = require("devtools/toolkit/shared/worker.js");
+const { DevToolsWorker } = require("devtools/toolkit/shared/worker.js");
 
 const { defer, resolve, reject, all } = require("devtools/toolkit/deprecated-sync-thenables");
 
 loader.lazyGetter(this, "Debugger", () => {
   let Debugger = require("Debugger");
   hackDebugger(Debugger);
   return Debugger;
 });
--- a/toolkit/themes/shared/in-content/common.inc.css
+++ b/toolkit/themes/shared/in-content/common.inc.css
@@ -176,17 +176,16 @@ html|button {
 /* xul buttons and menulists */
 
 *|button,
 xul|colorpicker[type="button"],
 xul|menulist {
   -moz-appearance: none;
   height: 30px;
   color: var(--in-content-text-color);
-  line-height: 20px;
   border: 1px solid var(--in-content-box-border-color);
   -moz-border-top-colors: none !important;
   -moz-border-right-colors: none !important;
   -moz-border-bottom-colors: none !important;
   -moz-border-left-colors: none !important;
   border-radius: 2px;
   background-color: var(--in-content-page-background);
 }