Merge m-c to fx-team. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Sun, 20 Jul 2014 15:15:04 -0400
changeset 215885 646e3fbf9826bc74f63bc6d0b4db1358305bf43f
parent 215884 36cf96084e123e1e20f9045b94cb51339cfeba69 (current diff)
parent 215849 56bec38169671937b4b0b77f09caf9da4dbb360b (diff)
child 215888 e9cdcf646d1ca5b0cde1baf4ba5e64df58283030
push id3857
push userraliiev@mozilla.com
push dateTue, 02 Sep 2014 16:39:23 +0000
treeherdermozilla-beta@5638b907b505 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone33.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to fx-team. a=merge
--- 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="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <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="d839242c2a4a6a445cbd8bc2443acbdf712a19fc"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="d4583df788d33119f1fc4c68dca40a986a9bf18b"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="227354333a185180b85471f2cc6abfb029e44718"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8b04b5aca4b0a894de40f4d53ae9750222d349a8"/>
   <!-- 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="cc67f31dc638c0b7edba3cf7e3d87cadf0ed52bf">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="d839242c2a4a6a445cbd8bc2443acbdf712a19fc"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="d4583df788d33119f1fc4c68dca40a986a9bf18b"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8b04b5aca4b0a894de40f4d53ae9750222d349a8"/>
   <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="276ce45e78b09c4a4ee643646f691d22804754c1">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="d839242c2a4a6a445cbd8bc2443acbdf712a19fc"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="d4583df788d33119f1fc4c68dca40a986a9bf18b"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8b04b5aca4b0a894de40f4d53ae9750222d349a8"/>
--- 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="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <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="d839242c2a4a6a445cbd8bc2443acbdf712a19fc"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="d4583df788d33119f1fc4c68dca40a986a9bf18b"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="227354333a185180b85471f2cc6abfb029e44718"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8b04b5aca4b0a894de40f4d53ae9750222d349a8"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="cc67f31dc638c0b7edba3cf7e3d87cadf0ed52bf">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="d839242c2a4a6a445cbd8bc2443acbdf712a19fc"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="d4583df788d33119f1fc4c68dca40a986a9bf18b"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8b04b5aca4b0a894de40f4d53ae9750222d349a8"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="e95b4ce22c825da44d14299e1190ea39a5260bde"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/>
@@ -125,23 +125,23 @@
   <project name="kernel_lk" path="bootable/bootloader/lk" remote="b2g" revision="2b1d8b5b7a760230f4c94c02e733e3929f44253a"/>
   <project name="platform_bootable_recovery" path="bootable/recovery" remote="b2g" revision="e81502511cda303c803e63f049574634bc96f9f2"/>
   <project name="platform/external/bluetooth/bluedroid" path="external/bluetooth/bluedroid" revision="81c4a859d75d413ad688067829d21b7ba9205f81"/>
   <project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="f0689ac1914cdbc59e53bdc9edd9013dc157c299"/>
   <project name="platform/external/bluetooth/glib" path="external/bluetooth/glib" revision="dd925f76e4f149c3d5571b80e12f7e24bbe89c59"/>
   <project name="platform/external/dbus" path="external/dbus" revision="ea87119c843116340f5df1d94eaf8275e1055ae8"/>
   <project name="platform_external_libnfc-nci" path="external/libnfc-nci" remote="t2m" revision="4186bdecb4dae911b39a8202252cc2310d91b0be"/>
   <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="320b05a5761eb2a4816f7529c91ea49422979b55"/>
-  <project name="platform/frameworks/av" path="frameworks/av" revision="5b934dc57dae25f286b0e7210dc6ff47f3244927"/>
-  <project name="platform/frameworks/base" path="frameworks/base" revision="af3e4fbdc9369643a92015ea2657361f3b1b46fe"/>
-  <project name="platform/frameworks/native" path="frameworks/native" revision="33a2b51f78416536e1bfba0c0b7776db307f3a4f"/>
+  <project name="platform/frameworks/av" path="frameworks/av" revision="0f7829661cd7125de9dc2c90eca2fa1dbc68dfbf"/>
+  <project name="platform/frameworks/base" path="frameworks/base" revision="228d9f4189c42a11a65946420e836faa54073291"/>
+  <project name="platform/frameworks/native" path="frameworks/native" revision="be9db09a9889fa0dcf6539f39598c11fb2a3be44"/>
   <project name="platform/hardware/libhardware" path="hardware/libhardware" revision="484802559ed106bac4811bd01c024ca64f741e60"/>
-  <project name="platform/hardware/qcom/audio" path="hardware/qcom/audio" revision="83f363a26069e9c188d2aaef5b9ef63e84ad1511"/>
+  <project name="platform/hardware/qcom/audio" path="hardware/qcom/audio" revision="de4ade568b273781416638fbbce13ff31b636ada"/>
   <project name="platform/hardware/qcom/camera" path="hardware/qcom/camera" revision="5e110615212302c5d798a3c223dcee458817651c"/>
-  <project name="platform/hardware/qcom/display" path="hardware/qcom/display" revision="280d29203b2aa30d713c5a6cc63d626e5a7df822"/>
+  <project name="platform/hardware/qcom/display" path="hardware/qcom/display" revision="fa9ffd47948eb24466de227e48fe9c4a7c5e7711"/>
   <project name="platform/hardware/qcom/gps" path="hardware/qcom/gps" revision="5dc48bd46f9589653f8bf297be5d73676f2e2867"/>
   <project name="platform/hardware/qcom/media" path="hardware/qcom/media" revision="8a0d0b0d9889ef99c4c6317c810db4c09295f15a"/>
   <project name="platform/hardware/qcom/wlan" path="hardware/qcom/wlan" revision="2208fa3537ace873b8f9ec2355055761c79dfd5f"/>
   <project name="platform/hardware/ril" path="hardware/ril" revision="c4e2ac95907a5519a0e09f01a0d8e27fec101af0"/>
   <project name="platform/system/bluetooth" path="system/bluetooth" revision="e1eb226fa3ad3874ea7b63c56a9dc7012d7ff3c2"/>
   <project name="platform/system/core" path="system/core" revision="bbf7212289fc8311e43f9d11e10788e310d36a08"/>
   <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="2cd3c7277621902b3749797eaaab618c4c80a58b"/>
   <project name="platform/system/qcom" path="system/qcom" revision="1cdab258b15258b7f9657da70e6f06ebd5a2fc25"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "git_revision": "", 
         "remote": "", 
         "branch": ""
     }, 
-    "revision": "4af9ed6ff7ff454659aa3b443b466dc1527c607f", 
+    "revision": "f96a2a006cb9a1d304a171f642718f3c3a644dad", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <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="d839242c2a4a6a445cbd8bc2443acbdf712a19fc"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="d4583df788d33119f1fc4c68dca40a986a9bf18b"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8b04b5aca4b0a894de40f4d53ae9750222d349a8"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <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="d839242c2a4a6a445cbd8bc2443acbdf712a19fc"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="d4583df788d33119f1fc4c68dca40a986a9bf18b"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="cc67f31dc638c0b7edba3cf7e3d87cadf0ed52bf">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="d839242c2a4a6a445cbd8bc2443acbdf712a19fc"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="d4583df788d33119f1fc4c68dca40a986a9bf18b"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8b04b5aca4b0a894de40f4d53ae9750222d349a8"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <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="d839242c2a4a6a445cbd8bc2443acbdf712a19fc"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="d4583df788d33119f1fc4c68dca40a986a9bf18b"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8b04b5aca4b0a894de40f4d53ae9750222d349a8"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -1,10 +1,10 @@
 <?xml version="1.0"?>
-<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1405034316000">
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1405723700000">
   <emItems>
       <emItem  blockID="i454" id="sqlmoz@facebook.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                                                                 <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                                                     <prefs>
               </prefs>
@@ -257,16 +257,20 @@
       <emItem  blockID="i630" id="webbooster@iminent.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                                                     <prefs>
                   <pref>browser.startup.homepage</pref>
                   <pref>browser.search.defaultenginename</pref>
               </prefs>
     </emItem>
+      <emItem  blockID="i7" id="{2224e955-00e9-4613-a844-ce69fccaae91}">
+                                                          <prefs>
+              </prefs>
+    </emItem>
       <emItem  blockID="i174" id="info@thebflix.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                                                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i478" id="{7e8a1050-cf67-4575-92df-dcc60e7d952d}">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
@@ -275,20 +279,21 @@
               </prefs>
     </emItem>
       <emItem  blockID="i582" id="discoverypro@discoverypro.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                                                     <prefs>
               </prefs>
     </emItem>
-      <emItem  blockID="i172" id="info@bflix.info">
-                        <versionRange  minVersion="0" maxVersion="*" severity="3">
+      <emItem  blockID="i670" id="/^({ad9a41d2-9a49-4fa6-a79e-71a0785364c8})|(ffxtlbr@mysearchdial\.com)$/">
+                        <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                                                     <prefs>
+                  <pref>browser.search.defaultenginename</pref>
               </prefs>
     </emItem>
       <emItem  blockID="i93" id="{68b8676b-99a5-46d1-b390-22411d8bcd61}">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                                                     <prefs>
               </prefs>
     </emItem>
@@ -777,16 +782,22 @@
               </prefs>
     </emItem>
       <emItem  blockID="i370" id="happylyrics@hpyproductions.net">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                                                     <prefs>
               </prefs>
     </emItem>
+      <emItem  blockID="i668" id="/^(matchersite(pro(src)?)?\@matchersite(pro(src)?)?\.com)|((pro)?sitematcher(_src|pro|site|sitesrc)?\@(pro)?sitematcher(_src|pro|site|sitesrc)?\.com)$/">
+                        <versionRange  minVersion="0" maxVersion="*" severity="1">
+                    </versionRange>
+                                                    <prefs>
+              </prefs>
+    </emItem>
       <emItem  blockID="i440" id="{2d069a16-fca1-4e81-81ea-5d5086dcbd0c}">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                                                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i396" id="/@(ft|putlocker|clickmovie|m2k|sharerepo|smarter-?)downloader\.com$/">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
@@ -901,18 +912,20 @@
               </prefs>
     </emItem>
       <emItem  blockID="i507" id="4zffxtbr-bs@VideoDownloadConverter_4z.com">
                         <versionRange  minVersion="0" maxVersion="5.75.3.25126" severity="1">
                     </versionRange>
                                                     <prefs>
               </prefs>
     </emItem>
-      <emItem  blockID="i7" id="{2224e955-00e9-4613-a844-ce69fccaae91}">
-                                                          <prefs>
+      <emItem  blockID="i172" id="info@bflix.info">
+                        <versionRange  minVersion="0" maxVersion="*" severity="3">
+                    </versionRange>
+                                                    <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i525" id="/^({65f9f6b7-2dae-46fc-bfaf-f88e4af1beca}|{9ed31f84-c8b3-4926-b950-dff74047ff79}|{0134af61-7a0c-4649-aeca-90d776060cb3}|{02edb56b-9b33-435b-b7df-b2843273a694}|{da51d4f6-3e7e-4ef8-b400-9198e0874606}|{b24577db-155e-4077-bb37-3fdd3c302bb5})$/">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                                                     <prefs>
               </prefs>
     </emItem>
@@ -1902,17 +1915,18 @@
                   <match name="filename" exp="AdobePDFViewerNPAPI\.plugin" />                      <versionRange  minVersion="0" maxVersion="10.1.3" severity="1"></versionRange>
                         </pluginItem>
       <pluginItem  blockID="p94">
                   <match name="filename" exp="Flash\ Player\.plugin" />                                    <versionRange  minVersion="0" maxVersion="10.2.159.1" severity="0">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="0.1" maxVersion="17.0.1" />
                           </targetApplication>
                   </versionRange>
-                        </pluginItem>
+                            <infoURL>https://get.adobe.com/flashplayer/</infoURL>
+          </pluginItem>
       <pluginItem  blockID="p102">
                   <match name="filename" exp="npmozax\.dll" />                      <versionRange  minVersion="0" maxVersion="*"></versionRange>
                         </pluginItem>
       <pluginItem  blockID="p113">
                   <match name="filename" exp="npuplaypc\.dll" />                      <versionRange  minVersion="0" maxVersion="1.0.0.0" severity="1"></versionRange>
                         </pluginItem>
       <pluginItem  blockID="p119">
       <match name="name" exp="Java\(TM\) Plug-in 1\.(6\.0_(\d|[0-2]\d?|3[0-2])|7\.0(_0?([1-4]))?)([^\d\._]|$)" />            <match name="filename" exp="libnpjp2\.so" />                                    <versionRange  severity="1">
@@ -1972,45 +1986,50 @@
                   <match name="filename" exp="nppdf32\.dll" />                      <versionRange  minVersion="10.0" maxVersion="10.1.5.9999" severity="0" vulnerabilitystatus="1"></versionRange>
                         </pluginItem>
       <pluginItem  blockID="p160">
                   <match name="filename" exp="NPSWF32\.dll" />                                    <versionRange  minVersion="0" maxVersion="10.2.9999" severity="0" vulnerabilitystatus="1">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="4.0" maxVersion="16.*" />
                           </targetApplication>
                   </versionRange>
-                        </pluginItem>
+                            <infoURL>https://get.adobe.com/flashplayer/</infoURL>
+          </pluginItem>
       <pluginItem  blockID="p176">
                   <match name="filename" exp="(NPSWF32\.dll)|(Flash\ Player\.plugin)" />                                    <versionRange  minVersion="10.3" maxVersion="10.3.183.18.999" severity="0" vulnerabilitystatus="1">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="19.0a1" maxVersion="*" />
                           </targetApplication>
                   </versionRange>
-                        </pluginItem>
+                            <infoURL>https://get.adobe.com/flashplayer/</infoURL>
+          </pluginItem>
       <pluginItem  blockID="p176">
                   <match name="filename" exp="(NPSWF32\.dll)|(Flash\ Player\.plugin)" />                                    <versionRange  minVersion="10.3" maxVersion="10.3.183.18.999" severity="0" vulnerabilitystatus="1">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="17.0.4" maxVersion="17.0.*" />
                           </targetApplication>
                   </versionRange>
-                        </pluginItem>
+                            <infoURL>https://get.adobe.com/flashplayer/</infoURL>
+          </pluginItem>
       <pluginItem  blockID="p178">
                   <match name="filename" exp="(NPSWF[0-9_]*\.dll)|(Flash\ Player\.plugin)" />                                    <versionRange  minVersion="11.0" maxVersion="11.7.700.169" severity="0" vulnerabilitystatus="1">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="19.0a1" maxVersion="*" />
                           </targetApplication>
                   </versionRange>
-                        </pluginItem>
+                            <infoURL>https://get.adobe.com/flashplayer/</infoURL>
+          </pluginItem>
       <pluginItem  blockID="p178">
                   <match name="filename" exp="(NPSWF[0-9_]*\.dll)|(Flash\ Player\.plugin)" />                                    <versionRange  minVersion="11.0" maxVersion="11.7.700.169" severity="0" vulnerabilitystatus="1">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="17.0.4" maxVersion="17.0.*" />
                           </targetApplication>
                   </versionRange>
-                        </pluginItem>
+                            <infoURL>https://get.adobe.com/flashplayer/</infoURL>
+          </pluginItem>
       <pluginItem  blockID="p180">
                   <match name="filename" exp="JavaAppletPlugin\.plugin" />                                    <versionRange  minVersion="Java 7 Update 0" maxVersion="Java 7 Update 11" severity="0" vulnerabilitystatus="1">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="17.0" maxVersion="*" />
                           </targetApplication>
                   </versionRange>
                         </pluginItem>
       <pluginItem  blockID="p182">
@@ -2096,38 +2115,42 @@
                   </versionRange>
                         </pluginItem>
       <pluginItem  blockID="p260">
                   <match name="filename" exp="(NPSWF32\.dll)|(Flash\ Player\.plugin)" />                                    <versionRange  minVersion="0" maxVersion="10.2.9999" severity="0" vulnerabilitystatus="1">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="18.0a1" maxVersion="*" />
                           </targetApplication>
                   </versionRange>
-                        </pluginItem>
+                            <infoURL>https://get.adobe.com/flashplayer/</infoURL>
+          </pluginItem>
       <pluginItem  blockID="p260">
                   <match name="filename" exp="(NPSWF32\.dll)|(Flash\ Player\.plugin)" />                                    <versionRange  minVersion="0" maxVersion="10.2.9999" severity="0" vulnerabilitystatus="1">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="17.0.4" maxVersion="17.0.*" />
                           </targetApplication>
                   </versionRange>
-                        </pluginItem>
+                            <infoURL>https://get.adobe.com/flashplayer/</infoURL>
+          </pluginItem>
       <pluginItem  blockID="p290">
                   <match name="filename" exp="(NPSWF32\.dll)|(Flash\ Player\.plugin)" />                                    <versionRange  minVersion="10.3.183.19" maxVersion="10.3.183.66" severity="0" vulnerabilitystatus="1">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="19.0a1" maxVersion="*" />
                           </targetApplication>
                   </versionRange>
-                        </pluginItem>
+                            <infoURL>https://get.adobe.com/flashplayer/</infoURL>
+          </pluginItem>
       <pluginItem  blockID="p290">
                   <match name="filename" exp="(NPSWF32\.dll)|(Flash\ Player\.plugin)" />                                    <versionRange  minVersion="10.3.183.19" maxVersion="10.3.183.66" severity="0" vulnerabilitystatus="1">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="17.0.4" maxVersion="17.0.*" />
                           </targetApplication>
                   </versionRange>
-                        </pluginItem>
+                            <infoURL>https://get.adobe.com/flashplayer/</infoURL>
+          </pluginItem>
       <pluginItem  blockID="p292">
                   <match name="filename" exp="JavaAppletPlugin\.plugin" />                                    <versionRange  minVersion="Java 7 Update 12" maxVersion="Java 7 Update 15" severity="0" vulnerabilitystatus="1">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="17.0" maxVersion="*" />
                           </targetApplication>
                   </versionRange>
                         </pluginItem>
       <pluginItem  blockID="p294">
@@ -2180,38 +2203,42 @@
                   </versionRange>
                         </pluginItem>
       <pluginItem  blockID="p330">
             <match name="description" exp="^Shockwave Flash (([1-9]\.[0-9]+)|(10\.([0-2]|(3 r(([0-9][0-9]?)|1(([0-7][0-9])|8[0-2]))))))( |$)" />      <match name="filename" exp="libflashplayer\.so" />                                    <versionRange  severity="0" vulnerabilitystatus="1">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="19.0a1" maxVersion="*" />
                           </targetApplication>
                   </versionRange>
-                        </pluginItem>
+                            <infoURL>https://get.adobe.com/flashplayer/</infoURL>
+          </pluginItem>
       <pluginItem  blockID="p330">
             <match name="description" exp="^Shockwave Flash (([1-9]\.[0-9]+)|(10\.([0-2]|(3 r(([0-9][0-9]?)|1(([0-7][0-9])|8[0-2]))))))( |$)" />      <match name="filename" exp="libflashplayer\.so" />                                    <versionRange  severity="0" vulnerabilitystatus="1">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="17.0.4" maxVersion="17.0.*" />
                           </targetApplication>
                   </versionRange>
-                        </pluginItem>
+                            <infoURL>https://get.adobe.com/flashplayer/</infoURL>
+          </pluginItem>
       <pluginItem  blockID="p332">
             <match name="description" exp="^Shockwave Flash 11.(0|1) r[0-9]{1,3}$" />      <match name="filename" exp="libflashplayer\.so" />                                    <versionRange  severity="0" vulnerabilitystatus="1">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="19.0a1" maxVersion="*" />
                           </targetApplication>
                   </versionRange>
-                        </pluginItem>
+                            <infoURL>https://get.adobe.com/flashplayer/</infoURL>
+          </pluginItem>
       <pluginItem  blockID="p332">
             <match name="description" exp="^Shockwave Flash 11.(0|1) r[0-9]{1,3}$" />      <match name="filename" exp="libflashplayer\.so" />                                    <versionRange  severity="0" vulnerabilitystatus="1">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="17.0.4" maxVersion="17.0.*" />
                           </targetApplication>
                   </versionRange>
-                        </pluginItem>
+                            <infoURL>https://get.adobe.com/flashplayer/</infoURL>
+          </pluginItem>
       <pluginItem  blockID="p366">
                   <match name="filename" exp="Scorch\.plugin" />                      <versionRange  minVersion="6.2.0" maxVersion="6.2.0" severity="1"></versionRange>
                         </pluginItem>
       <pluginItem  blockID="p408">
                   <match name="filename" exp="QuickTime Plugin\.plugin" />                      <versionRange  minVersion="0" maxVersion="7.6.5" severity="0" vulnerabilitystatus="1"></versionRange>
                         </pluginItem>
       <pluginItem  blockID="p410">
                   <match name="filename" exp="npqtplugin\.dll" />                      <versionRange  minVersion="0" maxVersion="7.7.3" severity="0" vulnerabilitystatus="1"></versionRange>
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -134,16 +134,18 @@ pref("app.update.cert.maxErrors", 5);
 //    the value for the name must be the same as the value for the attribute name
 //    on the certificate.
 // If these conditions aren't met it will be treated the same as when there is
 // no update available. This validation will not be performed when the
 // |app.update.url.override| user preference has been set for testing updates or
 // when the |app.update.cert.checkAttributes| preference is set to false. Also,
 // the |app.update.url.override| preference should ONLY be used for testing.
 // IMPORTANT! metro.js should also be updated for updates to certs.X.issuerName
+// IMPORTANT! media.gmp-manager.certs.* prefs should also be updated if these
+// are updated.
 
 // Non-release builds (Nightly, Aurora, etc.) have been switched over to aus4.mozilla.org.
 // This condition protects us against accidentally using it for release builds.
 #ifndef RELEASE_BUILD
 pref("app.update.certs.1.issuerName", "CN=DigiCert Secure Server CA,O=DigiCert Inc,C=US");
 pref("app.update.certs.1.commonName", "aus4.mozilla.org");
 
 pref("app.update.certs.2.issuerName", "CN=Thawte SSL CA,O=\"Thawte, Inc.\",C=US");
@@ -1585,16 +1587,55 @@ pref("identity.fxaccounts.settings.uri",
 // On GTK, we now default to showing the menubar only when alt is pressed:
 #ifdef MOZ_WIDGET_GTK
 pref("ui.key.menuAccessKeyFocuses", true);
 #endif
 
 // Encrypted media extensions.
 pref("media.eme.enabled", false);
 
+// GMPInstallManager prefs
+
+// Enables some extra logging (can reduce performance)
+pref("media.gmp-manager.log", false);
+
+// User-settable override to media.gmp-manager.url for testing purposes.
+//pref("media.gmp-manager.url.override", "");
+
+// Update service URL for GMP install/updates:
+pref("media.gmp-manager.url", "https://aus4.mozilla.org/update/3/GMP/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml");
+
+// When |media.gmp-manager.cert.requireBuiltIn| is true or not specified the
+// final certificate and all certificates the connection is redirected to before
+// the final certificate for the url specified in the |media.gmp-manager.url|
+// preference must be built-in.
+pref("media.gmp-manager.cert.requireBuiltIn", true);
+
+// The |media.gmp-manager.certs.| preference branch contains branches that are
+// sequentially numbered starting at 1 that contain attribute name / value
+// pairs for the certificate used by the server that hosts the update xml file
+// as specified in the |media.gmp-manager.url| preference. When these preferences are
+// present the following conditions apply for a successful update check:
+// 1. the uri scheme must be https
+// 2. the preference name must exist as an attribute name on the certificate and
+//    the value for the name must be the same as the value for the attribute name
+//    on the certificate.
+// If these conditions aren't met it will be treated the same as when there is
+// no update available. This validation will not be performed when the
+// |media.gmp-manager.url.override| user preference has been set for testing updates or
+// when the |media.gmp-manager.cert.checkAttributes| preference is set to false. Also,
+// the |media.gmp-manager.url.override| preference should ONLY be used for testing.
+// IMPORTANT! app.update.certs.* prefs should also be updated if these
+// are updated.
+pref("media.gmp-manager.cert.checkAttributes", true);
+pref("media.gmp-manager.certs.1.issuerName", "CN=DigiCert Secure Server CA,O=DigiCert Inc,C=US");
+pref("media.gmp-manager.certs.1.commonName", "aus4.mozilla.org");
+pref("media.gmp-manager.certs.2.issuerName", "CN=Thawte SSL CA,O=\"Thawte, Inc.\",C=US");
+pref("media.gmp-manager.certs.2.commonName", "aus4.mozilla.org");
+
 // Delete HTTP cache v2 data of users that didn't opt-in manually
 pref("browser.cache.auto_delete_cache_version", 1);
 // Play with different values of the decay time and get telemetry,
 // 0 means to randomize (and persist) the experiment value in users' profiles,
 // -1 means no experiment is run and we use the preferred value for frecency (6h)
 pref("browser.cache.frecency_experiment", 0);
 
 pref("browser.translation.detectLanguage", false);
@@ -1607,9 +1648,9 @@ pref("experiments.enabled", true);
 pref("experiments.manifest.fetchIntervalSeconds", 86400);
 pref("experiments.manifest.uri", "https://telemetry-experiment.cdn.mozilla.net/manifest/v1/firefox/%VERSION%/%CHANNEL%");
 pref("experiments.manifest.certs.1.commonName", "*.cdn.mozilla.net");
 pref("experiments.manifest.certs.1.issuerName", "CN=Cybertrust Public SureServer SV CA,O=Cybertrust Inc");
 // Whether experiments are supported by the current application profile.
 pref("experiments.supported", true);
 
 // Enable the OpenH264 plugin support in the addon manager.
-pref("media.openh264.providerEnabled", true);
+pref("media.gmp-gmpopenh264.provider.enabled", true);
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -16,16 +16,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
                                   "resource://gre/modules/BrowserUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
                                   "resource://gre/modules/Task.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "CharsetMenu",
                                   "resource://gre/modules/CharsetMenu.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ShortcutUtils",
                                   "resource://gre/modules/ShortcutUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "GMPInstallManager",
+                                  "resource://gre/modules/GMPInstallManager.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gDNSService",
                                    "@mozilla.org/network/dns-service;1",
                                    "nsIDNSService");
 
 const nsIWebNavigation = Ci.nsIWebNavigation;
 
 var gLastBrowserCharset = null;
@@ -1311,16 +1313,24 @@ var gBrowserInit = {
       Cu.reportError("Could not end startup crash tracking: " + ex);
     }
 
     if (typeof WindowsPrefSync !== 'undefined') {
       // Pulls in Metro controlled prefs and pushes out Desktop controlled prefs
       WindowsPrefSync.init();
     }
 
+    // Delay this a minute because there's no rush
+    setTimeout(() => {
+      this.gmpInstallManager = new GMPInstallManager();
+      // We don't really care about the results, if somenoe is interested they
+      // can check the log.
+      this.gmpInstallManager.simpleCheckAndInstall();
+    }, 1000 * 60);
+
     SessionStore.promiseInitialized.then(() => {
       // Bail out if the window has been closed in the meantime.
       if (window.closed) {
         return;
       }
 
       // Enable the Restore Last Session command if needed
       RestoreLastSessionObserver.init();
@@ -1460,16 +1470,19 @@ var gBrowserInit = {
         gPrefService.removeObserver(gHomeButton.prefDomain, gHomeButton);
       } catch (ex) {
         Cu.reportError(ex);
       }
 
       if (typeof WindowsPrefSync !== 'undefined') {
         WindowsPrefSync.uninit();
       }
+      if (this.gmpInstallManager) {
+        this.gmpInstallManager.uninit();
+      }
 
       BrowserOffline.uninit();
       OfflineApps.uninit();
       IndexedDBPromptHelper.uninit();
       LightweightThemeListener.uninit();
       PanelUI.uninit();
     }
 
--- a/content/media/gmp/GMPChild.cpp
+++ b/content/media/gmp/GMPChild.cpp
@@ -42,19 +42,22 @@ GMPChild::~GMPChild()
 }
 
 bool
 GMPChild::Init(const std::string& aPluginPath,
                base::ProcessHandle aParentProcessHandle,
                MessageLoop* aIOLoop,
                IPC::Channel* aChannel)
 {
+#ifdef GMP_CRASHREPORTER_READY
+// See bug 1041226
 #ifdef MOZ_CRASHREPORTER
   SendPCrashReporterConstructor(CrashReporter::CurrentThreadId());
 #endif
+#endif
 #if defined(XP_WIN)
   mozilla::SandboxTarget::Instance()->StartSandbox();
 #endif
   return LoadPluginLibrary(aPluginPath) &&
          Open(aChannel, aParentProcessHandle, aIOLoop);
 }
 
 bool
--- a/dom/contacts/ContactManager.js
+++ b/dom/contacts/ContactManager.js
@@ -243,39 +243,53 @@ ContactManager.prototype = {
       }
 
     // Shortcut for ALLOW_ACTION so we avoid a parent roundtrip
     let principal = this._window.document.nodePrincipal;
     let type = "contacts-" + access;
     let permValue =
       Services.perms.testExactPermissionFromPrincipal(principal, type);
     if (permValue == Ci.nsIPermissionManager.ALLOW_ACTION) {
-      aAllowCallback();
+      if (aAllowCallback) {
+        aAllowCallback();
+      }
       return;
-    } else if (permValue == Ci.nsIPermissionManager.DENY_ACTION) {
-      aCancelCallback();
+    } else if (permValue == Ci.nsIPermissionManager.DENY_ACTION ||
+               permValue == Ci.nsIPermissionManager.UNKNOWN_ACTION) {
+      if (aCancelCallback) {
+        aCancelCallback();
+      }
+      return;
     }
 
     // Create an array with a single nsIContentPermissionType element.
     let type = {
       type: "contacts",
       access: access,
-      options: null,
+      options: [],
       QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionType])
     };
     let typeArray = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
     typeArray.appendElement(type, false);
 
     // create a nsIContentPermissionRequest
     let request = {
       types: typeArray,
       principal: principal,
       QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionRequest]),
-      allow: aAllowCallback,
-      cancel: aCancelCallback,
+      allow: aAllowCallback ||
+             function() {
+               if (DEBUG)
+                 debug("Default allow contacts callback. " + access +"\n");
+             },
+      cancel: aCancelCallback ||
+              function() {
+                if (DEBUG)
+                  debug("Default cancel contacts callback. " + access +"\n");
+              },
       window: this._window
     };
 
     // Using askPermission from nsIDOMWindowUtils that takes care of the
     // remoting if needed.
     let windowUtils = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
                           .getInterface(Ci.nsIDOMWindowUtils);
     windowUtils.askPermission(request);
@@ -420,34 +434,34 @@ ContactManager.prototype = {
 
     let allowCallback = function() {
       cpmm.sendAsyncMessage("Contacts:GetRevision", {
         requestID: this.getRequestId({ request: request })
       });
     }.bind(this);
 
     let cancelCallback = function() {
-      Services.DOMRequest.fireError(request);
+      Services.DOMRequest.fireError(request, "");
     };
 
     this.askPermission("revision", request, allowCallback, cancelCallback);
     return request;
   },
 
   getCount: function() {
     let request = this.createRequest();
 
     let allowCallback = function() {
       cpmm.sendAsyncMessage("Contacts:GetCount", {
         requestID: this.getRequestId({ request: request })
       });
     }.bind(this);
 
     let cancelCallback = function() {
-      Services.DOMRequest.fireError(request);
+      Services.DOMRequest.fireError(request, "");
     };
 
     this.askPermission("count", request, allowCallback, cancelCallback);
     return request;
   },
 
   init: function(aWindow) {
     // DOMRequestIpcHelper.initHelper sets this._window
--- a/dom/contacts/tests/mochitest.ini
+++ b/dom/contacts/tests/mochitest.ini
@@ -1,20 +1,23 @@
 [DEFAULT]
 skip-if = e10s
 
 [shared.js]
 [test_contacts_basics.html]
 skip-if = (toolkit == 'gonk' && debug) #debug-only failure
 [test_contacts_basics2.html]
+skip-if = (toolkit == 'gonk' && debug) #debug-only failure
 [test_contacts_blobs.html]
+skip-if = (toolkit == 'gonk' && debug) #debug-only failure
 [test_contacts_events.html]
 [test_contacts_getall.html]
 skip-if = (toolkit == 'gonk' && debug) #debug-only failure
 [test_contacts_getall2.html]
+skip-if = (toolkit == 'gonk' && debug) #debug-only failure
 [test_contacts_international.html]
 [test_contacts_substringmatching.html]
 [test_contacts_substringmatchingVE.html]
 [test_contacts_substringmatchingCL.html]
 [test_migration.html]
   support-files =
     test_migration_chrome.js
   skip-if = os == "android"
--- a/security/manager/boot/src/StaticHPKPins.h
+++ b/security/manager/boot/src/StaticHPKPins.h
@@ -1062,9 +1062,9 @@ static const TransportSecurityPreload kP
   { "youtube.com", true, false, false, -1, &kPinset_google_root_pems },
   { "ytimg.com", true, false, false, -1, &kPinset_google_root_pems },
 };
 
 // Pinning Preload List Length = 325;
 
 static const int32_t kUnknownId = -1;
 
-static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1413940883162000);
+static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1414231808712000);
--- a/security/manager/boot/src/nsSTSPreloadList.errors
+++ b/security/manager/boot/src/nsSTSPreloadList.errors
@@ -59,17 +59,17 @@ groups.google.com: did not receive HSTS 
 haste.ch: could not connect to host
 history.google.com: did not receive HSTS header (error ignored - included regardless)
 hostedtalkgadget.google.com: did not receive HSTS header (error ignored - included regardless)
 in.xero.com: max-age too low: 3600
 intercom.io: did not receive HSTS header
 iop.intuit.com: max-age too low: 86400
 irccloud.com: did not receive HSTS header
 jitsi.org: did not receive HSTS header
-jottit.com: did not receive HSTS header
+jottit.com: could not connect to host
 keymaster.lookout.com: did not receive HSTS header
 kiwiirc.com: max-age too low: 5256000
 ledgerscope.net: did not receive HSTS header
 liberty.lavabit.com: could not connect to host
 lifeguard.aecom.com: did not receive HSTS header
 lists.mayfirst.org: did not receive HSTS header
 lumi.do: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 124"  data: no]
 mail.google.com: did not receive HSTS header (error ignored - included regardless)
@@ -89,29 +89,27 @@ ottospora.nl: could not connect to host
 passwordbox.com: did not receive HSTS header
 paypal.com: max-age too low: 14400
 payroll.xero.com: max-age too low: 3600
 piratenlogin.de: did not receive HSTS header
 platform.lookout.com: could not connect to host
 play.google.com: did not receive HSTS header (error ignored - included regardless)
 prodpad.com: did not receive HSTS header
 profiles.google.com: did not receive HSTS header (error ignored - included regardless)
-rapidresearch.me: did not receive HSTS header
+rapidresearch.me: could not connect to host
 riseup.net: did not receive HSTS header
 sah3.net: could not connect to host
 saturngames.co.uk: did not receive HSTS header
 script.google.com: did not receive HSTS header (error ignored - included regardless)
 security.google.com: did not receive HSTS header (error ignored - included regardless)
 semenkovich.com: did not receive HSTS header
 serverdensity.io: did not receive HSTS header
 shops.neonisi.com: could not connect to host
-silentcircle.com: did not receive HSTS header
 silentcircle.org: could not connect to host
 simon.butcher.name: max-age too low: 2629743
-simple.com: did not receive HSTS header
 sites.google.com: did not receive HSTS header (error ignored - included regardless)
 sol.io: could not connect to host
 souyar.de: could not connect to host
 souyar.net: could not connect to host
 souyar.us: could not connect to host
 spreadsheets.google.com: did not receive HSTS header (error ignored - included regardless)
 square.com: did not receive HSTS header
 ssl.google-analytics.com: did not receive HSTS header (error ignored - included regardless)
@@ -121,27 +119,25 @@ sunshinepress.org: could not connect to 
 surfeasy.com: did not receive HSTS header
 talk.google.com: did not receive HSTS header (error ignored - included regardless)
 talkgadget.google.com: did not receive HSTS header (error ignored - included regardless)
 translate.googleapis.com: did not receive HSTS header (error ignored - included regardless)
 uprotect.it: could not connect to host
 wallet.google.com: did not receive HSTS header (error ignored - included regardless)
 webmail.mayfirst.org: did not receive HSTS header
 whonix.org: did not receive HSTS header
-www.airbnb.com: could not connect to host
 www.calyxinstitute.org: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 124"  data: no]
 www.cueup.com: did not receive HSTS header
 www.developer.mydigipass.com: could not connect to host
 www.dropbox.com: max-age too low: 2592000
 www.elanex.biz: did not receive HSTS header
 www.gmail.com: did not receive HSTS header (error ignored - included regardless)
 www.googlemail.com: did not receive HSTS header (error ignored - included regardless)
 www.gov.uk: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 124"  data: no]
 www.greplin.com: could not connect to host
-www.intercom.io: did not receive HSTS header
 www.jitsi.org: did not receive HSTS header
 www.lastpass.com: did not receive HSTS header
 www.ledgerscope.net: did not receive HSTS header
 www.logentries.com: did not receive HSTS header
 www.moneybookers.com: did not receive HSTS header
 www.neonisi.com: could not connect to host
 www.paycheckrecords.com: max-age too low: 86400
 www.paypal.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 124"  data: no]
--- a/security/manager/boot/src/nsSTSPreloadList.inc
+++ b/security/manager/boot/src/nsSTSPreloadList.inc
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*****************************************************************************/
 /* This is an automatically generated file. If you're not                    */
 /* nsSiteSecurityService.cpp, you shouldn't be #including it.     */
 /*****************************************************************************/
 
 #include <stdint.h>
-const PRTime gPreloadListExpirationTime = INT64_C(1416045990752000);
+const PRTime gPreloadListExpirationTime = INT64_C(1416651005379000);
 
 class nsSTSPreload
 {
   public:
     const char *mHost;
     const bool mIncludeSubdomains;
 };
 
@@ -92,16 +92,17 @@ static const nsSTSPreload kSTSPreloadLis
   { "espra.com", true },
   { "f-droid.org", true },
   { "factor.cc", false },
   { "fairbill.com", true },
   { "faq.lookout.com", false },
   { "feedbin.com", false },
   { "fiken.no", true },
   { "fj.simple.com", false },
+  { "flynn.io", true },
   { "forum.linode.com", false },
   { "forum.quantifiedself.com", true },
   { "gernert-server.de", true },
   { "getcloak.com", false },
   { "glass.google.com", true },
   { "gmail.com", false },
   { "go.xero.com", false },
   { "gocardless.com", true },
@@ -117,16 +118,17 @@ static const nsSTSPreload kSTSPreloadLis
   { "history.google.com", true },
   { "hostedtalkgadget.google.com", true },
   { "howrandom.org", true },
   { "id.atlassian.com", true },
   { "id.mayfirst.org", false },
   { "imouto.my", false },
   { "inertianetworks.com", true },
   { "itriskltd.com", true },
+  { "jackyyf.com", false },
   { "keeperapp.com", true },
   { "keepersecurity.com", true },
   { "keyerror.com", true },
   { "kinsights.com", false },
   { "lastpass.com", false },
   { "launchkey.com", true },
   { "library.linode.com", false },
   { "linode.com", false },
@@ -200,17 +202,19 @@ static const nsSTSPreload kSTSPreloadLis
   { "roundcube.mayfirst.org", false },
   { "sakaki.anime.my", true },
   { "sandbox.mydigipass.com", false },
   { "script.google.com", true },
   { "security.google.com", true },
   { "securityheaders.com", true },
   { "seifried.org", true },
   { "shodan.io", true },
+  { "silentcircle.com", true },
   { "simbolo.co.uk", false },
+  { "simple.com", false },
   { "sites.google.com", true },
   { "skydrive.live.com", false },
   { "spreadsheets.google.com", true },
   { "squareup.com", false },
   { "ssl.google-analytics.com", true },
   { "stage.wepay.com", false },
   { "static.wepay.com", false },
   { "stocktrade.de", false },
@@ -266,16 +270,17 @@ static const nsSTSPreload kSTSPreloadLis
   { "www.eternalgoth.co.uk", true },
   { "www.evernote.com", false },
   { "www.getcloak.com", false },
   { "www.gmail.com", false },
   { "www.googlemail.com", false },
   { "www.gov.uk", false },
   { "www.grc.com", false },
   { "www.heliosnet.com", true },
+  { "www.intercom.io", false },
   { "www.irccloud.com", false },
   { "www.linode.com", false },
   { "www.lookout.com", false },
   { "www.makeyourlaws.org", true },
   { "www.mydigipass.com", false },
   { "www.mylookout.com", false },
   { "www.noisebridge.net", false },
   { "www.opsmate.com", true },
--- a/testing/profiles/prefs_general.js
+++ b/testing/profiles/prefs_general.js
@@ -35,16 +35,18 @@ user_pref("dom.min_background_timeout_va
 user_pref("test.mousescroll", true);
 user_pref("security.default_personal_cert", "Select Automatically"); // Need to client auth test be w/o any dialogs
 user_pref("network.http.prompt-temp-redirect", false);
 user_pref("media.cache_size", 100);
 user_pref("media.volume_scale", "0.01");
 user_pref("security.warn_viewing_mixed", false);
 user_pref("app.update.enabled", false);
 user_pref("app.update.staging.enabled", false);
+// Make sure GMPInstallManager won't hit the network.
+user_pref("media.gmp-manager.url", "https://%(server)s/dummy.xml");
 user_pref("browser.panorama.experienced_first_run", true); // Assume experienced
 user_pref("dom.w3c_touch_events.enabled", 1);
 user_pref("dom.undo_manager.enabled", true);
 user_pref("dom.webcomponents.enabled", true);
 user_pref("dom.animations-api.core.enabled", true);
 // Set a future policy version to avoid the telemetry prompt.
 user_pref("toolkit.telemetry.prompted", 999);
 user_pref("toolkit.telemetry.notifiedOptOut", 999);
new file mode 100644
--- /dev/null
+++ b/toolkit/modules/GMPInstallManager.jsm
@@ -0,0 +1,953 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+this.EXPORTED_SYMBOLS = [];
+
+const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu, manager: Cm} =
+  Components;
+// Chunk size for the incremental downloader
+const DOWNLOAD_CHUNK_BYTES_SIZE = 300000;
+// Incremental downloader interval
+const DOWNLOAD_INTERVAL  = 0;
+// 1 day default
+const DEFAULT_SECONDS_BETWEEN_CHECKS = 60 * 60 * 24;
+const OPEN_H264_ID = "gmp-gmpopenh264";
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/FileUtils.jsm");
+Cu.import("resource://gre/modules/Promise.jsm");
+Cu.import("resource://gre/modules/Preferences.jsm");
+Cu.import("resource://gre/modules/Log.jsm");
+Cu.import("resource://gre/modules/osfile.jsm");
+Cu.import("resource://gre/modules/Task.jsm");
+Cu.import("resource://gre/modules/ctypes.jsm");
+
+this.EXPORTED_SYMBOLS = ["GMPInstallManager", "GMPExtractor", "GMPDownloader",
+                         "GMPAddon", "GMPPrefs"];
+
+var gLocale = null;
+
+// Shared code for suppressing bad cert dialogs
+XPCOMUtils.defineLazyGetter(this, "gCertUtils", function() {
+  let temp = { };
+  Cu.import("resource://gre/modules/CertUtils.jsm", temp);
+  return temp;
+});
+
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
+                                  "resource://gre/modules/UpdateChannel.jsm");
+
+// Used to determine if logging should be enabled
+XPCOMUtils.defineLazyGetter(this, "gLogEnabled", function() {
+  return GMPPrefs.get(GMPPrefs.KEY_LOG_ENABLED);
+});
+
+
+function getScopedLogger(prefix) {
+  let logger = Log.repository.getLogger(prefix);
+  if (gLogEnabled) {
+    logger.level = Log.Level.Debug;
+    let appender = new Log.DumpAppender();
+    logger.addAppender(appender);
+  }
+  return logger;
+}
+
+
+/**
+ * Manages preferences for GMP addons
+ */
+let GMPPrefs = {
+  /**
+   * Obtains the specified preference in relation to the specified addon
+   * @param key The GMPPrefs key value to use
+   * @param addon The addon to scope the preference to
+   * @param defaultValue The default value if no preference exists
+   * @return The obtained preference value, or the defaultVlaue if none exists
+   */
+  get: function(key, addon, defaultValue) {
+    if (key === GMPPrefs.KEY_APP_DISTRIBUTION ||
+        key === GMPPrefs.KEY_APP_DISTRIBUTION_VERSION) {
+      let prefValue = "default";
+      try {
+        prefValue = Services.prefs.getDefaultBranch(null).getCharPref(key);
+      } catch (e) {
+        // use default when pref not found
+      }
+      return prefValue;
+    }
+
+    return Preferences.get(this._getPrefKey(key, addon), defaultValue);
+  },
+  /**
+   * Sets the specified preference in relation to the specified addon
+   * @param key The GMPPrefs key value to use
+   * @param val The value to set
+   * @param addon The addon to scope the preference to
+   */
+  set: function(key, val, addon) {
+    let log = getScopedLogger("GMPPrefs.set");
+    log.info("Setting pref: " + this._getPrefKey(key, addon) +
+             " to value: " + val);
+    return Preferences.set(this._getPrefKey(key, addon), val);
+  },
+  _getPrefKey: function(key, addon) {
+    return  key.replace("{0}", addon || "");
+  },
+
+  /**
+   * List of keys which can be used in get and set
+   */
+  KEY_LOG_ENABLED: "media.gmp-manager.log",
+  KEY_ADDON_LAST_UPDATE: "media.{0}.lastUpdate",
+  KEY_ADDON_PATH: "media.{0}.path",
+  KEY_ADDON_VERSION: "media.{0}.version",
+  KEY_URL: "media.gmp-manager.url",
+  KEY_URL_OVERRIDE: "media.gmp-manager.url.override",
+  KEY_CERT_CHECKATTRS: "media.gmp-manager.cert.checkAttributes",
+  KEY_CERT_REQUIREBUILTIN: "media.gmp-manager.cert.requireBuiltIn",
+  KEY_UPDATE_LAST_CHECK: "media.gmp-manager.lastCheck",
+  KEY_UPDATE_SECONDS_BETWEEN_CHECKS: "media.gmp-manager.secondsBetweenChecks",
+  KEY_APP_DISTRIBUTION: "distribution.id",
+  KEY_APP_DISTRIBUTION_VERSION: "distribution.version",
+
+  CERTS_BRANCH: "media.gmp-manager.certs."
+};
+
+// This is copied directly from nsUpdateService.js
+// It is used for calculating the URL string w/ var replacement.
+// TODO: refactor this out somewhere else
+XPCOMUtils.defineLazyGetter(this, "gOSVersion", function aus_gOSVersion() {
+  let osVersion;
+  let sysInfo = Cc["@mozilla.org/system-info;1"].
+                getService(Ci.nsIPropertyBag2);
+  try {
+    osVersion = sysInfo.getProperty("name") + " " + sysInfo.getProperty("version");
+  }
+  catch (e) {
+    LOG("gOSVersion - OS Version unknown: updates are not possible.");
+  }
+
+  if (osVersion) {
+#ifdef XP_WIN
+    const BYTE = ctypes.uint8_t;
+    const WORD = ctypes.uint16_t;
+    const DWORD = ctypes.uint32_t;
+    const WCHAR = ctypes.jschar;
+    const BOOL = ctypes.int;
+
+    // This structure is described at:
+    // http://msdn.microsoft.com/en-us/library/ms724833%28v=vs.85%29.aspx
+    const SZCSDVERSIONLENGTH = 128;
+    const OSVERSIONINFOEXW = new ctypes.StructType('OSVERSIONINFOEXW',
+        [
+        {dwOSVersionInfoSize: DWORD},
+        {dwMajorVersion: DWORD},
+        {dwMinorVersion: DWORD},
+        {dwBuildNumber: DWORD},
+        {dwPlatformId: DWORD},
+        {szCSDVersion: ctypes.ArrayType(WCHAR, SZCSDVERSIONLENGTH)},
+        {wServicePackMajor: WORD},
+        {wServicePackMinor: WORD},
+        {wSuiteMask: WORD},
+        {wProductType: BYTE},
+        {wReserved: BYTE}
+        ]);
+
+    // This structure is described at:
+    // http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx
+    const SYSTEM_INFO = new ctypes.StructType('SYSTEM_INFO',
+        [
+        {wProcessorArchitecture: WORD},
+        {wReserved: WORD},
+        {dwPageSize: DWORD},
+        {lpMinimumApplicationAddress: ctypes.voidptr_t},
+        {lpMaximumApplicationAddress: ctypes.voidptr_t},
+        {dwActiveProcessorMask: DWORD.ptr},
+        {dwNumberOfProcessors: DWORD},
+        {dwProcessorType: DWORD},
+        {dwAllocationGranularity: DWORD},
+        {wProcessorLevel: WORD},
+        {wProcessorRevision: WORD}
+        ]);
+
+    let kernel32 = false;
+    try {
+      kernel32 = ctypes.open("Kernel32");
+    } catch (e) {
+      LOG("gOSVersion - Unable to open kernel32! " + e);
+      osVersion += ".unknown (unknown)";
+    }
+
+    if(kernel32) {
+      try {
+        // Get Service pack info
+        try {
+          let GetVersionEx = kernel32.declare("GetVersionExW",
+                                              ctypes.default_abi,
+                                              BOOL,
+                                              OSVERSIONINFOEXW.ptr);
+          let winVer = OSVERSIONINFOEXW();
+          winVer.dwOSVersionInfoSize = OSVERSIONINFOEXW.size;
+
+          if(0 !== GetVersionEx(winVer.address())) {
+            osVersion += "." + winVer.wServicePackMajor
+                      +  "." + winVer.wServicePackMinor;
+          } else {
+            LOG("gOSVersion - Unknown failure in GetVersionEX (returned 0)");
+            osVersion += ".unknown";
+          }
+        } catch (e) {
+          LOG("gOSVersion - error getting service pack information. Exception: " + e);
+          osVersion += ".unknown";
+        }
+
+        // Get processor architecture
+        let arch = "unknown";
+        try {
+          let GetNativeSystemInfo = kernel32.declare("GetNativeSystemInfo",
+                                                     ctypes.default_abi,
+                                                     ctypes.void_t,
+                                                     SYSTEM_INFO.ptr);
+          let sysInfo = SYSTEM_INFO();
+          // Default to unknown
+          sysInfo.wProcessorArchitecture = 0xffff;
+
+          GetNativeSystemInfo(sysInfo.address());
+          switch(sysInfo.wProcessorArchitecture) {
+            case 9:
+              arch = "x64";
+              break;
+            case 6:
+              arch = "IA64";
+              break;
+            case 0:
+              arch = "x86";
+              break;
+          }
+        } catch (e) {
+          LOG("gOSVersion - error getting processor architecture.  Exception: " + e);
+        } finally {
+          osVersion += " (" + arch + ")";
+        }
+      } finally {
+        kernel32.close();
+      }
+    }
+#endif
+
+    try {
+      osVersion += " (" + sysInfo.getProperty("secondaryLibrary") + ")";
+    }
+    catch (e) {
+      // Not all platforms have a secondary widget library, so an error is nothing to worry about.
+    }
+    osVersion = encodeURIComponent(osVersion);
+  }
+  return osVersion;
+});
+
+// This is copied directly from nsUpdateService.js
+// It is used for calculating the URL string w/ var replacement.
+// TODO: refactor this out somewhere else
+XPCOMUtils.defineLazyGetter(this, "gABI", function aus_gABI() {
+  let abi = null;
+  try {
+    abi = Services.appinfo.XPCOMABI;
+  }
+  catch (e) {
+    LOG("gABI - XPCOM ABI unknown: updates are not possible.");
+  }
+#ifdef XP_MACOSX
+  // Mac universal build should report a different ABI than either macppc
+  // or mactel.
+  let macutils = Cc["@mozilla.org/xpcom/mac-utils;1"].
+                 getService(Ci.nsIMacUtils);
+
+  if (macutils.isUniversalBinary)
+    abi += "-u-" + macutils.architecturesInBinary;
+#ifdef MOZ_SHARK
+  // Disambiguate optimised and shark nightlies
+  abi += "-shark"
+#endif
+#endif
+  return abi;
+});
+
+/**
+ * Provides an easy API for downloading and installing GMP Addons
+ */
+function GMPInstallManager() {
+}
+/**
+ * Temp file name used for downloading
+ */
+GMPInstallManager.prototype = {
+  /**
+   * Obtains a URL with replacement of vars
+   */
+  _getURL: function() {
+    let log = getScopedLogger("GMPInstallManager._getURL");
+    // Use the override URL if it is specified.  The override URL is just like
+    // the normal URL but it does not check the cert.
+    let url = GMPPrefs.get(GMPPrefs.KEY_URL_OVERRIDE);
+    if (url) {
+      log.info("Using override url: " + url);
+    } else {
+      url = GMPPrefs.get(GMPPrefs.KEY_URL);
+      log.info("Using url: " + url);
+    }
+
+    url =
+      url.replace(/%PRODUCT%/g, Services.appinfo.name)
+         .replace(/%VERSION%/g, Services.appinfo.version)
+         .replace(/%BUILD_ID%/g, Services.appinfo.appBuildID)
+         .replace(/%BUILD_TARGET%/g, Services.appinfo.OS + "_" + gABI)
+         .replace(/%OS_VERSION%/g, gOSVersion);
+    if (/%LOCALE%/.test(url)) {
+      // TODO: Get the real local, does it actually matter for GMP plugins?
+      url = url.replace(/%LOCALE%/g, "en-US");
+    }
+    url =
+      url.replace(/%CHANNEL%/g, UpdateChannel.get())
+         .replace(/%PLATFORM_VERSION%/g, Services.appinfo.platformVersion)
+         .replace(/%DISTRIBUTION%/g,
+                  GMPPrefs.get(GMPPrefs.KEY_APP_DISTRIBUTION))
+         .replace(/%DISTRIBUTION_VERSION%/g,
+                  GMPPrefs.get(GMPPrefs.KEY_APP_DISTRIBUTION_VERSION))
+         .replace(/\+/g, "%2B");
+    log.info("Using url (with replacement): " + url);
+    return url;
+  },
+  /**
+   * Performs an addon check.
+   * @return a promise which will be resolved or rejected.
+   *         The promise is resolved with an array of GMPAddons
+   *         The promise is rejected with an object with properties:
+   *           target: The XHR request object
+   *           status: The HTTP status code
+   *           type: Sometimes specifies type of rejection
+   */
+  checkForAddons: function() {
+    let log = getScopedLogger("GMPInstallManager.checkForAddons");
+    if (this._deferred) {
+        log.error("checkForAddons already called");
+        return Promise.reject({type: "alreadycalled"});
+    }
+    this._deferred = Promise.defer();
+    let url = this._getURL();
+
+    this._request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
+                    createInstance(Ci.nsISupports);
+    // This is here to let unit test code override XHR
+    if (this._request.wrappedJSObject) {
+      this._request = this._request.wrappedJSObject;
+    }
+    this._request.open("GET", url, true);
+    let allowNonBuiltIn = !GMPPrefs.get(GMPPrefs.KEY_CERT_CHECKATTRS,
+                                        undefined, true);
+    this._request.channel.notificationCallbacks =
+      new gCertUtils.BadCertHandler(allowNonBuiltIn);
+    // Prevent the request from reading from the cache.
+    this._request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
+    // Prevent the request from writing to the cache.
+    this._request.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
+
+    this._request.overrideMimeType("text/xml");
+    // The Cache-Control header is only interpreted by proxies and the
+    // final destination. It does not help if a resource is already
+    // cached locally.
+    this._request.setRequestHeader("Cache-Control", "no-cache");
+    // HTTP/1.0 servers might not implement Cache-Control and
+    // might only implement Pragma: no-cache
+    this._request.setRequestHeader("Pragma", "no-cache");
+
+    this._request.addEventListener("error", this.onErrorXML.bind(this) ,false);
+    this._request.addEventListener("load", this.onLoadXML.bind(this), false);
+
+    log.info("sending request to: " + url);
+    this._request.send(null);
+
+    return this._deferred.promise;
+  },
+  /**
+   * Installs the specified addon and calls a callback when done.
+   * @param gmpAddon The GMPAddon object to install
+   * @return a promise which will be resolved or rejected
+   *         The promise will resolve with an array of paths that were extracted
+   *         The promise will reject with an error object:
+   *           target: The XHR request object
+   *           status: The HTTP status code
+   *           type: A string to represent the type of error
+   *                 downloaderr, or verifyerr
+   */
+  installAddon: function(gmpAddon) {
+    if (this._deferred) {
+        log.error("checkForAddons already called");
+        return Promise.reject({type: "alreadycalled"});
+    }
+    this.gmpDownloader = new GMPDownloader(gmpAddon);
+    return this.gmpDownloader.start();
+  },
+  _getTimeSinceLastCheck: function() {
+    let now = Math.round(Date.now() / 1000);
+    // Default to 0 here because `now - 0` will be returned later if that case
+    // is hit. We want a large value so a check will occur.
+    let lastCheck = GMPPrefs.get(GMPPrefs.KEY_UPDATE_LAST_CHECK,
+                                 undefined, 0);
+    // Handle clock jumps, return now since we want it to represent
+    // a lot of time has passed since the last check.
+    if (now < lastCheck) {
+      return now;
+    }
+    return now - lastCheck;
+  },
+  _updateLastCheck: function() {
+    let now = Math.round(Date.now() / 1000);
+    GMPPrefs.set(GMPPrefs.KEY_UPDATE_LAST_CHECK, now);
+  },
+  /**
+   * Wrapper for checkForAddons and installaddon.
+   * Will only install if not already installed and will log the results.
+   */
+  simpleCheckAndInstall: function() {
+    let log = getScopedLogger("GMPInstallManager.simpleCheckAndInstall");
+    let secondsBetweenChecks =
+      GMPPrefs.get(GMPPrefs.KEY_UPDATE_SECONDS_BETWEEN_CHECKS, undefined,
+                   DEFAULT_SECONDS_BETWEEN_CHECKS)
+    let secondsSinceLast = this._getTimeSinceLastCheck();
+    log.info("Last check was: " + secondsSinceLast +
+             " seconds ago, minimum seconds: " + secondsBetweenChecks);
+    if (secondsBetweenChecks > secondsSinceLast) {
+      log.info("Will not check for updates.");
+      return Promise.resolve();
+    }
+
+    let promise = this.checkForAddons();
+    promise.then(gmpAddons => {
+      this._updateLastCheck();
+      log.info("Found " + gmpAddons.length + " addons advertised.");
+      let addonsToInstall = gmpAddons.filter(gmpAddon => {
+        log.info("Found addon: " + gmpAddon.toString());
+        return gmpAddon.isValid && gmpAddon.isOpenH264 &&
+               !gmpAddon.isInstalled
+      });
+      if (!addonsToInstall.length) {
+        log.info("No new addons to install, returning");
+        return;
+      }
+      addonsToInstall.forEach(gmpAddon => {
+        promise = this.installAddon(gmpAddon);
+        promise.then(extractedPaths => {
+          // installed!
+          log.info("Addon installed successfully: " + gmpAddon.toString());
+        }, () => {
+          if (!GMPPrefs.get(GMPPrefs.KEY_LOG_ENABLED)) {
+            Cu.reportError(gmpAddon.toString() + " could not be installed. Enable " +
+                           GMPPrefs.KEY_LOG_ENABLED + " for details!");
+          }
+          log.error("Could not install addon: " + gmpAddon.toString());
+        });
+      });
+    });
+  },
+
+  /**
+   * Makes sure everything is cleaned up
+   */
+  uninit: function() {
+    let log = getScopedLogger("GMPDownloader.uninit");
+    if (this._request) {
+      log.info("Aborting request");
+      this._request.abort();
+    }
+    if (this._deferred) {
+        log.info("Rejecting deferred");
+        this._deferred.reject({type: "uninitialized"});
+    }
+    log.info("Done cleanup");
+  },
+
+  /**
+   * If set to true, specifies to leave the temporary downloaded zip file.
+   * This is useful for tests.
+   */
+  overrideLeaveDownloadedZip: false,
+
+  /**
+   * The XMLHttpRequest succeeded and the document was loaded.
+   * @param event The nsIDOMEvent for the load
+  */
+  onLoadXML: function(event) {
+    let log = getScopedLogger("GMPInstallManager.onLoadXML");
+    log.info("request completed downloading document");
+
+    let certs = null;
+    if (!Services.prefs.prefHasUserValue(GMPPrefs.KEY_URL_OVERRIDE) &&
+        GMPPrefs.get(GMPPrefs.KEY_CERT_CHECKATTRS, undefined, true)) {
+      certs = gCertUtils.readCertPrefs(GMPPrefs.CERTS_BRANCH);
+    }
+
+    let allowNonBuiltIn = !GMPPrefs.get(GMPPrefs.KEY_CERT_REQUIREBUILTIN,
+                                        undefined, true);
+    log.info("allowNonBuiltIn: " + allowNonBuiltIn);
+    gCertUtils.checkCert(this._request.channel, allowNonBuiltIn, certs);
+
+    this.parseResponseXML();
+  },
+
+  /**
+   * Returns the status code for the XMLHttpRequest
+   */
+  _getChannelStatus: function(request) {
+    let log = getScopedLogger("GMPInstallManager._getChannelStatus");
+    let status = 0;
+    try {
+      status = request.status;
+      log.info("request.status is: " + request.status);
+    }
+    catch (e) {
+    }
+
+    if (status == 0) {
+      status = request.channel.QueryInterface(Ci.nsIRequest).status;
+    }
+    return status;
+  },
+
+  /**
+   * There was an error of some kind during the XMLHttpRequest
+   * @param event The nsIDOMEvent for the error
+  */
+  onErrorXML: function(event) {
+    let log = getScopedLogger("GMPInstallManager.onErrorXML");
+    let request = event.target;
+    let status = this._getChannelStatus(request);
+    let message = "request.status: " + status;
+    log.warn(message);
+    this._deferred.reject({
+      target: request,
+      status: status,
+      message: message
+    });
+    delete this._deferred;
+  },
+
+  /**
+   * Returns an array of GMPAddon objects discovered by the update check.
+   * Or returns an empty array if there were any problems with parsing.
+   * If there's an error, it will be logged if logging is enabled.
+   */
+  parseResponseXML: function() {
+    try {
+      let log = getScopedLogger("GMPInstallManager.parseResponseXML");
+      let updatesElement = this._request.responseXML.documentElement;
+      if (!updatesElement) {
+        let message = "empty updates document";
+        log.warn(message);
+        this._deferred.reject({
+          target: this._request,
+          message: message
+        });
+        delete this._deferred;
+        return;
+      }
+
+      if (updatesElement.nodeName != "updates") {
+        let message = "got node name: " + updatesElement.nodeName +
+          ", expected: updates";
+        log.warn(message);
+        this._deferred.reject({
+          target: this._request,
+          message: message
+        });
+        delete this._deferred;
+        return;
+      }
+
+      const ELEMENT_NODE = Ci.nsIDOMNode.ELEMENT_NODE;
+      let gmpResults = [];
+      for (let i = 0; i < updatesElement.childNodes.length; ++i) {
+        let updatesChildElement = updatesElement.childNodes.item(i);
+        if (updatesChildElement.nodeType != ELEMENT_NODE) {
+          continue;
+        }
+        if (updatesChildElement.localName == "addons") {
+          gmpResults = GMPAddon.parseGMPAddonsNode(updatesChildElement);
+        }
+      }
+       this._deferred.resolve(gmpResults);
+       delete this._deferred;
+    } catch (e) {
+      this._deferred.reject({
+        target: this._request,
+        message: e
+      });
+      delete this._deferred;
+    }
+  },
+};
+
+/**
+ * Used to construct a single GMP addon
+ * GMPAddon objects are returns from GMPInstallManager.checkForAddons
+ * GMPAddon objects can also be used in calls to GMPInstallManager.installAddon
+ *
+ * @param gmpAddon The AUS response XML's DOM element `addon`
+ */
+function GMPAddon(gmpAddon) {
+  let log = getScopedLogger("GMPAddon.constructor");
+  gmpAddon.QueryInterface(Ci.nsIDOMElement);
+  ["id", "URL", "hashFunction",
+   "hashValue", "version", "size"].forEach(name => {
+    if (gmpAddon.hasAttribute(name)) {
+      this[name] = gmpAddon.getAttribute(name);
+    }
+  });
+  this.size = Number(this.size) || undefined;
+  log.info ("Created new addon: " + this.toString());
+}
+/**
+ * Parses an XML GMP addons node from AUS into an array
+ * @param addonsElement An nsIDOMElement compatible node with XML from AUS
+ * @return An array of GMPAddon results
+ */
+GMPAddon.parseGMPAddonsNode = function(addonsElement) {
+  let log = getScopedLogger("GMPAddon.parseGMPAddonsNode");
+  let gmpResults = [];
+  if (addonsElement.localName !== "addons") {
+    return;
+  }
+
+  addonsElement.QueryInterface(Ci.nsIDOMElement);
+  let addonCount = addonsElement.childNodes.length;
+  for (let i = 0; i < addonCount; ++i) {
+    let addonElement = addonsElement.childNodes.item(i);
+    if (addonElement.localName !== "addon") {
+      continue;
+    }
+    addonElement.QueryInterface(Ci.nsIDOMElement);
+    try {
+      gmpResults.push(new GMPAddon(addonElement));
+    } catch (e) {
+      log.warn("invalid addon: " + e);
+      continue;
+    }
+  }
+  return gmpResults;
+};
+GMPAddon.prototype = {
+  /**
+   * Returns a string representation of the addon
+   */
+  toString: function() {
+    return this.id + " (" +
+           "isValid: " + this.isValid +
+           ", isInstalled: " + this.isInstalled +
+           ", isOpenH264: " + this.isOpenH264 +
+           ", hashFunction: " + this.hashFunction+
+           ", hashValue: " + this.hashValue +
+           (this.size !== undefined ? ", size: " + this.size : "" ) +
+           ")";
+  },
+  /**
+   * If all the fields aren't specified don't consider this addon valid
+   * @return true if the addon is parsed and valid
+   */
+  get isValid() {
+    return this.id && this.URL && this.version &&
+      this.hashFunction && !!this.hashValue;
+  },
+  /**
+   * Open H264 has special handling.
+   * @return true if the plugin is the openh264 plugin
+   */
+  get isOpenH264() {
+    return this.id === OPEN_H264_ID;
+  },
+  get isInstalled() {
+    return this.version &&
+      GMPPrefs.get(GMPPrefs.KEY_ADDON_VERSION, this.id) === this.version;
+  }
+};
+/**
+ * Constructs a GMPExtractor object which is used to extract a GMP zip
+ * into the specified location. (Which typically leties per platform)
+ * @param zipPath The path on disk of the zip file to extract
+ */
+function GMPExtractor(zipPath, installToDirPath) {
+    this.zipPath = zipPath;
+    this.installToDirPath = installToDirPath;
+}
+GMPExtractor.prototype = {
+  /**
+   * Obtains a list of all the entries in a zipfile in the format of *.*.
+   * This also includes files inside directories.
+   *
+   * @param zipReader the nsIZipReader to check
+   * @return An array of string name entries which can be used
+   *         in nsIZipReader.extract
+   */
+  _getZipEntries: function(zipReader) {
+    let entries = [];
+    let enumerator = zipReader.findEntries("*.*");
+    while (enumerator.hasMore()) {
+      entries.push(enumerator.getNext());
+    }
+    return entries;
+  },
+  /**
+   * Installs the this.zipPath contents into the directory used to store GMP
+   * addons for the current platform.
+   *
+   * @return a promise which will be resolved or rejected
+   *         See GMPInstallManager.installAddon for resolve/rejected info
+   */
+  install: function() {
+    try {
+      let log = getScopedLogger("GMPExtractor.install");
+      this._deferred = Promise.defer();
+      log.info("Installing " + this.zipPath + "...");
+      // Get the input zip file
+      let zipFile = Cc["@mozilla.org/file/local;1"].
+                    createInstance(Ci.nsIFile);
+      zipFile.initWithPath(this.zipPath);
+
+      // Initialize a zipReader and obtain the entries
+      var zipReader = Cc["@mozilla.org/libjar/zip-reader;1"].
+                      createInstance(Ci.nsIZipReader);
+      zipReader.open(zipFile)
+      let entries = this._getZipEntries(zipReader);
+      let extractedPaths = [];
+
+      // Extract each of the entries
+      entries.forEach(entry => {
+        // We don't need these types of files
+        if (entry.contains("__MACOSX")) {
+          return;
+        }
+        let outFile = Cc["@mozilla.org/file/local;1"].
+                      createInstance(Ci.nsILocalFile);
+        outFile.initWithPath(this.installToDirPath);
+        outFile.appendRelativePath(entry);
+
+        // Make sure the directory hierarchy exists
+        if(!outFile.parent.exists()) {
+          outFile.parent.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0755", 8));
+        }
+        zipReader.extract(entry, outFile);
+        extractedPaths.push(outFile.path);
+        log.info(entry + " was successfully extracted to: " +
+            outFile.path);
+      });
+      zipReader.close();
+      if (!GMPInstallManager.overrideLeaveDownloadedZip) {
+        zipFile.remove(false);
+      }
+
+      log.info(this.zipPath + " was installed successfully");
+      this._deferred.resolve(extractedPaths);
+    } catch (e) {
+      if (zipReader) {
+        zipReader.close();
+      }
+      this._deferred.reject({
+        target: this,
+        status: e,
+        type: "exception"
+      });
+    }
+    return this._deferred.promise;
+  }
+};
+
+
+/**
+ * Constructs an object which downloads and initiates an install of
+ * the specified GMPAddon object.
+ * @param gmpAddon The addon to install.
+ */
+function GMPDownloader(gmpAddon)
+{
+  this._gmpAddon = gmpAddon;
+}
+/**
+ * Computes the file hash of fileToHash with the specified hash function
+ * @param hashFunctionName A hash function name such as sha512
+ * @param fileToHash An nsIFile to hash
+ * @return a promise which resolve to a digest in binary hex format
+ */
+GMPDownloader.computeHash = function(hashFunctionName, fileToHash) {
+  let log = getScopedLogger("GMPDownloader.computeHash");
+  let digest;
+  let fileStream = Cc["@mozilla.org/network/file-input-stream;1"].
+                   createInstance(Ci.nsIFileInputStream);
+  fileStream.init(fileToHash, FileUtils.MODE_RDONLY,
+                  FileUtils.PERMS_FILE, 0);
+  try {
+    let hash = Cc["@mozilla.org/security/hash;1"].
+               createInstance(Ci.nsICryptoHash);
+    let hashFunction =
+      Ci.nsICryptoHash[hashFunctionName.toUpperCase()];
+    if (!hashFunction) {
+      log.error("could not get hash function");
+      return Promise.reject();
+    }
+    hash.init(hashFunction);
+    hash.updateFromStream(fileStream, -1);
+    digest = binaryToHex(hash.finish(false));
+  } catch (e) {
+    log.warn("failed to compute hash: " + e);
+    digest = "";
+  }
+  fileStream.close();
+  return Promise.resolve(digest);
+},
+GMPDownloader.prototype = {
+  /**
+   * Starts the download process for an addon.
+   * @return a promise which will be resolved or rejected
+   *         See GMPInstallManager.installAddon for resolve/rejected info
+   */
+  start: function() {
+    let log = getScopedLogger("GMPDownloader.start");
+    this._deferred = Promise.defer();
+    if (!this._gmpAddon.isValid) {
+      log.info("gmpAddon is not valid, will not continue");
+      return Promise.reject({
+        target: this,
+        status: status,
+        type: "downloaderr"
+      });
+    }
+
+    let uri = Services.io.newURI(this._gmpAddon.URL, null, null);
+    this._request = Cc["@mozilla.org/network/incremental-download;1"].
+                    createInstance(Ci.nsIIncrementalDownload);
+    let gmpFile = FileUtils.getFile("TmpD", [this._gmpAddon.id + ".zip"]);
+    if (gmpFile.exists()) {
+      gmpFile.remove(false);
+    }
+
+    log.info("downloading from " + uri.spec + " to " + gmpFile.path);
+    this._request.init(uri, gmpFile, DOWNLOAD_CHUNK_BYTES_SIZE,
+                       DOWNLOAD_INTERVAL);
+    this._request.start(this, null);
+    return this._deferred.promise;
+  },
+  // For nsIRequestObserver
+  onStartRequest: function(request, context) {
+  },
+  // For nsIRequestObserver
+  // Called when the GMP addon zip file is downloaded
+  onStopRequest: function(request, context, status) {
+    let log = getScopedLogger("GMPDownloader.onStopRequest");
+    log.info("onStopRequest called");
+    if (!Components.isSuccessCode(status)) {
+      log.info("status failed: " + status);
+      this._deferred.reject({
+        target: this,
+        status: status,
+        type: "downloaderr"
+      });
+      return;
+    }
+
+    let promise = this._verifyDownload();
+    promise.then(() => {
+      log.info("GMP file is ready to unzip");
+      let destination = this._request.destination;
+
+      let zipPath = destination.path;
+      let gmpAddon = this._gmpAddon;
+      let installToDirPath = Cc["@mozilla.org/file/local;1"].
+                          createInstance(Ci.nsIFile);
+      let path = OS.Path.join(OS.Constants.Path.profileDir, gmpAddon.id);
+      installToDirPath.initWithPath(path);
+      log.info("install to directory path: " + installToDirPath.path);
+      let gmpInstaller = new GMPExtractor(zipPath, installToDirPath.path);
+      let installPromise = gmpInstaller.install();
+      installPromise.then(extractedPaths => {
+        // Success, set the prefs
+        let now = Math.round(Date.now() / 1000);
+        GMPPrefs.set(GMPPrefs.KEY_ADDON_LAST_UPDATE, now, gmpAddon.id);
+        // Setting the path pref signals installation completion to consumers,
+        // so set the version and potential other information they use first.
+        GMPPrefs.set(GMPPrefs.KEY_ADDON_VERSION, gmpAddon.version,
+                     gmpAddon.id);
+        GMPPrefs.set(GMPPrefs.KEY_ADDON_PATH,
+                     installToDirPath.path, gmpAddon.id);
+        this._deferred.resolve(extractedPaths);
+      }, err => {
+        this._deferred.reject(err);
+      });
+    }, err => {
+      log.warn("verifyDownload check failed");
+      this._deferred.reject({
+        target: this,
+        status: 200,
+        type: "verifyerr"
+      });
+    });
+  },
+  /**
+   * Verifies that the downloaded zip file's hash matches the GMPAddon hash.
+   * @return a promise which resolves if the download verifies
+   */
+  _verifyDownload: function() {
+    let verifyDownloadDeferred = Promise.defer();
+    let log = getScopedLogger("GMPDownloader._verifyDownload");
+    log.info("_verifyDownload called");
+    if (!this._request) {
+      return Promise.reject();
+    }
+
+    let destination = this._request.destination;
+    log.info("for path: " + destination.path);
+
+    // Ensure that the file size matches the expected file size.
+    if (this._gmpAddon.size !== undefined &&
+        destination.fileSize != this._gmpAddon.size) {
+      log.warn("Downloader:_verifyDownload downloaded size " +
+               destination.fileSize + " != expected size " +
+               this._gmpAddon.size + ".");
+      return Promise.reject();
+    }
+
+    let promise = GMPDownloader.computeHash(this._gmpAddon.hashFunction, destination);
+    promise.then(digest => {
+        let expectedDigest = this._gmpAddon.hashValue.toLowerCase();
+        if (digest !== expectedDigest) {
+          log.warn("hashes do not match! Got: `" +
+                   digest + "`, expected: `" + expectedDigest +  "`");
+          this._deferred.reject();
+          return;
+        }
+
+        log.info("hashes match!");
+        verifyDownloadDeferred.resolve();
+    }, err => {
+        verifyDownloadDeferred.reject();
+    });
+    return verifyDownloadDeferred.promise;
+  },
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIRequestObserver])
+};
+
+/**
+ * Convert a string containing binary values to hex.
+ */
+function binaryToHex(input) {
+  let result = "";
+  for (let i = 0; i < input.length; ++i) {
+    let hex = input.charCodeAt(i).toString(16);
+    if (hex.length == 1)
+      hex = "0" + hex;
+    result += hex;
+  }
+  return result;
+}
--- a/toolkit/modules/moz.build
+++ b/toolkit/modules/moz.build
@@ -55,16 +55,17 @@ EXTRA_JS_MODULES += [
     'Task.jsm',
     'TelemetryTimestamps.jsm',
     'Timer.jsm',
     'ZipUtils.jsm',
 ]
 
 EXTRA_PP_JS_MODULES += [
     'CertUtils.jsm',
+    'GMPInstallManager.jsm',
     'ResetProfile.jsm',
     'Services.jsm',
     'Troubleshoot.jsm',
     'UpdateChannel.jsm',
     'WindowDraggingUtils.jsm',
     'WindowsPrefSync.jsm',
 ]
 
new file mode 100644
--- /dev/null
+++ b/toolkit/modules/tests/xpcshell/test_GMPInstallManager.js
@@ -0,0 +1,725 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu, manager: Cm} = Components;
+const URL_HOST = "http://localhost";
+
+Cu.import("resource://gre/modules/GMPInstallManager.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/FileUtils.jsm");
+Cu.import("resource://testing-common/httpd.js");
+Cu.import("resource://gre/modules/Promise.jsm");
+Cu.import("resource://gre/modules/Preferences.jsm")
+
+do_get_profile();
+
+function run_test() {Cu.import("resource://gre/modules/Preferences.jsm")
+  Preferences.set("media.gmp-manager.log", true);
+  run_next_test();
+}
+
+/**
+ * Tests that the helper used for preferences works correctly
+ */
+add_test(function test_prefs() {
+  let addon1 = "addon1", addon2 = "addon2";
+
+  GMPPrefs.set(GMPPrefs.KEY_LOG_ENABLED, true);
+  GMPPrefs.set(GMPPrefs.KEY_URL, "http://not-really-used");
+  GMPPrefs.set(GMPPrefs.KEY_URL_OVERRIDE, "http://not-really-used-2");
+  GMPPrefs.set(GMPPrefs.KEY_ADDON_LAST_UPDATE, "1", addon1);
+  GMPPrefs.set(GMPPrefs.KEY_ADDON_PATH, "2", addon1);
+  GMPPrefs.set(GMPPrefs.KEY_ADDON_VERSION, "3", addon1);
+  GMPPrefs.set(GMPPrefs.KEY_ADDON_LAST_UPDATE, "4", addon2);
+  GMPPrefs.set(GMPPrefs.KEY_ADDON_PATH, "5", addon2);
+  GMPPrefs.set(GMPPrefs.KEY_ADDON_VERSION, "6", addon2);
+  GMPPrefs.set(GMPPrefs.KEY_CERT_CHECKATTRS, true);
+
+  do_check_true(GMPPrefs.get(GMPPrefs.KEY_LOG_ENABLED));
+  do_check_eq(GMPPrefs.get(GMPPrefs.KEY_URL), "http://not-really-used");
+  do_check_eq(GMPPrefs.get(GMPPrefs.KEY_URL_OVERRIDE), "http://not-really-used-2");
+  do_check_eq(GMPPrefs.get(GMPPrefs.KEY_ADDON_LAST_UPDATE, addon1), "1");
+  do_check_eq(GMPPrefs.get(GMPPrefs.KEY_ADDON_PATH, addon1), "2");
+  do_check_eq(GMPPrefs.get(GMPPrefs.KEY_ADDON_VERSION, addon1), "3");
+  do_check_eq(GMPPrefs.get(GMPPrefs.KEY_ADDON_LAST_UPDATE, addon2), "4");
+  do_check_eq(GMPPrefs.get(GMPPrefs.KEY_ADDON_PATH, addon2), "5");
+  do_check_eq(GMPPrefs.get(GMPPrefs.KEY_ADDON_VERSION, addon2), "6");
+  do_check_true(GMPPrefs.get(GMPPrefs.KEY_CERT_CHECKATTRS));
+  run_next_test();
+});
+
+/**
+ * Tests that an uninit without a check works fine
+ */
+add_test(function test_checkForAddons_noResponse() {
+  let installManager = new GMPInstallManager();
+  installManager.uninit();
+  run_next_test();
+});
+
+/**
+ * Tests that an uninit without an install works fine
+ */
+add_test(function test_checkForAddons_noResponse() {
+  overrideXHR(200, "");
+  let installManager = new GMPInstallManager();
+  let promise = installManager.checkForAddons();
+  promise.then(function(gmpAddons) {
+    do_throw("no repsonse should reject");
+  }, function(err) {
+      installManager.uninit();
+  });
+  run_next_test();
+});
+
+/**
+ * Tests that no response returned rejects
+ */
+add_test(function test_checkForAddons_noResponse() {
+  overrideXHR(200, "");
+  let installManager = new GMPInstallManager();
+  let promise = installManager.checkForAddons();
+  promise.then(function(gmpAddons) {
+    do_throw("no repsonse should reject");
+  }, function(err) {
+    do_check_true(!!err);
+    installManager.uninit();
+    run_next_test();
+  });
+});
+
+/**
+ * Tests that no addons element returned resolves with no addons
+ */
+add_task(function test_checkForAddons_noAddonsElement() {
+  overrideXHR(200, "<updates></updates>");
+  let installManager = new GMPInstallManager();
+  let gmpAddons = yield installManager.checkForAddons();
+  do_check_eq(gmpAddons.length, 0);
+  installManager.uninit();
+});
+
+/**
+ * Tests that empty addons element returned resolves with no addons
+ */
+add_task(function test_checkForAddons_noAddonsElement() {
+  overrideXHR(200, "<updates><addons/></updates>");
+  let installManager = new GMPInstallManager();
+  let gmpAddons = yield installManager.checkForAddons();
+  do_check_eq(gmpAddons.length, 0);
+  installManager.uninit();
+});
+
+/**
+ * Tests that a response with the wrong root element rejects
+ */
+add_test(function test_checkForAddons_wrongResponseXML() {
+  overrideXHR(200, "<digits_of_pi>3.141592653589793....</digits_of_pi>");
+  let installManager = new GMPInstallManager();
+  let promise = installManager.checkForAddons();
+  promise.then(function(err, gmpAddons) {
+    do_throw("response with the wrong root element should reject");
+  }, function(err) {
+    do_check_true(!!err);
+    installManager.uninit();
+    run_next_test();
+  });
+});
+
+
+/**
+ * Tests that a 404 error works as expected
+ */
+add_test(function test_checkForAddons_404Error() {
+  overrideXHR(404, "");
+  let installManager = new GMPInstallManager();
+  let promise = installManager.checkForAddons();
+  promise.then(function(gmpAddons) {
+    do_throw("404 response should reject");
+  }, function(err) {
+    do_check_true(!!err);
+    do_check_eq(err.status, 404);
+    installManager.uninit();
+    run_next_test();
+  });
+});
+
+
+/**
+ * Tests that gettinga a funky non XML response works as expected
+ */
+add_test(function test_checkForAddons_notXML() {
+  overrideXHR(200, "3.141592653589793....");
+  let installManager = new GMPInstallManager();
+  let promise = installManager.checkForAddons();
+  promise.then(function(gmpAddons) {
+    do_throw("non XML response should reject");
+  }, function(err) {
+    do_check_true(!!err);
+    installManager.uninit();
+    run_next_test();
+  });
+});
+
+/**
+ * Tests that getting a response with a single addon works as expected
+ */
+add_test(function test_checkForAddons_singleAddonNoUpdates() {
+  let responseXML =
+    "<?xml version=\"1.0\"?>" +
+    "<updates>" +
+    "    <addons>" +
+    "        <addon id=\"gmp-gmpopenh264\"" +
+    "               URL=\"http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip\"" +
+    "               hashFunction=\"sha256\"" +
+    "               hashValue=\"1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee\"" +
+    "               version=\"1.1\"/>" +
+    "  </addons>" +
+    "</updates>"
+  overrideXHR(200, responseXML);
+  let installManager = new GMPInstallManager();
+  let promise = installManager.checkForAddons();
+  promise.then(function(gmpAddons) {
+    do_check_eq(gmpAddons.length, 1);
+    let gmpAddon= gmpAddons[0];
+    do_check_eq(gmpAddon.id, "gmp-gmpopenh264");
+    do_check_eq(gmpAddon.URL, "http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip");
+    do_check_eq(gmpAddon.hashFunction, "sha256");
+    do_check_eq(gmpAddon.hashValue, "1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee");
+    do_check_eq(gmpAddon.version, "1.1");
+    do_check_eq(gmpAddon.size, undefined);
+    do_check_true(gmpAddon.isValid);
+    do_check_true(gmpAddon.isOpenH264);
+    do_check_false(gmpAddon.isInstalled);
+    installManager.uninit();
+    run_next_test();
+  }, function(err) {
+    do_throw("1 addon found should not reject");
+  });
+});
+
+/**
+ * Tests that getting a response with a single addon with the optional size
+ * attribute parses as expected.
+ */
+add_test(function test_checkForAddons_singleAddonNoUpdates() {
+  let responseXML =
+    "<?xml version=\"1.0\"?>" +
+    "<updates>" +
+    "    <addons>" +
+    "        <addon id=\"openh264-plugin-no-at-symbol\"" +
+    "               URL=\"http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip\"" +
+    "               hashFunction=\"sha256\"" +
+    "               size=\"42\"" +
+    "               hashValue=\"1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee\"" +
+    "               version=\"1.1\"/>" +
+    "  </addons>" +
+    "</updates>"
+  overrideXHR(200, responseXML);
+  let installManager = new GMPInstallManager();
+  let promise = installManager.checkForAddons();
+  promise.then(function(gmpAddons) {
+    do_check_eq(gmpAddons.length, 1);
+    let gmpAddon= gmpAddons[0];
+    do_check_eq(gmpAddon.id, "openh264-plugin-no-at-symbol");
+    do_check_eq(gmpAddon.URL, "http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip");
+    do_check_eq(gmpAddon.hashFunction, "sha256");
+    do_check_eq(gmpAddon.hashValue, "1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee");
+    do_check_eq(gmpAddon.size, 42);
+    do_check_eq(gmpAddon.version, "1.1");
+    do_check_true(gmpAddon.isValid);
+    do_check_false(gmpAddon.isOpenH264);
+    do_check_false(gmpAddon.isInstalled);
+    installManager.uninit();
+    run_next_test();
+  }, function(err) {
+    do_throw("1 addon found should not reject");
+  });
+});
+
+/**
+ * Tests that checking for multiple addons work correctly.
+ * Also tests that invalid addons work correctly.
+ */
+add_test(function test_checkForAddons_multipleAddonNoUpdatesSomeInvalid() {
+  let responseXML =
+    "<?xml version=\"1.0\"?>" +
+    "<updates>" +
+    "    <addons>" +
+    // valid openh264
+    "        <addon id=\"gmp-gmpopenh264\"" +
+    "               URL=\"http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip\"" +
+    "               hashFunction=\"sha256\"" +
+    "               hashValue=\"1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee\"" +
+    "               version=\"1.1\"/>" +
+    // valid not openh264
+    "        <addon id=\"NOT-gmp-gmpopenh264\"" +
+    "               URL=\"http://127.0.0.1:8011/NOT-gmp-gmpopenh264-1.1.zip\"" +
+    "               hashFunction=\"sha512\"" +
+    "               hashValue=\"141592656f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee\"" +
+    "               version=\"9.1\"/>" +
+    // noid
+    "        <addon notid=\"NOT-gmp-gmpopenh264\"" +
+    "               URL=\"http://127.0.0.1:8011/NOT-gmp-gmpopenh264-1.1.zip\"" +
+    "               hashFunction=\"sha512\"" +
+    "               hashValue=\"141592656f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee\"" +
+    "               version=\"9.1\"/>" +
+    // no URL
+    "        <addon id=\"NOT-gmp-gmpopenh264\"" +
+    "               notURL=\"http://127.0.0.1:8011/NOT-gmp-gmpopenh264-1.1.zip\"" +
+    "               hashFunction=\"sha512\"" +
+    "               hashValue=\"141592656f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee\"" +
+    "               version=\"9.1\"/>" +
+    // no hash function
+    "        <addon id=\"NOT-gmp-gmpopenh264\"" +
+    "               URL=\"http://127.0.0.1:8011/NOT-gmp-gmpopenh264-1.1.zip\"" +
+    "               nothashFunction=\"sha512\"" +
+    "               hashValue=\"141592656f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee\"" +
+    "               version=\"9.1\"/>" +
+    // no hash function
+    "        <addon id=\"NOT-gmp-gmpopenh264\"" +
+    "               URL=\"http://127.0.0.1:8011/NOT-gmp-gmpopenh264-1.1.zip\"" +
+    "               hashFunction=\"sha512\"" +
+    "               nothashValue=\"141592656f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee\"" +
+    "               version=\"9.1\"/>" +
+    // not version
+    "        <addon id=\"NOT-gmp-gmpopenh264\"" +
+    "               URL=\"http://127.0.0.1:8011/NOT-gmp-gmpopenh264-1.1.zip\"" +
+    "               hashFunction=\"sha512\"" +
+    "               hashValue=\"141592656f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee\"" +
+    "               notversion=\"9.1\"/>" +
+    "  </addons>" +
+    "</updates>"
+  overrideXHR(200, responseXML);
+  let installManager = new GMPInstallManager();
+  let promise = installManager.checkForAddons();
+  promise.then(function(gmpAddons) {
+    do_check_eq(gmpAddons.length, 7);
+    let gmpAddon= gmpAddons[0];
+    do_check_eq(gmpAddon.id, "gmp-gmpopenh264");
+    do_check_eq(gmpAddon.URL, "http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip");
+    do_check_eq(gmpAddon.hashFunction, "sha256");
+    do_check_eq(gmpAddon.hashValue, "1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee");
+    do_check_eq(gmpAddon.version, "1.1");
+    do_check_true(gmpAddon.isValid);
+    do_check_true(gmpAddon.isOpenH264);
+    do_check_false(gmpAddon.isInstalled);
+
+    gmpAddon= gmpAddons[1];
+    do_check_eq(gmpAddon.id, "NOT-gmp-gmpopenh264");
+    do_check_eq(gmpAddon.URL, "http://127.0.0.1:8011/NOT-gmp-gmpopenh264-1.1.zip");
+    do_check_eq(gmpAddon.hashFunction, "sha512");
+    do_check_eq(gmpAddon.hashValue, "141592656f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee");
+    do_check_eq(gmpAddon.version, "9.1");
+    do_check_true(gmpAddon.isValid);
+    do_check_false(gmpAddon.isOpenH264);
+    do_check_false(gmpAddon.isInstalled);
+
+    for (let i = 2; i < gmpAddons.length; i++) {
+      do_check_false(gmpAddons[i].isValid);
+      do_check_false(gmpAddons[i].isInstalled);
+    }
+    installManager.uninit();
+    run_next_test();
+  }, function(err) {
+    do_throw("multiple addons found should not reject");
+  });
+});
+
+/**
+ * Tests that checking for addons when there are also updates available
+ * works as expected.
+ */
+add_test(function test_checkForAddons_updatesWithAddons() {
+  let responseXML =
+    "<?xml version=\"1.0\"?>" +
+    "    <updates>" +
+    "        <update type=\"minor\" displayVersion=\"33.0a1\" appVersion=\"33.0a1\" platformVersion=\"33.0a1\" buildID=\"20140628030201\">" +
+    "        <patch type=\"complete\" URL=\"http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/2014/06/2014-06-28-03-02-01-mozilla-central/firefox-33.0a1.en-US.mac.complete.mar\" hashFunction=\"sha512\" hashValue=\"f3f90d71dff03ae81def80e64bba3e4569da99c9e15269f731c2b167c4fc30b3aed9f5fee81c19614120230ca333e73a5e7def1b8e45d03135b2069c26736219\" size=\"85249896\"/>" +
+    "    </update>" +
+    "    <addons>" +
+    "        <addon id=\"gmp-gmpopenh264\"" +
+    "               URL=\"http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip\"" +
+    "               hashFunction=\"sha256\"" +
+    "               hashValue=\"1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee\"" +
+    "               version=\"1.1\"/>" +
+    "  </addons>" +
+    "</updates>"
+  overrideXHR(200, responseXML);
+  let installManager = new GMPInstallManager();
+  let promise = installManager.checkForAddons();
+  promise.then(function(gmpAddons) {
+    do_check_eq(gmpAddons.length, 1);
+    let gmpAddon= gmpAddons[0];
+    do_check_eq(gmpAddon.id, "gmp-gmpopenh264");
+    do_check_eq(gmpAddon.URL, "http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip");
+    do_check_eq(gmpAddon.hashFunction, "sha256");
+    do_check_eq(gmpAddon.hashValue, "1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee");
+    do_check_eq(gmpAddon.version, "1.1");
+    do_check_true(gmpAddon.isValid);
+    do_check_true(gmpAddon.isOpenH264);
+    do_check_false(gmpAddon.isInstalled);
+    installManager.uninit();
+    run_next_test();
+  }, function(err) {
+    do_throw("updates with addons should not reject");
+  });
+});
+
+/**
+ * Tests that installing found addons works as expected
+ */
+function test_checkForAddons_installAddon(id, includeSize,wantInstallReject) {
+  do_print("Running installAddon for includeSize: " + includeSize +
+           " and wantInstallReject: " + wantInstallReject);
+  let httpServer = new HttpServer();
+  let dir = FileUtils.getDir("TmpD", [], true);
+  httpServer.registerDirectory("/", dir);
+  httpServer.start(-1);
+  let testserverPort = httpServer.identity.primaryPort;
+  let zipFileName = "test_" + id + "_GMP.zip";
+
+  let zipURL = URL_HOST + ":" + testserverPort + "/" + zipFileName;
+  do_print("zipURL: " + zipURL);
+
+  let data = "e~=0.5772156649";
+  let zipFile = createNewZipFile(zipFileName, data);
+  let hashFunc = "sha256";
+  let expectedDigest = yield GMPDownloader.computeHash(hashFunc, zipFile);
+
+  let fileSize = zipFile.size;
+  if (wantInstallReject) {
+      fileSize = 1;
+  }
+
+  let responseXML =
+    "<?xml version=\"1.0\"?>" +
+    "<updates>" +
+    "    <addons>" +
+    "        <addon id=\"" + id + "-gmp-gmpopenh264\"" +
+    "               URL=\"" + zipURL + "\"" +
+    "               hashFunction=\"" + hashFunc + "\"" +
+    "               hashValue=\"" + expectedDigest + "\"" +
+    (includeSize ? " size=\"" + fileSize + "\"" : "") +
+    "               version=\"1.1\"/>" +
+    "  </addons>" +
+    "</updates>"
+
+  overrideXHR(200, responseXML);
+  let installManager = new GMPInstallManager();
+  let checkPromise = installManager.checkForAddons();
+  checkPromise.then(function(gmpAddons) {
+    do_check_eq(gmpAddons.length, 1);
+    let gmpAddon = gmpAddons[0];
+    do_check_false(gmpAddon.isInstalled);
+
+    GMPInstallManager.overrideLeaveDownloadedZip = true;
+    let installPromise = installManager.installAddon(gmpAddon);
+    installPromise.then(function(extractedPaths) {
+      if (wantInstallReject) {
+        do_throw("install update should reject");
+      }
+      do_check_eq(extractedPaths.length, 1);
+      let extractedPath = extractedPaths[0];
+
+      do_print("Extracted path: " + extractedPath);
+
+      let extractedFile = Cc["@mozilla.org/file/local;1"].
+                          createInstance(Ci.nsIFile);
+      extractedFile.initWithPath(extractedPath);
+      do_check_true(extractedFile.exists());
+      let readData = readStringFromFile(extractedFile);
+      do_check_eq(readData, data);
+
+      // Check that the downloaded zip mathces the offered zip exactly
+      let downloadedGMPFile = FileUtils.getFile("TmpD",
+        [gmpAddon.id + ".zip"]);
+      do_check_true(downloadedGMPFile.exists());
+      let downloadedBytes = getBinaryFileData(downloadedGMPFile);
+      let sourceBytes = getBinaryFileData(zipFile);
+      do_check_true(compareBinaryData(downloadedBytes, sourceBytes));
+
+      // Make sure the prefs are set correctly
+      do_check_true(!!GMPPrefs.get(GMPPrefs.KEY_ADDON_LAST_UPDATE,
+                                   gmpAddon.id, ""));
+      do_check_eq(GMPPrefs.get(GMPPrefs.KEY_ADDON_PATH, gmpAddon.id, ""),
+                               extractedFile.parent.path);
+      do_check_eq(GMPPrefs.get(GMPPrefs.KEY_ADDON_VERSION, gmpAddon.id, ""),
+                               "1.1");
+      // Make sure it reports as being installed
+      do_check_true(gmpAddon.isInstalled);
+
+      // Cleanup
+      extractedFile.parent.remove(true);
+      zipFile.remove(false);
+      httpServer.stop(function() {});
+      do_print("Removing downloaded GMP file: " + downloadedGMPFile.path);
+      downloadedGMPFile.remove(false);
+      installManager.uninit();
+    }, function(err) {
+      zipFile.remove(false);
+      let downloadedGMPFile = FileUtils.getFile("TmpD",
+        [gmpAddon.id + ".zip"]);
+      do_print("Removing from err downloaded GMP file: " +
+               downloadedGMPFile.path);
+      downloadedGMPFile.remove(false);
+      if (!wantInstallReject) {
+        do_throw("install update should not reject");
+      }
+    });
+  }, function(err) {
+    do_throw("checking updates to install them should not reject");
+  });
+}
+
+add_task(test_checkForAddons_installAddon.bind(null, "1", true, false));
+add_task(test_checkForAddons_installAddon.bind(null, "2", false, false));
+add_task(test_checkForAddons_installAddon.bind(null, "3", true, true));
+
+/**
+ * Tests that installing addons when there is no server works as expected
+ */
+add_test(function test_installAddon_noServer() {
+  let dir = FileUtils.getDir("TmpD", [], true);
+  let zipFileName = "test_GMP.zip";
+  let zipURL = URL_HOST + ":0/" + zipFileName;
+
+  let data = "e~=0.5772156649";
+  let zipFile = createNewZipFile(zipFileName, data);
+
+  let responseXML =
+    "<?xml version=\"1.0\"?>" +
+    "<updates>" +
+    "    <addons>" +
+    "        <addon id=\"gmp-gmpopenh264\"" +
+    "               URL=\"" + zipURL + "\"" +
+    "               hashFunction=\"sha256\"" +
+    "               hashValue=\"11221cbda000347b054028b527a60e578f919cb10f322ef8077d3491c6fcb474\"" +
+    "               version=\"1.1\"/>" +
+    "  </addons>" +
+    "</updates>"
+
+  overrideXHR(200, responseXML);
+  let installManager = new GMPInstallManager();
+  let checkPromise = installManager.checkForAddons();
+  checkPromise.then(function(gmpAddons) {
+    do_check_eq(gmpAddons.length, 1);
+    let gmpAddon= gmpAddons[0];
+
+    GMPInstallManager.overrideLeaveDownloadedZip = true;
+    let installPromise = installManager.installAddon(gmpAddon);
+    installPromise.then(function(extractedPaths) {
+        do_throw("No server for install should reject");
+    }, function(err) {
+      do_check_true(!!err);
+      installManager.uninit();
+      run_next_test();
+    });
+  }, function(err) {
+    do_throw("check should not reject for installn o server");
+  });
+});
+
+/**
+ * Returns the read stream into a string
+ */
+function readStringFromInputStream(inputStream) {
+  let sis = Cc["@mozilla.org/scriptableinputstream;1"].
+            createInstance(Ci.nsIScriptableInputStream);
+  sis.init(inputStream);
+  let text = sis.read(sis.available());
+  sis.close();
+  return text;
+}
+
+/**
+ * Reads a string of text from a file.
+ * This function only works with ASCII text.
+ */
+function readStringFromFile(file) {
+  if (!file.exists()) {
+    do_print("readStringFromFile - file doesn't exist: " + file.path);
+    return null;
+  }
+  let fis = Cc["@mozilla.org/network/file-input-stream;1"].
+            createInstance(Ci.nsIFileInputStream);
+  fis.init(file, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0);
+  return readStringFromInputStream(fis);
+}
+
+/**
+ * Bare bones XMLHttpRequest implementation for testing onprogress, onerror,
+ * and onload nsIDomEventListener handleEvent.
+ */
+function makeHandler(aVal) {
+  if (typeof aVal == "function")
+    return { handleEvent: aVal };
+  return aVal;
+}
+/**
+ * Constructs a mock xhr which is used for testing different aspects
+ * of responses.
+ */
+function xhr(inputStatus, inputResponse) {
+  this.inputStatus = inputStatus;
+  this.inputResponse = inputResponse;
+}
+xhr.prototype = {
+  overrideMimeType: function(aMimetype) { },
+  setRequestHeader: function(aHeader, aValue) { },
+  status: null,
+  channel: { set notificationCallbacks(aVal) { } },
+  _url: null,
+  _method: null,
+  open: function(aMethod, aUrl) {
+    this.channel.originalURI = Services.io.newURI(aUrl, null, null);
+    this._method = aMethod; this._url = aUrl;
+  },
+  abort: function() {
+  },
+  responseXML: null,
+  responseText: null,
+  send: function(aBody) {
+    let self = this;
+    do_execute_soon(function() {
+      self.status = self.inputStatus;
+      self.responseText = self.inputResponse;
+      try {
+        let parser = Cc["@mozilla.org/xmlextras/domparser;1"].
+                     createInstance(Ci.nsIDOMParser);
+        self.responseXML = parser.parseFromString(self.inputResponse,
+                                                  "application/xml");
+      } catch (e) {
+        self.responseXML = null;
+      }
+      let e = { target: self };
+      if (self.inputStatus === 200) {
+        self.onload(e);
+      } else {
+        self.onerror(e);
+      }
+    });
+  },
+  _onprogress: null,
+  set onprogress(aValue) { this._onprogress = makeHandler(aValue); },
+  get onprogress() { return this._onprogress; },
+  _onerror: null,
+  set onerror(aValue) { this._onerror = makeHandler(aValue); },
+  get onerror() { return this._onerror; },
+  _onload: null,
+  set onload(aValue) { this._onload = makeHandler(aValue); },
+  get onload() { return this._onload; },
+  addEventListener: function(aEvent, aValue, aCapturing) {
+    eval("this._on" + aEvent + " = aValue");
+  },
+  flags: Ci.nsIClassInfo.SINGLETON,
+  implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT,
+  getHelperForLanguage: function(aLanguage) null,
+  getInterfaces: function(aCount) {
+    let interfaces = [Ci.nsISupports];
+    aCount.value = interfaces.length;
+    return interfaces;
+  },
+  classDescription: "XMLHttpRequest",
+  contractID: "@mozilla.org/xmlextras/xmlhttprequest;1",
+  classID: Components.ID("{c9b37f43-4278-4304-a5e0-600991ab08cb}"),
+  createInstance: function(aOuter, aIID) {
+    if (aOuter == null)
+      return this.QueryInterface(aIID);
+    throw Cr.NS_ERROR_NO_AGGREGATION;
+  },
+  QueryInterface: function(aIID) {
+    if (aIID.equals(Ci.nsIClassInfo) ||
+        aIID.equals(Ci.nsISupports))
+      return this;
+    throw Cr.NS_ERROR_NO_INTERFACE;
+  },
+  get wrappedJSObject() { return this; }
+};
+
+/**
+ * Helper used to overrideXHR requests (no matter to what URL) with the
+ * specified status and response.
+ * @param status The status you want to get back when an XHR request is made
+ * @param response The response you want to get back when an XHR request is made
+ */
+function overrideXHR(status, response) {
+  let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+  if (overrideXHR.myxhr) {
+    registrar.unregisterFactory(overrideXHR.myxhr.classID, overrideXHR.myxhr);
+  }
+  overrideXHR.myxhr = new xhr(status, response);
+  registrar.registerFactory(overrideXHR.myxhr.classID,
+                            overrideXHR.myxhr.classDescription,
+                            overrideXHR.myxhr.contractID,
+                            overrideXHR.myxhr);
+}
+
+/**
+ * Compares binary data of 2 arrays and returns true if they are the same
+ *
+ * @param arr1 The first array to compare
+ * @param arr2 The second array to compare
+*/
+function compareBinaryData(arr1, arr2) {
+  do_check_eq(arr1.length, arr2.length);
+  for (let i = 0; i < arr1.length; i++) {
+    if (arr1[i] != arr2[i]) {
+      do_print("Data differs at index " + i +
+               ", arr1: " + arr1[i] + ", arr2: " + arr2[i]);
+      return false;
+    }
+  }
+  return true;
+}
+
+/**
+ * Reads a file's data and returns it
+ *
+ * @param file The file to read the data from
+ * @return array of bytes for the data in the file.
+*/
+function getBinaryFileData(file) {
+  let fileStream = Cc["@mozilla.org/network/file-input-stream;1"].
+                   createInstance(Ci.nsIFileInputStream);
+  // Open as RD_ONLY with default permissions.
+  fileStream.init(file, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0);
+
+  // Check the returned size versus the expected size.
+  let stream = Cc["@mozilla.org/binaryinputstream;1"].
+               createInstance(Ci.nsIBinaryInputStream);
+  stream.setInputStream(fileStream);
+  let bytes = stream.readByteArray(stream.available());
+  fileStream.close();
+  return bytes;
+}
+
+/**
+ * Creates a new zip file containing a file with the specified data
+ * @param zipName The name of the zip file
+ * @param data The data to go inside the zip for the filename entry1.info
+ */
+function createNewZipFile(zipName, data) {
+   // Create a zip file which will be used for extracting
+    let stream = Cc["@mozilla.org/io/string-input-stream;1"].
+                 createInstance(Ci.nsIStringInputStream);
+    stream.setData(data, data.length);
+    let zipWriter = Cc["@mozilla.org/zipwriter;1"].
+                    createInstance(Components.interfaces.nsIZipWriter);
+    let zipFile = FileUtils.getFile("TmpD", [zipName]);
+    if (zipFile.exists()) {
+      zipFile.remove(false);
+    }
+    // From prio.h
+    const PR_RDWR = 0x04;
+    const PR_CREATE_FILE = 0x08;
+    const PR_TRUNCATE    = 0x20;
+    zipWriter.open(zipFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
+    zipWriter.addEntryStream("entry1.info", Date.now(),
+                             Ci.nsIZipWriter.COMPRESSION_BEST, stream, false);
+    zipWriter.close();
+    stream.close();
+    do_print("zip file created on disk at: " + zipFile.path);
+    return zipFile;
+}
--- a/toolkit/modules/tests/xpcshell/xpcshell.ini
+++ b/toolkit/modules/tests/xpcshell/xpcshell.ini
@@ -8,16 +8,17 @@ support-files =
   zips/zen.zip
 
 [test_AsyncShutdown.js]
 [test_BinarySearch.js]
 [test_DeferredTask.js]
 [test_dict.js]
 [test_DirectoryLinksProvider.js]
 [test_FileUtils.js]
+[test_GMPInstallManager.js]
 [test_Http.js]
 [test_Log.js]
 [test_NewTabUtils.js]
 [test_PermissionsUtils.js]
 [test_Preferences.js]
 [test_Promise.js]
 [test_propertyListsUtils.js]
 [test_readCertPrefs.js]
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -3083,17 +3083,17 @@ var gDetailView = {
         document.getElementById("detail-error").textContent = gStrings.ext.formatStringFromName(
           "details.notification.vulnerableNoUpdate",
           [this._addon.name], 1
         );
         var errorLink = document.getElementById("detail-error-link");
         errorLink.value = gStrings.ext.GetStringFromName("details.notification.vulnerableNoUpdate.link");
         errorLink.href = this._addon.blocklistURL;
         errorLink.hidden = false;
-      } else if (this._addon.id == "openh264-plugin@cisco.com" && !this._addon.isInstalled) {
+      } else if (this._addon.id == "gmp-gmpopenh264" && !this._addon.isInstalled) {
         this.node.setAttribute("notification", "warning");
         let warning = document.getElementById("detail-warning");
         warning.textContent = gStrings.ext.GetStringFromName("details.notification.openH264Pending");
       } else {
         this.node.removeAttribute("notification");
       }
     }
 
--- a/toolkit/mozapps/extensions/content/extensions.xml
+++ b/toolkit/mozapps/extensions/content/extensions.xml
@@ -1289,17 +1289,17 @@
               this.setAttribute("notification", "error");
               this._error.textContent = gStrings.ext.formatStringFromName(
                 "notification.vulnerableNoUpdate",
                 [this.mAddon.name], 1
               );
               this._errorLink.value = gStrings.ext.GetStringFromName("notification.vulnerableNoUpdate.link");
               this._errorLink.href = this.mAddon.blocklistURL;
               this._errorLink.hidden = false;
-            } else if (this.mAddon.id == "openh264-plugin@cisco.com" && !this.mAddon.isInstalled) {
+            } else if (this.mAddon.id == "gmp-gmpopenh264" && !this.mAddon.isInstalled) {
               this.setAttribute("notification", "warning");
               this._warning.textContent = gStrings.ext.GetStringFromName("notification.openH264Pending");
             } else {
               this.removeAttribute("notification");
             }
           }
 
           this._preferencesBtn.hidden = (!this.mAddon.optionsURL) ||
--- a/toolkit/mozapps/extensions/internal/OpenH264Provider.jsm
+++ b/toolkit/mozapps/extensions/internal/OpenH264Provider.jsm
@@ -10,38 +10,66 @@ const Cu = Components.utils;
 
 this.EXPORTED_SYMBOLS = [];
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/AddonManager.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/osfile.jsm");
+Cu.import("resource://gre/modules/Log.jsm");
 
 const URI_EXTENSION_STRINGS    = "chrome://mozapps/locale/extensions/extensions.properties";
 const STRING_TYPE_NAME         = "type.%ID%.name";
 
-const OPENH264_PLUGIN_ID       = "openh264-plugin@cisco.com";
-const OPENH264_PREF_BRANCH     = "media.openh264.";
+const OPENH264_PLUGIN_ID       = "gmp-gmpopenh264";
+const OPENH264_PREF_BRANCH     = "media." + OPENH264_PLUGIN_ID + ".";
 const OPENH264_PREF_ENABLED    = "enabled";
 const OPENH264_PREF_PATH       = "path";
 const OPENH264_PREF_VERSION    = "version";
 const OPENH264_PREF_LASTUPDATE = "lastUpdate";
 const OPENH264_PREF_AUTOUPDATE = "autoupdate";
-const OPENH264_PREF_PROVIDERENABLED = "providerEnabled";
+const OPENH264_PREF_PROVIDERENABLED = "provider.enabled";
+const OPENH264_PREF_LOGGING    = "provider.logging";
+const OPENH264_PREF_LOGGING_LEVEL = OPENH264_PREF_LOGGING + ".level"; // media.gmp-gmpopenh264.provider.logging.level
+const OPENH264_PREF_LOGGING_DUMP = OPENH264_PREF_LOGGING + ".dump"; // media.gmp-gmpopenh264.provider.logging.dump
 const OPENH264_HOMEPAGE_URL    = "http://www.openh264.org/";
 const OPENH264_OPTIONS_URL     = "chrome://mozapps/content/extensions/openH264Prefs.xul";
 
 XPCOMUtils.defineLazyGetter(this, "pluginsBundle",
   () => Services.strings.createBundle("chrome://global/locale/plugins.properties"));
 XPCOMUtils.defineLazyGetter(this, "prefs",
   () => new Preferences(OPENH264_PREF_BRANCH));
 XPCOMUtils.defineLazyGetter(this, "gmpService",
   () => Cc["@mozilla.org/gecko-media-plugin-service;1"].getService(Ci.mozIGeckoMediaPluginService));
 
+let gLogger;
+let gLogDumping = false;
+let gLogAppenderDump = null;
+
+function configureLogging() {
+  if (!gLogger) {
+    gLogger = Log.repository.getLogger("Toolkit.OpenH264Provider");
+    gLogger.addAppender(new Log.ConsoleAppender(new Log.BasicFormatter()));
+  }
+  gLogger.level = prefs.get(OPENH264_PREF_LOGGING_LEVEL, Log.Level.Warn);
+
+  let logDumping = prefs.get(OPENH264_PREF_LOGGING_DUMP, false);
+  if (logDumping != gLogDumping) {
+    if (logDumping) {
+      gLogAppenderDump = new Log.DumpAppender(new Log.BasicFormatter());
+      gLogger.addAppender(gLogAppenderDump);
+    } else {
+      gLogger.removeAppender(gLogAppenderDump);
+      gLogAppenderDump = null;
+    }
+    gLogDumping = logDumping;
+  }
+}
+
 /**
  * The OpenH264Wrapper provides the info for the OpenH264 GMP plugin to public callers through the API.
  */
 
 let OpenH264Wrapper = Object.freeze({
   optionsType: AddonManager.OPTIONS_TYPE_INLINE,
   optionsURL: OPENH264_OPTIONS_URL,
 
@@ -58,17 +86,17 @@ let OpenH264Wrapper = Object.freeze({
       return prefs.get(OPENH264_PREF_VERSION, "");
     }
     return "";
   },
 
   get isActive() { return !this.userDisabled; },
   get appDisabled() { return false; },
 
-  get userDisabled() { return !prefs.get(OPENH264_PREF_ENABLED, false); },
+  get userDisabled() { return !prefs.get(OPENH264_PREF_ENABLED, true); },
   set userDisabled(aVal) { prefs.set(OPENH264_PREF_ENABLED, aVal === false); },
 
   get blocklistState() { return Ci.nsIBlocklistService.STATE_NOT_BLOCKED; },
   get size() { return 0; },
   get scope() { return AddonManager.SCOPE_APPLICATION; },
   get pendingOperations() { return AddonManager.PENDING_NONE; },
 
   get operationsRequiringRestart() { return AddonManager.OP_NEEDS_RESTART_NONE },
@@ -80,17 +108,17 @@ let OpenH264Wrapper = Object.freeze({
                                          AddonManager.PERM_CAN_DISABLE;
     }
     return permissions;
   },
 
   get updateDate() {
     let time = Number(prefs.get(OPENH264_PREF_LASTUPDATE, null));
     if (time !== NaN && this.isInstalled) {
-      return new Date(time)
+      return new Date(time * 1000)
     }
     return null;
   },
 
   get isCompatible() {
     return true;
   },
 
@@ -153,55 +181,76 @@ let OpenH264Wrapper = Object.freeze({
   get isInstalled() {
     let path = prefs.get(OPENH264_PREF_PATH, "");
     return path.length > 0;
   },
 });
 
 let OpenH264Provider = {
   startup: function() {
+    configureLogging();
+    this._log = Log.repository.getLogger("Toolkit.OpenH264Provider");
+    this.gmpPath = prefs.get(OPENH264_PREF_PATH, null);
+    let enabled = prefs.get(OPENH264_PREF_ENABLED, true);
+    this._log.trace("startup() - enabled=" + enabled + ", gmpPath="+this.gmpPath);
+
+
     Services.obs.addObserver(this, AddonManager.OPTIONS_NOTIFICATION_DISPLAYED, false);
     prefs.observe(OPENH264_PREF_ENABLED, this.onPrefEnabledChanged, this);
     prefs.observe(OPENH264_PREF_PATH, this.onPrefPathChanged, this);
+    prefs.observe(OPENH264_PREF_LOGGING, configureLogging);
 
-    this.gmpPath = prefs.get(OPENH264_PREF_PATH, null);
-    if (this.gmpPath) {
+    if (this.gmpPath && enabled) {
+      this._log.info("startup() - adding gmp directory " + this.gmpPath);
       gmpService.addPluginDirectory(this.gmpPath);
     }
   },
 
   shutdown: function() {
+    this._log.trace("shutdown()");
     Services.obs.removeObserver(this, AddonManager.OPTIONS_NOTIFICATION_DISPLAYED);
     prefs.ignore(OPENH264_PREF_ENABLED, this.onPrefEnabledChanged, this);
     prefs.ignore(OPENH264_PREF_PATH, this.onPrefPathChanged, this);
+    prefs.ignore(OPENH264_PREF_LOGGING, configureLogging);
   },
 
   onPrefEnabledChanged: function() {
     let wrapper = OpenH264Wrapper;
 
     AddonManagerPrivate.callAddonListeners(wrapper.isActive ?
                                            "onEnabling" : "onDisabling",
                                            wrapper, false);
+    if (this.gmpPath) {
+      if (wrapper.isActive) {
+        this._log.info("onPrefEnabledChanged() - adding gmp directory " + this.gmpPath);
+        gmpService.addPluginDirectory(this.gmpPath);
+      } else {
+        this._log.info("onPrefEnabledChanged() - removing gmp directory " + this.gmpPath);
+        gmpService.removePluginDirectory(this.gmpPath);
+      }
+    }
     AddonManagerPrivate.callAddonListeners(wrapper.isActive ?
                                            "onEnabled" : "onDisabled",
                                            wrapper);
   },
 
   onPrefPathChanged: function() {
     let wrapper = OpenH264Wrapper;
 
     AddonManagerPrivate.callAddonListeners("onUninstalling", wrapper, false);
     if (this.gmpPath) {
+      this._log.info("onPrefPathChanged() - removing gmp directory " + this.gmpPath);
       gmpService.removePluginDirectory(this.gmpPath);
     }
     AddonManagerPrivate.callAddonListeners("onUninstalled", wrapper);
 
     AddonManagerPrivate.callInstallListeners("onExternalInstall", null, wrapper, null, false);
     this.gmpPath = prefs.get(OPENH264_PREF_PATH, null);
-    if (this.gmpPath) {
+    if (this.gmpPath && wrapper.isActive) {
+      this._log.info("onPrefPathChanged() - adding gmp directory " + this.gmpPath);
       gmpService.addPluginDirectory(this.gmpPath);
     }
     AddonManagerPrivate.callAddonListeners("onInstalled", wrapper);
   },
 
   buildWrapper: function() {
     let description = pluginsBundle.GetStringFromName("openH264_description");
     return new OpenH264Wrapper(OPENH264_PLUGIN_ID,
--- a/toolkit/mozapps/extensions/test/browser/browser_openH264.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_openH264.js
@@ -1,21 +1,24 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 let {AddonTestUtils} = Components.utils.import("resource://testing-common/AddonManagerTesting.jsm", {});
 
-const OPENH264_PLUGIN_ID       = "openh264-plugin@cisco.com";
-const OPENH264_PREF_BRANCH     = "media.openh264.";
+const OPENH264_PLUGIN_ID       = "gmp-gmpopenh264";
+const OPENH264_PREF_BRANCH     = "media." + OPENH264_PLUGIN_ID + ".";
 const OPENH264_PREF_ENABLED    = OPENH264_PREF_BRANCH + "enabled";
 const OPENH264_PREF_PATH       = OPENH264_PREF_BRANCH + "path";
 const OPENH264_PREF_VERSION    = OPENH264_PREF_BRANCH + "version";
 const OPENH264_PREF_LASTUPDATE = OPENH264_PREF_BRANCH + "lastUpdate";
 const OPENH264_PREF_AUTOUPDATE = OPENH264_PREF_BRANCH + "autoupdate";
+const PREF_LOGGING             = OPENH264_PREF_BRANCH + "provider.logging";
+const PREF_LOGGING_LEVEL       = PREF_LOGGING + ".level";
+const PREF_LOGGING_DUMP        = PREF_LOGGING + ".dump";
 
 const TEST_DATE = new Date(2013, 0, 1, 12);
 
 let gManagerWindow;
 let gCategoryUtilities;
 let gIsEnUsLocale;
 
 let gOptionsObserver = {
@@ -51,27 +54,32 @@ function openDetailsView(aId) {
   EventUtils.synthesizeMouseAtCenter(item, { clickCount: 2 }, gManagerWindow);
 
   let deferred = Promise.defer();
   wait_for_view_load(gManagerWindow, deferred.resolve);
   return deferred.promise;
 }
 
 add_task(function* initializeState() {
+  Services.prefs.setBoolPref(PREF_LOGGING_DUMP, true);
+  Services.prefs.setIntPref(PREF_LOGGING_LEVEL, 0);
+
   gManagerWindow = yield open_manager();
   gCategoryUtilities = new CategoryUtilities(gManagerWindow);
 
   registerCleanupFunction(() => {
     Services.obs.removeObserver(gOptionsObserver, AddonManager.OPTIONS_NOTIFICATION_DISPLAYED);
 
     Services.prefs.clearUserPref(OPENH264_PREF_ENABLED);
     Services.prefs.clearUserPref(OPENH264_PREF_PATH);
     Services.prefs.clearUserPref(OPENH264_PREF_VERSION);
     Services.prefs.clearUserPref(OPENH264_PREF_LASTUPDATE);
     Services.prefs.clearUserPref(OPENH264_PREF_AUTOUPDATE);
+    Services.prefs.clearUserPref(PREF_LOGGING_DUMP);
+    Services.prefs.clearUserPref(PREF_LOGGING_LEVEL);
   });
 
   let chrome = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry);
   gIsEnUsLocale = chrome.getSelectedLocale("global") == "en-US";
 
   Services.obs.addObserver(gOptionsObserver, AddonManager.OPTIONS_NOTIFICATION_DISPLAYED, false);
 
   // Start out with OpenH264 not being installed, disabled and automatic updates disabled.
--- a/toolkit/mozapps/extensions/test/xpcshell/test_duplicateplugins.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_duplicateplugins.js
@@ -97,17 +97,17 @@ registrar.registerFactory(Components.ID(
                           "Fake Plugin Host",
                           "@mozilla.org/plugin/host;1", PluginHostFactory);
 
 var gPluginIDs = [null, null, null, null, null];
 
 function run_test() {
   do_test_pending();
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
-  Services.prefs.setBoolPref("media.openh264.providerEnabled", false);
+  Services.prefs.setBoolPref("media.gmp-gmpopenh264.provider.enabled", false);
 
   startupManager();
 
   run_test_1();
 }
 
 function found_plugin(aNum, aId) {
   if (gPluginIDs[aNum])
--- a/toolkit/mozapps/extensions/test/xpcshell/test_openh264.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_openh264.js
@@ -1,37 +1,42 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
-const OPENH264_PLUGIN_ID       = "openh264-plugin@cisco.com";
-const OPENH264_PREF_BRANCH     = "media.openh264.";
+const OPENH264_PLUGIN_ID       = "gmp-gmpopenh264";
+const OPENH264_PREF_BRANCH     = "media." + OPENH264_PLUGIN_ID + ".";
 const OPENH264_PREF_ENABLED    = OPENH264_PREF_BRANCH + "enabled";
 const OPENH264_PREF_PATH       = OPENH264_PREF_BRANCH + "path";
 const OPENH264_PREF_VERSION    = OPENH264_PREF_BRANCH + "version";
 const OPENH264_PREF_LASTUPDATE = OPENH264_PREF_BRANCH + "lastUpdate";
 const OPENH264_PREF_AUTOUPDATE = OPENH264_PREF_BRANCH + "autoupdate";
+const PREF_LOGGING             = OPENH264_PREF_BRANCH + "provider.logging";
+const PREF_LOGGING_LEVEL       = PREF_LOGGING + ".level";
+const PREF_LOGGING_DUMP        = PREF_LOGGING + ".dump";
 
 XPCOMUtils.defineLazyGetter(this, "pluginsBundle",
   () => Services.strings.createBundle("chrome://global/locale/plugins.properties"));
 
 let gProfileDir = null;
 
 function run_test() {
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
   startupManager();
   run_next_test();
 }
 
 add_task(function* test_notInstalled() {
   Services.prefs.setCharPref(OPENH264_PREF_PATH, "");
   Services.prefs.setBoolPref(OPENH264_PREF_ENABLED, false);
+  Services.prefs.setBoolPref(PREF_LOGGING_DUMP, true);
+  Services.prefs.setIntPref(PREF_LOGGING_LEVEL, 0);
 
   let addons = yield promiseAddonsByIDs([OPENH264_PLUGIN_ID]);
   Assert.equal(addons.length, 1);
   let addon = addons[0];
 
   Assert.ok(!addon.isInstalled);
   Assert.equal(addon.type, "plugin");
   Assert.equal(addon.version, "");
@@ -68,23 +73,24 @@ add_task(function* test_notInstalled() {
   Assert.ok(libraries);
   Assert.equal(libraries.length, 0);
   Assert.equal(addon.pluginFullpath, "");
 });
 
 add_task(function* test_installed() {
   const TEST_DATE = new Date(2013, 0, 1, 12);
   const TEST_VERSION = "1.2.3.4";
+  const TEST_TIME_SEC = Math.round(TEST_DATE.getTime() / 1000);
 
   let file = Services.dirsvc.get("ProfD", Ci.nsIFile);
   file.append("openh264");
   file.append("testDir");
 
   Services.prefs.setBoolPref(OPENH264_PREF_ENABLED, false);
-  Services.prefs.setCharPref(OPENH264_PREF_LASTUPDATE, "" + TEST_DATE.getTime());
+  Services.prefs.setCharPref(OPENH264_PREF_LASTUPDATE, "" + TEST_TIME_SEC);
   Services.prefs.setCharPref(OPENH264_PREF_VERSION, TEST_VERSION);
   Services.prefs.setCharPref(OPENH264_PREF_PATH, file.path);
 
   let addons = yield promiseAddonsByIDs([OPENH264_PLUGIN_ID]);
   Assert.equal(addons.length, 1);
   let addon = addons[0];
 
   Assert.ok(addon.isInstalled);
@@ -95,17 +101,17 @@ add_task(function* test_installed() {
 
   Assert.ok(!addon.isActive);
   Assert.ok(!addon.appDisabled);
   Assert.ok(addon.userDisabled);
 
   Assert.equal(addon.permissions, AddonManager.PERM_CAN_UPGRADE |
                                   AddonManager.PERM_CAN_ENABLE);
 
-  Assert.equal(addon.updateDate.getTime(), TEST_DATE.getTime());
+  Assert.equal(addon.updateDate.getTime(), TEST_TIME_SEC * 1000);
 
   let mimetypes = addon.pluginMimeTypes;
   Assert.ok(mimetypes);
   Assert.equal(mimetypes.length, 0);
   let libraries = addon.pluginLibraries;
   Assert.ok(libraries);
   Assert.equal(libraries.length, 1);
   Assert.equal(libraries[0], "testDir");
@@ -158,16 +164,17 @@ add_task(function* test_pluginRegistrati
 
   let MockGMPService = {
     addPluginDirectory: path => addedPath = path,
     removePluginDirectory: path => removedPath = path,
   };
 
   let OpenH264Scope = Cu.import("resource://gre/modules/addons/OpenH264Provider.jsm");
   OpenH264Scope.gmpService = MockGMPService;
+  Services.prefs.setBoolPref(OPENH264_PREF_ENABLED, true);
 
   // Check that the OpenH264 plugin gets registered after startup.
   Services.prefs.setCharPref(OPENH264_PREF_PATH, file.path);
   clearPaths();
   yield promiseRestartManager();
   Assert.equal(addedPath, file.path);
   Assert.equal(removedPath, null);
 
@@ -186,9 +193,28 @@ add_task(function* test_pluginRegistrati
   // Changing the pref mid-session should cause unregistration and registration.
   Services.prefs.setCharPref(OPENH264_PREF_PATH, file.path);
   clearPaths();
   let file2 = file.clone();
   file2.append("foo");
   Services.prefs.setCharPref(OPENH264_PREF_PATH, file2.path);
   Assert.equal(addedPath, file2.path);
   Assert.equal(removedPath, file.path);
+
+  // Disabling OpenH264 should cause unregistration.
+  Services.prefs.setCharPref(OPENH264_PREF_PATH, file.path);
+  clearPaths();
+  Services.prefs.setBoolPref(OPENH264_PREF_ENABLED, false);
+  Assert.equal(addedPath, null);
+  Assert.equal(removedPath, file.path);
+
+  // Restarting with OpenH264 disabled should not cause registration.
+  clearPaths();
+  yield promiseRestartManager();
+  Assert.equal(addedPath, null);
+  Assert.equal(removedPath, null);
+
+  // Re-enabling OpenH264 should cause registration.
+  clearPaths();
+  Services.prefs.setBoolPref(OPENH264_PREF_ENABLED, true);
+  Assert.equal(addedPath, file.path);
+  Assert.equal(removedPath, null);
 });
--- a/toolkit/mozapps/extensions/test/xpcshell/test_pluginchange.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_pluginchange.js
@@ -62,17 +62,17 @@ registrar.registerFactory(Components.ID(
                           "@mozilla.org/plugin/host;1", PluginHostFactory);
 
 // This verifies that when the list of plugins changes the add-ons manager
 // correctly updates
 function run_test() {
   do_test_pending();
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
 
-  Services.prefs.setBoolPref("media.openh264.providerEnabled", false);
+  Services.prefs.setBoolPref("media.gmp-gmpopenh264.provider.enabled", false);
 
   startupManager();
   AddonManager.addAddonListener(AddonListener);
   AddonManager.addInstallListener(InstallListener);
 
   run_test_1();
 }