Merge m-c to inbound
authorWes Kocher <wkocher@mozilla.com>
Wed, 23 Jul 2014 18:55:48 -0700
changeset 195811 f654e6bf1089820f000b61fcb5fc3099d17bffe9
parent 195810 9c3d8f8b46f7cddc778420daa47002afd708a7c1 (current diff)
parent 195764 a91ec42d6a9cd718673372a2c70bfca7903c3ca6 (diff)
child 195812 3a5cfc71b3d30980ccd42535d9a001ad5410d8b4
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
milestone34.0a1
Merge m-c to inbound
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,23 +14,23 @@
   <!--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="15c84c943e41ad834640a45e1e1c2ac804168af7"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="5458f73e319759543fddf7e96d7ece4d78318e32"/>
   <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="04ffbed6a18a2085a27bc113034fe71b8d10c10e"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="11f01789444d4ebe97581c31d8756d773e18356f"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="425f8b5fadf5889834c5acd27d23c9e0b2129c28"/>
   <project name="device/common" path="device/common" revision="42b808b7e93d0619286ae8e59110b176b7732389"/>
   <project name="device/sample" path="device/sample" revision="237bd668d0f114d801a8d6455ef5e02cc3577587"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
   <project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="52a1a862a8bac319652b8f82d9541ba40bfa45ce"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,20 +12,20 @@
   <!--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="3aa6abd313f965a84aa86c6b213dc154e4875139">
     <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="15c84c943e41ad834640a45e1e1c2ac804168af7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="5458f73e319759543fddf7e96d7ece4d78318e32"/>
   <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="04ffbed6a18a2085a27bc113034fe71b8d10c10e"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="11f01789444d4ebe97581c31d8756d773e18356f"/>
   <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"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,25 +10,25 @@
   <!--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="999e945b85c578c503ad445c2285940f16aacdae">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="15c84c943e41ad834640a45e1e1c2ac804168af7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="5458f73e319759543fddf7e96d7ece4d78318e32"/>
   <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="04ffbed6a18a2085a27bc113034fe71b8d10c10e"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="11f01789444d4ebe97581c31d8756d773e18356f"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="1950e4760fa14688b83cdbb5acaa1af9f82ef434"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="ac6eb97a37035c09fb5ede0852f0881e9aadf9ad"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="737f591c5f95477148d26602c7be56cbea0cdeb9"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="51da9b1981be481b92a59a826d4d78dc73d0989a"/>
   <project name="device/common" path="device/common" revision="798a3664597e6041985feab9aef42e98d458bc3d"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,23 +14,23 @@
   <!--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="15c84c943e41ad834640a45e1e1c2ac804168af7"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="5458f73e319759543fddf7e96d7ece4d78318e32"/>
   <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="04ffbed6a18a2085a27bc113034fe71b8d10c10e"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="11f01789444d4ebe97581c31d8756d773e18356f"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="425f8b5fadf5889834c5acd27d23c9e0b2129c28"/>
   <project name="device/common" path="device/common" revision="42b808b7e93d0619286ae8e59110b176b7732389"/>
   <project name="device/sample" path="device/sample" revision="237bd668d0f114d801a8d6455ef5e02cc3577587"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
   <project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="52a1a862a8bac319652b8f82d9541ba40bfa45ce"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,20 +12,20 @@
   <!--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="3aa6abd313f965a84aa86c6b213dc154e4875139">
     <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="15c84c943e41ad834640a45e1e1c2ac804168af7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="5458f73e319759543fddf7e96d7ece4d78318e32"/>
   <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="04ffbed6a18a2085a27bc113034fe71b8d10c10e"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="11f01789444d4ebe97581c31d8756d773e18356f"/>
   <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"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "git_revision": "", 
         "remote": "", 
         "branch": ""
     }, 
-    "revision": "a9373a0a5aa0a03e259bb25765e350cf2e9e1a77", 
+    "revision": "2a39622b30294c36997f9b8260230182344184de", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,22 +12,22 @@
   <!--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="15c84c943e41ad834640a45e1e1c2ac804168af7"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="5458f73e319759543fddf7e96d7ece4d78318e32"/>
   <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="04ffbed6a18a2085a27bc113034fe71b8d10c10e"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="11f01789444d4ebe97581c31d8756d773e18356f"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="746bc48f34f5060f90801925dcdd964030c1ab6d"/>
   <project name="platform/development" path="development" revision="2460485184bc8535440bb63876d4e63ec1b4770c"/>
   <project name="device/common" path="device/common" revision="0dcc1e03659db33b77392529466f9eb685cdd3c7"/>
   <project name="device/sample" path="device/sample" revision="68b1cb978a20806176123b959cb05d4fa8adaea4"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
--- 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="15c84c943e41ad834640a45e1e1c2ac804168af7"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="5458f73e319759543fddf7e96d7ece4d78318e32"/>
   <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,20 +12,20 @@
   <!--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="3aa6abd313f965a84aa86c6b213dc154e4875139">
     <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="15c84c943e41ad834640a45e1e1c2ac804168af7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="5458f73e319759543fddf7e96d7ece4d78318e32"/>
   <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="04ffbed6a18a2085a27bc113034fe71b8d10c10e"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="11f01789444d4ebe97581c31d8756d773e18356f"/>
   <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"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,22 +12,22 @@
   <!--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="15c84c943e41ad834640a45e1e1c2ac804168af7"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="5458f73e319759543fddf7e96d7ece4d78318e32"/>
   <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="04ffbed6a18a2085a27bc113034fe71b8d10c10e"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="11f01789444d4ebe97581c31d8756d773e18356f"/>
   <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="cd5dfce80bc3f0139a56b58aca633202ccaee7f8"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="e0a9ac010df3afaa47ba107192c05ac8b5516435"/>
   <project name="platform/development" path="development" revision="a384622f5fcb1d2bebb9102591ff7ae91fe8ed2d"/>
   <project name="device/common" path="device/common" revision="7c65ea240157763b8ded6154a17d3c033167afb7"/>
   <project name="device/sample" path="device/sample" revision="c328f3d4409db801628861baa8d279fb8855892f"/>
--- a/browser/components/loop/MozLoopAPI.jsm
+++ b/browser/components/loop/MozLoopAPI.jsm
@@ -28,49 +28,46 @@ function injectLoopAPI(targetWindow) {
   let ringerStopper;
 
   let api = {
     /**
      * Sets and gets the "do not disturb" mode activation flag.
      */
     doNotDisturb: {
       enumerable: true,
-      configurable: true,
       get: function() {
         return MozLoopService.doNotDisturb;
       },
       set: function(aFlag) {
         MozLoopService.doNotDisturb = aFlag;
       }
     },
 
     /**
      * Returns the current locale of the browser.
      *
      * @returns {String} The locale string
      */
     locale: {
       enumerable: true,
-      configurable: true,
       get: function() {
         return MozLoopService.locale;
       }
     },
 
     /**
      * Returns translated strings associated with an element. Designed
      * for use with l10n.js
      *
      * @param {String} key The element id
      * @returns {Object} A JSON string containing the localized
      *                   attribute/value pairs for the element.
      */
     getStrings: {
       enumerable: true,
-      configurable: true,
       writable: true,
       value: function(key) {
         return MozLoopService.getStrings(key);
       }
     },
 
     /**
      * Call to ensure that any necessary registrations for the Loop Service
@@ -80,17 +77,16 @@ function injectLoopAPI(targetWindow) {
      * - err null on successful registration, non-null otherwise.
      *
      * @param {Function} callback Will be called once registration is complete,
      *                            or straight away if registration has already
      *                            happened.
      */
     ensureRegistered: {
       enumerable: true,
-      configurable: true,
       writable: true,
       value: function(callback) {
         // We translate from a promise to a callback, as we can't pass promises from
         // Promise.jsm across the priv versus unpriv boundary.
         return MozLoopService.register().then(() => {
           callback(null);
         }, err => {
           callback(err);
@@ -107,17 +103,16 @@ function injectLoopAPI(targetWindow) {
      * This is used to determine whether or not we should be registering with the
      * push server on start.
      *
      * @param {Integer} expiryTimeSeconds The seconds since epoch of the expiry time
      *                                    of the url.
      */
     noteCallUrlExpiry: {
       enumerable: true,
-      configurable: true,
       writable: true,
       value: function(expiryTimeSeconds) {
         MozLoopService.noteCallUrlExpiry(expiryTimeSeconds);
       }
     },
 
     /**
      * Set any character preference under "loop."
@@ -125,17 +120,16 @@ function injectLoopAPI(targetWindow) {
      * @param {String} prefName The name of the pref without the preceding "loop."
      * @param {String} stringValue The value to set.
      *
      * Any errors thrown by the Mozilla pref API are logged to the console
      * and cause false to be returned.
      */
     setLoopCharPref: {
       enumerable: true,
-      configurable: true,
       writable: true,
       value: function(prefName, value) {
         MozLoopService.setLoopCharPref(prefName, value);
       }
     },
 
     /**
      * Return any preference under "loop." that's coercible to a character
@@ -147,29 +141,27 @@ function injectLoopAPI(targetWindow) {
      * Any errors thrown by the Mozilla pref API are logged to the console
      * and cause null to be returned. This includes the case of the preference
      * not being found.
      *
      * @return {String} on success, null on error
      */
     getLoopCharPref: {
       enumerable: true,
-      configurable: true,
       writable: true,
       value: function(prefName) {
         return MozLoopService.getLoopCharPref(prefName);
       }
     },
 
     /**
      * Starts alerting the user about an incoming call
      */
     startAlerting: {
       enumerable: true,
-      configurable: true,
       writable: true,
       value: function() {
         let chromeWindow = getChromeWindow(targetWindow);
         chromeWindow.getAttention();
         ringer = new chromeWindow.Audio();
         ringer.src = Services.prefs.getCharPref("loop.ringtone");
         ringer.loop = true;
         ringer.load();
@@ -183,17 +175,16 @@ function injectLoopAPI(targetWindow) {
       }
     },
 
     /**
      * Stops alerting the user about an incoming call
      */
     stopAlerting: {
       enumerable: true,
-      configurable: true,
       writable: true,
       value: function() {
         if (ringerStopper) {
           targetWindow.document.removeEventListener("visibilitychange",
                                                     ringerStopper);
           ringerStopper = null;
         }
         if (ringer) {
@@ -219,31 +210,31 @@ function injectLoopAPI(targetWindow) {
      * @param {String} path The path to make the request to.
      * @param {String} method The request method, e.g. 'POST', 'GET'.
      * @param {Object} payloadObj An object which is converted to JSON and
      *                            transmitted with the request.
      * @param {Function} callback Called when the request completes.
      */
     hawkRequest: {
       enumerable: true,
-      configurable: true,
       writable: true,
       value: function(path, method, payloadObj, callback) {
         // XXX Should really return a DOM promise here.
         return MozLoopService.hawkRequest(path, method, payloadObj).then((response) => {
           callback(null, response.body);
         }, (error) => {
           callback(Cu.cloneInto(error, targetWindow));
         });
       }
     },
   };
 
   let contentObj = Cu.createObjectIn(targetWindow);
   Object.defineProperties(contentObj, api);
+  Object.seal(contentObj);
   Cu.makeObjectPropsNormal(contentObj);
 
   targetWindow.navigator.wrappedJSObject.__defineGetter__("mozLoop", function() {
     // We do this in a getter, so that we create these objects
     // only on demand (this is a potential concern, since
     // otherwise we might add one per iframe, and keep them
     // alive for as long as the window is alive).
     delete targetWindow.navigator.wrappedJSObject.mozLoop;
--- a/browser/components/loop/MozLoopService.jsm
+++ b/browser/components/loop/MozLoopService.jsm
@@ -42,31 +42,36 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 
 XPCOMUtils.defineLazyModuleGetter(this, "MozLoopPushHandler",
                                   "resource:///modules/loop/MozLoopPushHandler.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
                                    "@mozilla.org/uuid-generator;1",
                                    "nsIUUIDGenerator");
 
+// The current deferred for the registration process. This is set if in progress
+// or the registration was successful. This is null if a registration attempt was
+// unsuccessful.
+let gRegisteredDeferred = null;
+let gPushHandler = null;
+let gHawkClient = null;
+let gRegisteredLoopServer = false;
+let gLocalizedStrings =  null;
+let gInitializeTimer = null;
+
 /**
  * Internal helper methods and state
  *
  * The registration is a two-part process. First we need to connect to
  * and register with the push server. Then we need to take the result of that
  * and register with the Loop server.
  */
 let MozLoopServiceInternal = {
   // The uri of the Loop server.
-  loopServerUri: Services.prefs.getCharPref("loop.server"),
-
-  // The current deferred for the registration process. This is set if in progress
-  // or the registration was successful. This is null if a registration attempt was
-  // unsuccessful.
-  _registeredDeferred: null,
+  get loopServerUri() Services.prefs.getCharPref("loop.server"),
 
   /**
    * The initial delay for push registration. This ensures we don't start
    * kicking off straight after browser startup, just a few seconds later.
    */
   get initialRegistrationDelayMilliseconds() {
     try {
       // Let a pref override this for developer & testing use.
@@ -132,28 +137,28 @@ let MozLoopServiceInternal = {
    * with the Loop server. It will return early if already registered.
    *
    * @param {Object} mockPushHandler Optional, test-only mock push handler. Used
    *                                 to allow mocking of the MozLoopPushHandler.
    * @returns {Promise} a promise that is resolved with no params on completion, or
    *          rejected with an error code or string.
    */
   promiseRegisteredWithServers: function(mockPushHandler) {
-    if (this._registeredDeferred) {
-      return this._registeredDeferred.promise;
+    if (gRegisteredDeferred) {
+      return gRegisteredDeferred.promise;
     }
 
-    this._registeredDeferred = Promise.defer();
+    gRegisteredDeferred = Promise.defer();
     // We grab the promise early in case .initialize or its results sets
     // it back to null on error.
-    let result = this._registeredDeferred.promise;
+    let result = gRegisteredDeferred.promise;
 
-    this._pushHandler = mockPushHandler || MozLoopPushHandler;
+    gPushHandler = mockPushHandler || MozLoopPushHandler;
 
-    this._pushHandler.initialize(this.onPushRegistered.bind(this),
+    gPushHandler.initialize(this.onPushRegistered.bind(this),
       this.onHandleNotification.bind(this));
 
     return result;
   },
 
   /**
    * Performs a hawk based request to the loop server.
    *
@@ -163,35 +168,35 @@ let MozLoopServiceInternal = {
    *                            transmitted with the request.
    * @returns {Promise}
    *        Returns a promise that resolves to the response of the API call,
    *        or is rejected with an error.  If the server response can be parsed
    *        as JSON and contains an 'error' property, the promise will be
    *        rejected with this JSON-parsed response.
    */
   hawkRequest: function(path, method, payloadObj) {
-    if (!this._hawkClient) {
-      this._hawkClient = new HawkClient(this.loopServerUri);
+    if (!gHawkClient) {
+      gHawkClient = new HawkClient(this.loopServerUri);
     }
 
     let sessionToken;
     try {
       sessionToken = Services.prefs.getCharPref("loop.hawk-session-token");
     } catch (x) {
       // It is ok for this not to exist, we'll default to sending no-creds
     }
 
     let credentials;
     if (sessionToken) {
       // true = use a hex key, as required by the server (see bug 1032738).
       credentials = deriveHawkCredentials(sessionToken, "sessionToken",
                                           2 * 32, true);
     }
 
-    return this._hawkClient.request(path, method, credentials, payloadObj);
+    return gHawkClient.request(path, method, credentials, payloadObj);
   },
 
   /**
    * Used to store a session token from a request if it exists in the headers.
    *
    * @param {Object} headers The request headers, which may include a
    *                         "hawk-session-token" to be saved.
    * @return true on success or no token, false on failure.
@@ -200,57 +205,56 @@ let MozLoopServiceInternal = {
     let sessionToken = headers["hawk-session-token"];
     if (sessionToken) {
       // XXX should do more validation here
       if (sessionToken.length === 64) {
         Services.prefs.setCharPref("loop.hawk-session-token", sessionToken);
       } else {
         // XXX Bubble the precise details up to the UI somehow (bug 1013248).
         console.warn("Loop server sent an invalid session token");
-        this._registeredDeferred.reject("session-token-wrong-size");
-        this._registeredDeferred = null;
+        gRegisteredDeferred.reject("session-token-wrong-size");
+        gRegisteredDeferred = null;
         return false;
       }
     }
     return true;
   },
 
   /**
    * Callback from MozLoopPushHandler - The push server has been registered
    * and has given us a push url.
    *
    * @param {String} pushUrl The push url given by the push server.
    */
   onPushRegistered: function(err, pushUrl) {
     if (err) {
-      this._registeredDeferred.reject(err);
-      this._registeredDeferred = null;
+      gRegisteredDeferred.reject(err);
+      gRegisteredDeferred = null;
       return;
     }
 
     this.registerWithLoopServer(pushUrl);
   },
 
   /**
    * Registers with the Loop server.
    *
    * @param {String} pushUrl The push url given by the push server.
    * @param {Boolean} noRetry Optional, don't retry if authentication fails.
    */
   registerWithLoopServer: function(pushUrl, noRetry) {
     this.hawkRequest("/registration", "POST", { simple_push_url: pushUrl})
       .then((response) => {
         // If this failed we got an invalid token. storeSessionToken rejects
-        // the _registeredDeferred promise for us, so here we just need to
+        // the gRegisteredDeferred promise for us, so here we just need to
         // early return.
         if (!this.storeSessionToken(response.headers))
           return;
 
-        this.registeredLoopServer = true;
-        this._registeredDeferred.resolve();
+        gRegisteredDeferred.resolve();
         // No need to clear the promise here, everything was good, so we don't need
         // to re-register.
       }, (error) => {
         // There's other errors than invalid auth token, but we should only do the reset
         // as a last resort.
         if (error.code === 401 && error.errno === INVALID_AUTH_TOKEN) {
           if (this.urlExpiryTimeIsInFuture()) {
             // XXX Should this be reported to the user is a visible manner?
@@ -261,18 +265,18 @@ let MozLoopServiceInternal = {
           // Authorization failed, invalid token, we need to try again with a new token.
           Services.prefs.clearUserPref("loop.hawk-session-token");
           this.registerWithLoopServer(pushUrl, true);
           return;
         }
 
         // XXX Bubble the precise details up to the UI somehow (bug 1013248).
         Cu.reportError("Failed to register with the loop server. error: " + error);
-        this._registeredDeferred.reject(error.errno);
-        this._registeredDeferred = null;
+        gRegisteredDeferred.reject(error.errno);
+        gRegisteredDeferred = null;
       }
     );
   },
 
   /**
    * Callback from MozLoopPushHandler - A push notification has been received from
    * the server.
    *
@@ -288,18 +292,18 @@ let MozLoopServiceInternal = {
 
   /**
    * A getter to obtain and store the strings for loop. This is structured
    * for use by l10n.js.
    *
    * @returns {Object} a map of element ids with attributes to set.
    */
   get localizedStrings() {
-    if (this._localizedStrings)
-      return this._localizedStrings;
+    if (gLocalizedStrings)
+      return gLocalizedStrings;
 
     var stringBundle =
       Services.strings.createBundle('chrome://browser/locale/loop/loop.properties');
 
     var map = {};
     var enumerator = stringBundle.getSimpleEnumeration();
     while (enumerator.hasMoreElements()) {
       var string = enumerator.getNext().QueryInterface(Ci.nsIPropertyElement);
@@ -311,17 +315,17 @@ let MozLoopServiceInternal = {
         property = key.substring(i + 1);
         key = key.substring(0, i);
       }
       if (!(key in map))
         map[key] = {};
       map[key][property] = string.value;
     }
 
-    return this._localizedStrings = map;
+    return gLocalizedStrings = map;
   },
 
   /**
    * Saves loop logs to the saved-telemetry-pings folder.
    *
    * @param {Object} pc The peerConnection in question.
    */
   stageForTelemetryUpload: function(window, pc) {
@@ -440,54 +444,55 @@ let MozLoopServiceInternal = {
         let pc_static = new window.mozRTCPeerConnectionStatic();
         pc_static.registerPeerConnectionLifecycleCallback(onPCLifecycleChange);
       }.bind(this), true);
     };
 
     Chat.open(contentWindow, origin, title, url, undefined, undefined, callback);
   }
 };
+Object.freeze(MozLoopServiceInternal);
+
+let gInitializeTimerFunc = () => {
+  // Kick off the push notification service into registering after a timeout
+  // this ensures we're not doing too much straight after the browser's finished
+  // starting up.
+  gInitializeTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+  gInitializeTimer.initWithCallback(() => {
+    MozLoopService.register();
+    gInitializeTimer = null;
+  },
+  MozLoopServiceInternal.initialRegistrationDelayMilliseconds, Ci.nsITimer.TYPE_ONE_SHOT);
+};
 
 /**
  * Public API
  */
 this.MozLoopService = {
+  set initializeTimerFunc(value) {
+    gInitializeTimerFunc = value;
+  },
+
   /**
    * Initialized the loop service, and starts registration with the
    * push and loop servers.
    */
   initialize: function() {
     // Don't do anything if loop is not enabled.
     if (!Services.prefs.getBoolPref("loop.enabled")) {
       return;
     }
 
     // If expiresTime is in the future then kick-off registration.
     if (MozLoopServiceInternal.urlExpiryTimeIsInFuture()) {
-      this._startInitializeTimer();
+      gInitializeTimerFunc();
     }
   },
 
   /**
-   * Internal function, exposed for testing purposes only. Used to start the
-   * initialize timer.
-   */
-  _startInitializeTimer: function() {
-    // Kick off the push notification service into registering after a timeout
-    // this ensures we're not doing too much straight after the browser's finished
-    // starting up.
-    this._initializeTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-    this._initializeTimer.initWithCallback(function() {
-      this.register();
-      this._initializeTimer = null;
-    }.bind(this),
-    MozLoopServiceInternal.initialRegistrationDelayMilliseconds, Ci.nsITimer.TYPE_ONE_SHOT);
-  },
-
-  /**
    * Starts registration of Loop with the push server, and then will register
    * with the Loop server. It will return early if already registered.
    *
    * @param {Object} mockPushHandler Optional, test-only mock push handler. Used
    *                                 to allow mocking of the MozLoopPushHandler.
    * @returns {Promise} a promise that is resolved with no params on completion, or
    *          rejected with an error code or string.
    */
@@ -618,8 +623,9 @@ this.MozLoopService = {
    *        or is rejected with an error.  If the server response can be parsed
    *        as JSON and contains an 'error' property, the promise will be
    *        rejected with this JSON-parsed response.
    */
   hawkRequest: function(path, method, payloadObj) {
     return MozLoopServiceInternal.hawkRequest(path, method, payloadObj);
   },
 };
+Object.freeze(this.MozLoopService);
--- a/browser/components/loop/test/xpcshell/test_loopservice_initialize.js
+++ b/browser/components/loop/test/xpcshell/test_loopservice_initialize.js
@@ -47,14 +47,14 @@ add_task(function test_initialize_starts
   Assert.equal(startTimerCalled, true,
     "should start the timer when expiry time is in the future");
 });
 
 function run_test()
 {
   // Override MozLoopService's initializeTimer, so that we can verify the timeout is called
   // correctly.
-  MozLoopService._startInitializeTimer = function() {
+  MozLoopService.initializeTimerFunc = function() {
     startTimerCalled = true;
   };
 
   run_next_test();
 }
--- a/browser/components/preferences/in-content/subdialogs.js
+++ b/browser/components/preferences/in-content/subdialogs.js
@@ -3,23 +3,25 @@
    - You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 let gSubDialog = {
   _closingCallback: null,
   _frame: null,
   _overlay: null,
+  _box: null,
   _injectedStyleSheets: ["chrome://mozapps/content/preferences/preferences.css",
                          "chrome://browser/skin/preferences/preferences.css",
                          "chrome://browser/skin/preferences/in-content/preferences.css"],
 
   init: function() {
     this._frame = document.getElementById("dialogFrame");
     this._overlay = document.getElementById("dialogOverlay");
+    this._box = document.getElementById("dialogBox");
 
     // Make the close button work.
     let dialogClose = document.getElementById("dialogClose");
     dialogClose.addEventListener("command", this.close.bind(this));
 
     // DOMTitleChanged isn't fired on the frame, only on the chromeEventHandler
     let chromeBrowser = window.QueryInterface(Ci.nsIInterfaceRequestor)
                               .getInterface(Ci.nsIWebNavigation)
@@ -59,16 +61,20 @@ let gSubDialog = {
   },
 
   open: function(aURL, aFeatures = null, aParams = null, aClosingCallback = null) {
     let features = aFeatures || "modal,centerscreen,resizable=no";
     let dialog = window.openDialog(aURL, "dialogFrame", features, aParams);
     if (aClosingCallback) {
       this._closingCallback = aClosingCallback.bind(dialog);
     }
+    let featureParams = new URLSearchParams(features.toLowerCase());
+    this._box.setAttribute("resizable", featureParams.has("resizable") &&
+                                        featureParams.get("resizable") != "no" &&
+                                        featureParams.get("resizable") != 0);
     return dialog;
   },
 
   close: function(aEvent = null) {
     if (this._closingCallback) {
       try {
         this._closingCallback.call(null, aEvent);
       } catch (ex) {
--- a/browser/themes/shared/incontentprefs/preferences.css
+++ b/browser/themes/shared/incontentprefs/preferences.css
@@ -864,16 +864,23 @@ description > html|a {
 #dialogBox {
   border: 1px solid #666;
   display: -moz-box;
   margin: 0;
   padding-right: 6px;
   padding-left: 6px;
 }
 
+#dialogBox[resizable="true"] {
+  resize: both;
+  overflow: hidden;
+  min-height: 30em;
+  min-width: 66ch;
+}
+
 #dialogTitle {
   -moz-margin-start: 5px !important;
 }
 
 .close-icon {
   background-color: transparent !important;
   border: none;
   box-shadow: none;
@@ -883,18 +890,19 @@ description > html|a {
 }
 
 #dialogBox > .groupbox-body {
   -moz-appearance: none;
   padding: 0;
 }
 
 #dialogFrame {
+  -moz-box-flex: 1;
   /* Default dialog dimensions */
-  height: 20em;
+  height: 30em;
   width: 66ch;
 }
 
 /* needs to be removed with bug 1035625 */
 :-moz-any(dialog, window, prefwindow) resizer {
   display: none;
 }
 
--- a/dom/apps/src/AppsServiceChild.jsm
+++ b/dom/apps/src/AppsServiceChild.jsm
@@ -3,76 +3,346 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const Cu = Components.utils;
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
-// This module exposes a subset of the functionnalities of the parent DOM
-// Registry to content processes, to be be used from the AppsService component.
+// This module exposes a subset of the functionalities of the parent DOM
+// Registry to content processes, to be used from the AppsService component.
 
-this.EXPORTED_SYMBOLS = ["DOMApplicationRegistry"];
+this.EXPORTED_SYMBOLS = ["DOMApplicationRegistry", "WrappedManifestCache"];
 
 Cu.import("resource://gre/modules/AppsUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 function debug(s) {
   //dump("-*- AppsServiceChild.jsm: " + s + "\n");
 }
 
+const APPS_IPC_MSG_NAMES = [
+  "Webapps:AddApp",
+  "Webapps:RemoveApp",
+  "Webapps:UpdateApp",
+  "Webapps:CheckForUpdate:Return:KO",
+  "Webapps:FireEvent",
+  "Webapps:UpdateState"
+];
+
+// A simple cache for the wrapped manifests.
+this.WrappedManifestCache = {
+  _cache: { },
+
+  // Gets an entry from the cache, and populates the cache if needed.
+  get: function mcache_get(aManifestURL, aManifest, aWindow, aInnerWindowID) {
+    if (!aManifest) {
+      return;
+    }
+
+    if (!(aManifestURL in this._cache)) {
+      this._cache[aManifestURL] = { };
+    }
+
+    let winObjs = this._cache[aManifestURL];
+    if (!(aInnerWindowID in winObjs)) {
+      winObjs[aInnerWindowID] = Cu.cloneInto(aManifest, aWindow);
+    }
+
+    return winObjs[aInnerWindowID];
+  },
+
+  // Invalidates an entry in the cache.
+  evict: function mcache_evict(aManifestURL, aInnerWindowID) {
+    debug("Evicting manifest " + aManifestURL + " window ID " +
+          aInnerWindowID);
+    if (aManifestURL in this._cache) {
+      let winObjs = this._cache[aManifestURL];
+      if (aInnerWindowID in winObjs) {
+        delete winObjs[aInnerWindowID];
+      }
+
+      if (Object.keys(winObjs).length == 0) {
+        delete this._cache[aManifestURL];
+      }
+    }
+  },
+
+  observe: function(aSubject, aTopic, aData) {
+    // Clear the cache on memory pressure.
+    this._cache = { };
+    Cu.forceGC();
+  },
+
+  init: function() {
+    Services.obs.addObserver(this, "memory-pressure", false);
+  }
+};
+
+this.WrappedManifestCache.init();
+
+
+// DOMApplicationRegistry keeps a cache containing a list of apps in the device.
+// This information is updated with the data received from the main process and
+// it is queried by the DOM objects to set their state.
+// This module handle all the messages broadcasted from the parent process,
+// including DOM events, which are dispatched to the corresponding DOM objects.
+
 this.DOMApplicationRegistry = {
+  // DOMApps will hold a list of arrays of weak references to
+  // mozIDOMApplication objects indexed by manifest URL.
+  DOMApps: {},
+
+  ready: false,
+  webapps: null,
+
   init: function init() {
-    debug("init");
     this.cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"]
                   .getService(Ci.nsISyncMessageSender);
 
-    ["Webapps:AddApp", "Webapps:RemoveApp"].forEach((function(aMsgName) {
+    APPS_IPC_MSG_NAMES.forEach((function(aMsgName) {
       this.cpmm.addMessageListener(aMsgName, this);
     }).bind(this));
 
+    this.cpmm.sendAsyncMessage("Webapps:RegisterForMessages", {
+      messages: APPS_IPC_MSG_NAMES
+    });
+
     // We need to prime the cache with the list of apps.
-    // XXX shoud we do this async and block callers if it's not yet there?
-    this.webapps = this.cpmm.sendSyncMessage("Webapps:GetList", { })[0];
-
+    let list = this.cpmm.sendSyncMessage("Webapps:GetList", { })[0];
+    this.webapps = list.webapps;
     // We need a fast mapping from localId -> app, so we add an index.
+    // We also add the manifest to the app object.
     this.localIdIndex = { };
     for (let id in this.webapps) {
       let app = this.webapps[id];
       this.localIdIndex[app.localId] = app;
+      app.manifest = list.manifests[id];
     }
 
     Services.obs.addObserver(this, "xpcom-shutdown", false);
   },
 
   observe: function(aSubject, aTopic, aData) {
-    // cpmm.addMessageListener causes the DOMApplicationRegistry object to live
-    // forever if we don't clean up properly.
+    // cpmm.addMessageListener causes the DOMApplicationRegistry object to
+    // live forever if we don't clean up properly.
     this.webapps = null;
-    ["Webapps:AddApp", "Webapps:RemoveApp"].forEach((function(aMsgName) {
+    this.DOMApps = null;
+
+    APPS_IPC_MSG_NAMES.forEach((aMsgName) => {
       this.cpmm.removeMessageListener(aMsgName, this);
-    }).bind(this));
+    });
+
+    this.cpmm.sendAsyncMessage("Webapps:UnregisterForMessages",
+                               APPS_IPC_MSG_NAMES)
   },
 
   receiveMessage: function receiveMessage(aMessage) {
     debug("Received " + aMessage.name + " message.");
-    let msg = aMessage.json;
+    let msg = aMessage.data;
     switch (aMessage.name) {
       case "Webapps:AddApp":
         this.webapps[msg.id] = msg.app;
         this.localIdIndex[msg.app.localId] = msg.app;
+        if (msg.manifest) {
+          this.webapps[msg.id].manifest = msg.manifest;
+        }
         break;
       case "Webapps:RemoveApp":
+        delete this.DOMApps[this.webapps[msg.id].manifestURL];
         delete this.localIdIndex[this.webapps[msg.id].localId];
         delete this.webapps[msg.id];
         break;
+      case "Webapps:UpdateApp":
+        let app = this.webapps[msg.oldId];
+        if (!app) {
+          return;
+        }
+
+        if (msg.app) {
+          for (let prop in msg.app) {
+            app[prop] = msg.app[prop];
+          }
+        }
+
+        this.webapps[msg.newId] = app;
+        this.localIdIndex[app.localId] = app;
+        delete this.webapps[msg.oldId];
+
+        let apps = this.DOMApps[msg.app.manifestURL];
+        if (!apps) {
+          return;
+        }
+        for (let i = 0; i < apps.length; i++) {
+          let domApp = apps[i].get();
+          if (!domApp || domApp._window === null) {
+            apps.splice(i, 1);
+            continue;
+          }
+          domApp._proxy = new Proxy(domApp, {
+            get: function(target, prop) {
+              if (!DOMApplicationRegistry.webapps[msg.newId]) {
+                return;
+              }
+              return DOMApplicationRegistry.webapps[msg.newId][prop];
+            },
+            set: function(target, prop, val) {
+              if (!DOMApplicationRegistry.webapps[msg.newId]) {
+                return;
+              }
+              DOMApplicationRegistry.webapps[msg.newId][prop] = val;
+              return;
+            },
+          });
+        }
+        break;
+      case "Webapps:FireEvent":
+        this._fireEvent(aMessage);
+        break;
+      case "Webapps:UpdateState":
+        this._updateState(msg);
+        break;
+      case "Webapps:CheckForUpdate:Return:KO":
+        let DOMApps = this.DOMApps[msg.manifestURL];
+        if (!DOMApps || !msg.requestID) {
+          return;
+        }
+        DOMApps.forEach((DOMApp) => {
+          let domApp = DOMApp.get();
+          if (domApp && msg.requestID) {
+            domApp._fireRequestResult(aMessage, true /* aIsError */);
+          }
+        });
+        break;
     }
   },
 
+  /**
+   * mozIDOMApplication management
+   */
+
+  // Every time a DOM app is created, we save a weak reference to it that will
+  // be used to dispatch events and fire request results.
+  addDOMApp: function(aApp, aManifestURL, aId) {
+    let weakRef = Cu.getWeakReference(aApp);
+
+    if (!this.DOMApps[aManifestURL]) {
+      this.DOMApps[aManifestURL] = [];
+    }
+
+    let apps = this.DOMApps[aManifestURL];
+
+    // Get rid of dead weak references.
+    for (let i = 0; i < apps.length; i++) {
+      let app = apps[i].get();
+      if (!app || app._window === null) {
+        apps.splice(i, 1);
+      }
+    }
+
+    apps.push(weakRef);
+
+    // Each DOM app contains a proxy object used to build their state. We
+    // return the handler for this proxy object with traps to get and set
+    // app properties kept in the DOMApplicationRegistry app cache.
+    return {
+      get: function(target, prop) {
+        if (!DOMApplicationRegistry.webapps[aId]) {
+          return;
+        }
+        return DOMApplicationRegistry.webapps[aId][prop];
+      },
+      set: function(target, prop, val) {
+        if (!DOMApplicationRegistry.webapps[aId]) {
+          return;
+        }
+        DOMApplicationRegistry.webapps[aId][prop] = val;
+        return;
+      },
+    };
+  },
+
+  _fireEvent: function(aMessage) {
+    let msg = aMessage.data;
+    debug("_fireEvent " + JSON.stringify(msg));
+    if (!this.DOMApps || !msg.manifestURL || !msg.eventType) {
+      return;
+    }
+
+    let DOMApps = this.DOMApps[msg.manifestURL];
+    if (!DOMApps) {
+      return;
+    }
+
+    // The parent might ask childs to trigger more than one event in one
+    // shot, so in order to avoid needless IPC we allow an array for the
+    // 'eventType' IPC message field.
+    if (!Array.isArray(msg.eventType)) {
+      msg.eventType = [msg.eventType];
+    }
+
+    DOMApps.forEach((DOMApp) => {
+      let domApp = DOMApp.get();
+      if (!domApp) {
+        return;
+      }
+      msg.eventType.forEach((aEventType) => {
+        if ('on' + aEventType in domApp) {
+          domApp._fireEvent(aEventType);
+        }
+      });
+
+      if (msg.requestID) {
+        aMessage.data.result = msg.manifestURL;
+        domApp._fireRequestResult(aMessage);
+      }
+    });
+  },
+
+  _updateState: function(aMessage) {
+    if (!this.DOMApps || !aMessage.id) {
+      return;
+    }
+
+    let app = this.webapps[aMessage.id];
+    if (!app) {
+      return;
+    }
+
+    if (aMessage.app) {
+      for (let prop in aMessage.app) {
+        app[prop] = aMessage.app[prop];
+      }
+    }
+
+    if ("error" in aMessage) {
+      app.downloadError = aMessage.error;
+    }
+
+    if (aMessage.manifest) {
+      app.manifest = aMessage.manifest;
+      // Evict the wrapped manifest cache for all the affected DOM objects.
+      let DOMApps = this.DOMApps[app.manifestURL];
+      if (!DOMApps) {
+        return;
+      }
+      DOMApps.forEach((DOMApp) => {
+        let domApp = DOMApp.get();
+        if (!domApp) {
+          return;
+        }
+        WrappedManifestCache.evict(app.manifestURL, domApp.innerWindowID);
+      });
+    }
+  },
+
+  /**
+   * nsIAppsService API
+   */
   getAppByManifestURL: function getAppByManifestURL(aManifestURL) {
     debug("getAppByManifestURL " + aManifestURL);
     return AppsUtils.getAppByManifestURL(this.webapps, aManifestURL);
   },
 
   getAppLocalIdByManifestURL: function getAppLocalIdByManifestURL(aManifestURL) {
     debug("getAppLocalIdByManifestURL " + aManifestURL);
     return AppsUtils.getAppLocalIdByManifestURL(this.webapps, aManifestURL);
@@ -84,17 +354,17 @@ this.DOMApplicationRegistry = {
   },
 
   getAppLocalIdByStoreId: function(aStoreId) {
     debug("getAppLocalIdByStoreId:" + aStoreId);
     return AppsUtils.getAppLocalIdByStoreId(this.webapps, aStoreId);
   },
 
   getAppByLocalId: function getAppByLocalId(aLocalId) {
-    debug("getAppByLocalId " + aLocalId);
+    debug("getAppByLocalId " + aLocalId + " - ready: " + this.ready);
     let app = this.localIdIndex[aLocalId];
     if (!app) {
       debug("Ouch, No app!");
       return null;
     }
 
     return new mozIApplication(app);
   },
--- a/dom/apps/src/Webapps.js
+++ b/dom/apps/src/Webapps.js
@@ -7,16 +7,17 @@ const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
 Cu.import("resource://gre/modules/AppsUtils.jsm");
 Cu.import("resource://gre/modules/BrowserElementPromptService.jsm");
+Cu.import("resource://gre/modules/AppsServiceChild.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
                                    "@mozilla.org/childprocessmessagemanager;1",
                                    "nsIMessageSender");
 
 function convertAppsArray(aApps, aWindow) {
   let apps = new aWindow.Array();
   for (let i = 0; i < aApps.length; i++) {
@@ -273,134 +274,121 @@ WebappsRegistry.prototype = {
                                     flags: Ci.nsIClassInfo.DOM_OBJECT,
                                     classDescription: "Webapps Registry"})
 }
 
 /**
   * mozIDOMApplication object
   */
 
-// A simple cache for the wrapped manifests.
-let manifestCache = {
-  _cache: { },
-
-  // Gets an entry from the cache, and populates the cache if needed.
-  get: function mcache_get(aManifestURL, aManifest, aWindow, aInnerWindowID) {
-    if (!(aManifestURL in this._cache)) {
-      this._cache[aManifestURL] = { };
-    }
-
-    let winObjs = this._cache[aManifestURL];
-    if (!(aInnerWindowID in winObjs)) {
-      winObjs[aInnerWindowID] = Cu.cloneInto(aManifest, aWindow);
-    }
-
-    return winObjs[aInnerWindowID];
-  },
-
-  // Invalidates an entry in the cache.
-  evict: function mcache_evict(aManifestURL, aInnerWindowID) {
-    if (aManifestURL in this._cache) {
-      let winObjs = this._cache[aManifestURL];
-      if (aInnerWindowID in winObjs) {
-        delete winObjs[aInnerWindowID];
-      }
-
-      if (Object.keys(winObjs).length == 0) {
-        delete this._cache[aManifestURL];
-      }
-    }
-  },
-
-  observe: function(aSubject, aTopic, aData) {
-    // Clear the cache on memory pressure.
-    this._cache = { };
-  },
-
-  init: function() {
-    Services.obs.addObserver(this, "memory-pressure", false);
-  }
-};
-
 function createApplicationObject(aWindow, aApp) {
-  let app = Cc["@mozilla.org/webapps/application;1"].createInstance(Ci.mozIDOMApplication);
+  let app = Cc["@mozilla.org/webapps/application;1"]
+              .createInstance(Ci.mozIDOMApplication);
   app.wrappedJSObject.init(aWindow, aApp);
   return app;
 }
 
 function WebappsApplication() {
   this.wrappedJSObject = this;
 }
 
 WebappsApplication.prototype = {
   __proto__: DOMRequestIpcHelper.prototype,
 
   init: function(aWindow, aApp) {
+    let proxyHandler = DOMApplicationRegistry.addDOMApp(this,
+                                                        aApp.manifestURL,
+                                                        aApp.id);
+    this._proxy = new Proxy(this, proxyHandler);
+
     this._window = aWindow;
-    let principal = this._window.document.nodePrincipal;
-    this._appStatus = principal.appStatus;
-    this.origin = aApp.origin;
-    this._manifest = aApp.manifest;
-    this._updateManifest = aApp.updateManifest;
-    this.manifestURL = aApp.manifestURL;
-    this.receipts = aApp.receipts;
-    this.installOrigin = aApp.installOrigin;
-    this.installTime = aApp.installTime;
-    this.installState = aApp.installState || "installed";
-    this.removable = aApp.removable;
-    this.lastUpdateCheck = aApp.lastUpdateCheck ? aApp.lastUpdateCheck
-                                                : Date.now();
-    this.updateTime = aApp.updateTime ? aApp.updateTime
-                                      : aApp.installTime;
-    this.progress = NaN;
-    this.downloadAvailable = aApp.downloadAvailable;
-    this.downloading = aApp.downloading;
-    this.readyToApplyDownload = aApp.readyToApplyDownload;
-    this.downloadSize = aApp.downloadSize || 0;
 
     this._onprogress = null;
     this._ondownloadsuccess = null;
     this._ondownloaderror = null;
     this._ondownloadavailable = null;
     this._ondownloadapplied = null;
 
-    this._downloadError = null;
+    this.initDOMRequestHelper(aWindow);
+  },
+
+  get _appStatus() {
+    return this._proxy.appStatus;
+  },
+
+  get downloadAvailable() {
+    return this._proxy.downloadAvailable;
+  },
+
+  get downloading() {
+    return this._proxy.downloading;
+  },
 
-    this.initDOMRequestHelper(aWindow, [
-      { name: "Webapps:CheckForUpdate:Return:KO", weakRef: true },
-      { name: "Webapps:Connect:Return:OK", weakRef: true },
-      { name: "Webapps:Connect:Return:KO", weakRef: true },
-      { name: "Webapps:FireEvent", weakRef: true },
-      { name: "Webapps:GetConnections:Return:OK", weakRef: true },
-      { name: "Webapps:UpdateState", weakRef: true }
-    ]);
+  get downloadSize() {
+    return this._proxy.downloadSize;
+  },
+
+  get installOrigin() {
+    return this._proxy.installOrigin;
+  },
+
+  get installState() {
+    return this._proxy.installState;
+  },
+
+  get installTime() {
+    return this._proxy.installTime;
+  },
 
-    cpmm.sendAsyncMessage("Webapps:RegisterForMessages", {
-      messages: ["Webapps:FireEvent",
-                 "Webapps:UpdateState"],
-      app: {
-        id: this.id,
-        manifestURL: this.manifestURL,
-        installState: this.installState,
-        downloading: this.downloading
-      }
-    });
+  get lastUpdateCheck() {
+    return this._proxy.lastUpdateCheck;
+  },
+
+  get manifestURL() {
+    return this._proxy.manifestURL;
+  },
+
+  get origin() {
+    return this._proxy.origin;
+  },
+
+  get progress() {
+    return this._proxy.progress;
+  },
+
+  get readyToApplyDownload() {
+    return this._proxy.readyToApplyDownload;
+  },
+
+  get receipts() {
+    return this._proxy.receipts;
+  },
+
+  set receipts(aReceipts) {
+    this._proxy.receipts = aReceipts;
+  },
+
+  get removable() {
+    return this._proxy.removable;
+  },
+
+  get updateTime() {
+    return this._proxy.updateTime;
   },
 
   get manifest() {
-    return manifestCache.get(this.manifestURL,
-                             this._manifest,
-                             this._window,
-                             this.innerWindowID);
+    return WrappedManifestCache.get(this.manifestURL,
+                                    this._proxy.manifest,
+                                    this._window,
+                                    this.innerWindowID);
   },
 
   get updateManifest() {
-    return this.updateManifest =
-      this._updateManifest ? Cu.cloneInto(this._updateManifest, this._window)
-                           : null;
+    return this._proxy.updateManifest ?
+      Cu.cloneInto(this._proxy.updateManifest, this._window) : null;
   },
 
   set onprogress(aCallback) {
     this._onprogress = aCallback;
   },
 
   get onprogress() {
     return this._onprogress;
@@ -435,20 +423,20 @@ WebappsApplication.prototype = {
   },
 
   get ondownloadapplied() {
     return this._ondownloadapplied;
   },
 
   get downloadError() {
     // Only return DOMError when we have an error.
-    if (!this._downloadError) {
+    if (!this._proxy.downloadError) {
       return null;
     }
-    return new this._window.DOMError(this._downloadError);
+    return new this._window.DOMError(this._proxy.downloadError);
   },
 
   download: function() {
     cpmm.sendAsyncMessage("Webapps:Download",
                           { manifestURL: this.manifestURL });
   },
 
   cancelDownload: function() {
@@ -480,51 +468,55 @@ WebappsApplication.prototype = {
   },
 
   clearBrowserData: function() {
     let request = this.createRequest();
     let browserChild =
       BrowserElementPromptService.getBrowserElementChildForWindow(this._window);
     if (browserChild) {
       this.addMessageListeners("Webapps:ClearBrowserData:Return");
-      browserChild.messageManager.sendAsyncMessage(
-        "Webapps:ClearBrowserData",
-        { manifestURL: this.manifestURL,
-          oid: this._id,
-          requestID: this.getRequestId(request) }
-      );
+      browserChild.messageManager.sendAsyncMessage("Webapps:ClearBrowserData", {
+        manifestURL: this.manifestURL,
+        oid: this._id,
+        requestID: this.getRequestId(request)
+      });
     } else {
       Services.DOMRequest.fireErrorAsync(request, "NO_CLEARABLE_BROWSER");
     }
     return request;
   },
 
   connect: function(aKeyword, aRules) {
+    this.addMessageListeners(["Webapps:Connect:Return:OK",
+                              "Webapps:Connect:Return:KO"]);
     return this.createPromise(function (aResolve, aReject) {
-      cpmm.sendAsyncMessage("Webapps:Connect",
-                            { keyword: aKeyword,
-                              rules: aRules,
-                              manifestURL: this.manifestURL,
-                              outerWindowID: this._id,
-                              requestID: this.getPromiseResolverId({
-                                resolve: aResolve,
-                                reject: aReject
-                              })});
+      cpmm.sendAsyncMessage("Webapps:Connect", {
+        keyword: aKeyword,
+        rules: aRules,
+        manifestURL: this.manifestURL,
+        outerWindowID: this._id,
+        requestID: this.getPromiseResolverId({
+          resolve: aResolve,
+          reject: aReject
+        })
+      });
     }.bind(this));
   },
 
   getConnections: function() {
+    this.addMessageListeners("Webapps:GetConnections:Return:OK");
     return this.createPromise(function (aResolve, aReject) {
-      cpmm.sendAsyncMessage("Webapps:GetConnections",
-                            { manifestURL: this.manifestURL,
-                              outerWindowID: this._id,
-                              requestID: this.getPromiseResolverId({
-                                resolve: aResolve,
-                                reject: aReject
-                              })});
+      cpmm.sendAsyncMessage("Webapps:GetConnections", {
+        manifestURL: this.manifestURL,
+        outerWindowID: this._id,
+        requestID: this.getPromiseResolverId({
+          resolve: aResolve,
+          reject: aReject
+        })
+      });
     }.bind(this));
   },
 
   addReceipt: function(receipt) {
     let request = this.createRequest();
 
     this.addMessageListeners(["Webapps:AddReceipt:Return:OK",
                               "Webapps:AddReceipt:Return:KO"]);
@@ -563,141 +555,92 @@ WebappsApplication.prototype = {
                                                       oid: this._id,
                                                       requestID: this.getRequestId(request) });
 
     return request;
   },
 
   uninit: function() {
     this._onprogress = null;
-    cpmm.sendAsyncMessage("Webapps:UnregisterForMessages", [
-      "Webapps:FireEvent",
-      "Webapps:UpdateState"
-    ]);
-
-    manifestCache.evict(this.manifestURL, this.innerWindowID);
+    WrappedManifestCache.evict(this.manifestURL, this.innerWindowID);
   },
 
   _fireEvent: function(aName) {
     let handler = this["_on" + aName];
     if (handler) {
       let event = new this._window.MozApplicationEvent(aName, {
         application: this
       });
       try {
         handler.handleEvent(event);
       } catch (ex) {
         dump("Event handler expection " + ex + "\n");
       }
     }
   },
 
-  _updateState: function(aMsg) {
-    if (aMsg.app) {
-      for (let prop in aMsg.app) {
-        this[prop] = aMsg.app[prop];
-      }
+  _fireRequestResult: function(aMessage, aIsError) {
+    let req;
+    let msg = aMessage.data;
+    req = this.takeRequest(msg.requestID);
+    if (!req) {
+      return;
     }
 
-    // Intentional use of 'in' so we unset the error if this is explicitly null.
-    if ('error' in aMsg) {
-      this._downloadError = aMsg.error;
-    }
-
-    if (aMsg.manifest) {
-      this._manifest = aMsg.manifest;
-      manifestCache.evict(this.manifestURL, this.innerWindowID);
-    }
+    aIsError ? Services.DOMRequest.fireError(req, msg.error)
+             : Services.DOMRequest.fireSuccess(req, msg.result);
   },
 
   receiveMessage: function(aMessage) {
     let msg = aMessage.json;
     let req;
     if (aMessage.name == "Webapps:Connect:Return:OK" ||
         aMessage.name == "Webapps:Connect:Return:KO" ||
         aMessage.name == "Webapps:GetConnections:Return:OK") {
       req = this.takePromiseResolver(msg.requestID);
     } else {
       req = this.takeRequest(msg.requestID);
     }
 
-    // ondownload* callbacks should be triggered on all app instances
-    if ((msg.oid != this._id || !req) &&
-        aMessage.name !== "Webapps:FireEvent" &&
-        aMessage.name !== "Webapps:UpdateState") {
+    if (msg.oid !== this._id || !req) {
       return;
     }
 
     switch (aMessage.name) {
       case "Webapps:Launch:Return:KO":
         this.removeMessageListeners(["Webapps:Launch:Return:OK",
                                      "Webapps:Launch:Return:KO"]);
         Services.DOMRequest.fireError(req, "APP_INSTALL_PENDING");
         break;
       case "Webapps:Launch:Return:OK":
         this.removeMessageListeners(["Webapps:Launch:Return:OK",
                                      "Webapps:Launch:Return:KO"]);
         Services.DOMRequest.fireSuccess(req, null);
         break;
-      case "Webapps:CheckForUpdate:Return:KO":
-        Services.DOMRequest.fireError(req, msg.error);
-        break;
-      case "Webapps:FireEvent":
-        if (msg.manifestURL != this.manifestURL) {
-           return;
-        }
-
-        // The parent might ask childs to trigger more than one event in one
-        // shot, so in order to avoid needless IPC we allow an array for the
-        // 'eventType' IPC message field.
-        if (!Array.isArray(msg.eventType)) {
-          msg.eventType = [msg.eventType];
-        }
-
-        msg.eventType.forEach((aEventType) => {
-          // If we are in a successful state clear any past errors.
-          if (aEventType === 'downloadapplied' ||
-              aEventType === 'downloadsuccess') {
-            this._downloadError = null;
-          }
-
-          if ("_on" + aEventType in this) {
-            this._fireEvent(aEventType);
-          } else {
-            dump("Unsupported event type " + aEventType + "\n");
-          }
-        });
-
-        if (req) {
-          Services.DOMRequest.fireSuccess(req, this.manifestURL);
-        }
-        break;
-      case "Webapps:UpdateState":
-        if (msg.manifestURL != this.manifestURL) {
-          return;
-        }
-
-        this._updateState(msg);
-        break;
       case "Webapps:ClearBrowserData:Return":
         this.removeMessageListeners(aMessage.name);
         Services.DOMRequest.fireSuccess(req, null);
         break;
       case "Webapps:Connect:Return:OK":
+        this.removeMessageListeners(["Webapps:Connect:Return:OK",
+                                     "Webapps:Connect:Return:KO"]);
         let messagePorts = [];
         msg.messagePortIDs.forEach((aPortID) => {
           let port = new this._window.MozInterAppMessagePort(aPortID);
           messagePorts.push(port);
         });
         req.resolve(messagePorts);
         break;
       case "Webapps:Connect:Return:KO":
+        this.removeMessageListeners(["Webapps:Connect:Return:OK",
+                                     "Webapps:Connect:Return:KO"]);
         req.reject("No connections registered");
         break;
       case "Webapps:GetConnections:Return:OK":
+        this.removeMessageListeners(aMessage.name);
         let connections = [];
         msg.connections.forEach((aConnection) => {
           let connection =
             new this._window.MozInterAppConnection(aConnection.keyword,
                                                    aConnection.pubAppManifestURL,
                                                    aConnection.subAppManifestURL);
           connections.push(connection);
         });
@@ -800,17 +743,16 @@ WebappsApplicationMgmt.prototype = {
       return;
     }
 
     cpmm.sendAsyncMessage("Webapps:ApplyDownload",
                           { manifestURL: aApp.manifestURL });
   },
 
   uninstall: function(aApp) {
-    dump("-- webapps.js uninstall " + aApp.manifestURL + "\n");
     let request = this.createRequest();
     cpmm.sendAsyncMessage("Webapps:Uninstall", { origin: aApp.origin,
                                                  manifestURL: aApp.manifestURL,
                                                  oid: this._id,
                                                  requestID: this.getRequestId(request) });
     return request;
   },
 
@@ -869,22 +811,18 @@ WebappsApplicationMgmt.prototype = {
           let app = msg.app;
           let event = new this._window.MozApplicationEvent("applicationinstall",
                            { application : createApplicationObject(this._window, app) });
           this._oninstall.handleEvent(event);
         }
         break;
       case "Webapps:Uninstall:Broadcast:Return:OK":
         if (this._onuninstall) {
-          let detail = {
-            manifestURL: msg.manifestURL,
-            origin: msg.origin
-          };
           let event = new this._window.MozApplicationEvent("applicationuninstall",
-                           { application : createApplicationObject(this._window, detail) });
+                           { application : createApplicationObject(this._window, msg) });
           this._onuninstall.handleEvent(event);
         }
         break;
       case "Webapps:Uninstall:Return:OK":
         Services.DOMRequest.fireSuccess(req, msg.manifestURL);
         break;
       case "Webapps:Uninstall:Return:KO":
         Services.DOMRequest.fireError(req, "NOT_INSTALLED");
@@ -903,12 +841,10 @@ WebappsApplicationMgmt.prototype = {
 
   classInfo: XPCOMUtils.generateCI({classID: Components.ID("{8c1bca96-266f-493a-8d57-ec7a95098c15}"),
                                     contractID: "@mozilla.org/webapps/application-mgmt;1",
                                     interfaces: [Ci.mozIDOMApplicationMgmt],
                                     flags: Ci.nsIClassInfo.DOM_OBJECT,
                                     classDescription: "Webapps Application Mgmt"})
 }
 
-manifestCache.init();
-
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([WebappsRegistry,
                                                      WebappsApplication]);
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -174,16 +174,17 @@ this.DOMApplicationRegistry = {
 
     this.frameMessages = ["Webapps:ClearBrowserData"];
 
     this.messages.forEach((function(msgName) {
       ppmm.addMessageListener(msgName, this);
     }).bind(this));
 
     cpmm.addMessageListener("Activities:Register:OK", this);
+    cpmm.addMessageListener("Activities:Register:KO", this);
 
     Services.obs.addObserver(this, "xpcom-shutdown", false);
     Services.obs.addObserver(this, "memory-pressure", false);
 
     AppDownloadManager.registerCancelFunction(this.cancelDownload.bind(this));
 
     this.appsFile = FileUtils.getFile(DIRECTORY_NAME,
                                       ["webapps", "webapps.json"], true).path;
@@ -270,28 +271,38 @@ this.DOMApplicationRegistry = {
     Services.obs.notifyObservers(this, "webapps-registry-start", null);
     this._registryStarted.resolve();
   },
 
   get registryStarted() {
     return this._registryStarted.promise;
   },
 
+  // The registry will be safe to clone when this promise is resolved.
+  _safeToClone: Promise.defer(),
+
   // Notify we are done with registering apps and save a copy of the registry.
   _registryReady: Promise.defer(),
   notifyAppsRegistryReady: function notifyAppsRegistryReady() {
+    // Usually this promise will be resolved earlier, but just in case,
+    // resolve it here also.
+    this._safeToClone.resolve();
     this._registryReady.resolve();
     Services.obs.notifyObservers(this, "webapps-registry-ready", null);
     this._saveApps();
   },
 
   get registryReady() {
     return this._registryReady.promise;
   },
 
+  get safeToClone() {
+    return this._safeToClone.promise;
+  },
+
   // Ensure that the .to property in redirects is a relative URL.
   sanitizeRedirects: function sanitizeRedirects(aSource) {
     if (!aSource) {
       return null;
     }
 
     let res = [];
     for (let i = 0; i < aSource.length; i++) {
@@ -957,16 +968,17 @@ this.DOMApplicationRegistry = {
         app.role = localeManifest.role;
         if (app.appStatus >= Ci.nsIPrincipal.APP_STATUS_PRIVILEGED) {
           app.redirects = this.sanitizeRedirects(manifest.redirects);
         }
         this._registerSystemMessages(manifest, app);
         this._registerInterAppConnections(manifest, app);
         appsToRegister.push({ manifest: manifest, app: app });
       });
+      this._safeToClone.resolve();
       this._registerActivitiesForApps(appsToRegister, aRunUpdate);
     });
   },
 
   observe: function(aSubject, aTopic, aData) {
     if (aTopic == "xpcom-shutdown") {
       this.messages.forEach((function(msgName) {
         ppmm.removeMessageListener(msgName, this);
@@ -1084,98 +1096,124 @@ this.DOMApplicationRegistry = {
         return null;
       }
     }
 
     let msg = aMessage.data || {};
     let mm = aMessage.target;
     msg.mm = mm;
 
+    let processedImmediately = true;
+
+    // There are two kind of messages: the messages that only make sense once the
+    // registry is ready, and those that can (or have to) be treated as soon as
+    // they're received.
     switch (aMessage.name) {
-      case "Webapps:Install": {
-#ifdef MOZ_WIDGET_ANDROID
-        Services.obs.notifyObservers(mm, "webapps-runtime-install", JSON.stringify(msg));
-#else
-        this.doInstall(msg, mm);
-#endif
-        break;
-      }
-      case "Webapps:GetSelf":
-        this.getSelf(msg, mm);
-        break;
-      case "Webapps:Uninstall":
-#ifdef MOZ_WIDGET_ANDROID
-        Services.obs.notifyObservers(mm, "webapps-runtime-uninstall", JSON.stringify(msg));
-#else
-        this.doUninstall(msg, mm);
-#endif
+      case "Activities:Register:KO":
+        dump("Activities didn't register correctly!");
+      case "Activities:Register:OK":
+        // Activities:Register:OK is special because it's one way the registryReady
+        // promise can be resolved.
+        // XXX: What to do when the activities registration failed? At this point
+        // just act as if nothing happened.
+        this.notifyAppsRegistryReady();
         break;
-      case "Webapps:Launch":
-        this.doLaunch(msg, mm);
-        break;
-      case "Webapps:CheckInstalled":
-        this.checkInstalled(msg, mm);
-        break;
-      case "Webapps:GetInstalled":
-        this.getInstalled(msg, mm);
-        break;
-      case "Webapps:GetNotInstalled":
-        this.getNotInstalled(msg, mm);
+      case "Webapps:GetList":
+        // GetList is special because it's synchronous. So far so well, it's the
+        // only synchronous message, if we get more at some point they should get
+        // this treatment also.
+        return this.doGetList();
+      case "child-process-shutdown":
+        this.removeMessageListener(["Webapps:Internal:AllMessages"], mm);
         break;
-      case "Webapps:GetAll":
-        this.doGetAll(msg, mm);
-        break;
-      case "Webapps:InstallPackage": {
-#ifdef MOZ_WIDGET_ANDROID
-        Services.obs.notifyObservers(mm, "webapps-runtime-install-package", JSON.stringify(msg));
-#else
-        this.doInstallPackage(msg, mm);
-#endif
-        break;
-      }
       case "Webapps:RegisterForMessages":
         this.addMessageListener(msg.messages, msg.app, mm);
         break;
       case "Webapps:UnregisterForMessages":
         this.removeMessageListener(msg, mm);
         break;
-      case "child-process-shutdown":
-        this.removeMessageListener(["Webapps:Internal:AllMessages"], mm);
-        break;
-      case "Webapps:GetList":
-        this.addMessageListener(["Webapps:AddApp", "Webapps:RemoveApp"], null, mm);
-        return this.webapps;
-      case "Webapps:Download":
-        this.startDownload(msg.manifestURL);
-        break;
-      case "Webapps:CancelDownload":
-        this.cancelDownload(msg.manifestURL);
-        break;
-      case "Webapps:CheckForUpdate":
-        this.checkForUpdate(msg, mm);
-        break;
-      case "Webapps:ApplyDownload":
-        this.applyDownload(msg.manifestURL);
-        break;
-      case "Activities:Register:OK":
-        this.notifyAppsRegistryReady();
-        break;
-      case "Webapps:Install:Return:Ack":
-        this.onInstallSuccessAck(msg.manifestURL);
-        break;
-      case "Webapps:AddReceipt":
-        this.addReceipt(msg, mm);
-        break;
-      case "Webapps:RemoveReceipt":
-        this.removeReceipt(msg, mm);
-        break;
-      case "Webapps:ReplaceReceipt":
-        this.replaceReceipt(msg, mm);
-        break;
+      default:
+        processedImmediately = false;
+    }
+
+    if (processedImmediately) {
+      return;
     }
+
+    // For all the rest (asynchronous), we wait till the registry is ready
+    // before processing the message.
+    this.registryReady.then( () => {
+      switch (aMessage.name) {
+        case "Webapps:Install": {
+#ifdef MOZ_WIDGET_ANDROID
+          Services.obs.notifyObservers(mm, "webapps-runtime-install", JSON.stringify(msg));
+#else
+          this.doInstall(msg, mm);
+#endif
+          break;
+        }
+        case "Webapps:GetSelf":
+          this.getSelf(msg, mm);
+          break;
+        case "Webapps:Uninstall":
+#ifdef MOZ_WIDGET_ANDROID
+          Services.obs.notifyObservers(mm, "webapps-runtime-uninstall", JSON.stringify(msg));
+#else
+          this.doUninstall(msg, mm);
+#endif
+          break;
+        case "Webapps:Launch":
+          this.doLaunch(msg, mm);
+          break;
+        case "Webapps:CheckInstalled":
+          this.checkInstalled(msg, mm);
+          break;
+        case "Webapps:GetInstalled":
+          this.getInstalled(msg, mm);
+          break;
+        case "Webapps:GetNotInstalled":
+          this.getNotInstalled(msg, mm);
+          break;
+        case "Webapps:GetAll":
+          this.doGetAll(msg, mm);
+          break;
+        case "Webapps:InstallPackage": {
+#ifdef MOZ_WIDGET_ANDROID
+          Services.obs.notifyObservers(mm, "webapps-runtime-install-package", JSON.stringify(msg));
+#else
+          this.doInstallPackage(msg, mm);
+#endif
+          break;
+        }
+        case "Webapps:Download":
+          this.startDownload(msg.manifestURL);
+          break;
+        case "Webapps:CancelDownload":
+          this.cancelDownload(msg.manifestURL);
+          break;
+        case "Webapps:CheckForUpdate":
+          this.checkForUpdate(msg, mm);
+          break;
+        case "Webapps:ApplyDownload":
+          this.applyDownload(msg.manifestURL);
+          break;
+        case "Webapps:Install:Return:Ack":
+          this.onInstallSuccessAck(msg.manifestURL);
+          break;
+        case "Webapps:AddReceipt":
+          this.addReceipt(msg, mm);
+          break;
+        case "Webapps:RemoveReceipt":
+          this.removeReceipt(msg, mm);
+          break;
+        case "Webapps:ReplaceReceipt":
+          this.replaceReceipt(msg, mm);
+          break;
+      }
+    });
   },
 
   getAppInfo: function getAppInfo(aAppId) {
     return AppsUtils.getAppInfo(this.webapps, aAppId);
   },
 
   // Some messages can be listened by several content processes:
   // Webapps:AddApp
@@ -1240,16 +1278,48 @@ this.DOMApplicationRegistry = {
       } else {
         deferred.resolve();
       }
     });
 
     return deferred.promise;
   },
 
+  /**
+    * Returns the full list of apps and manifests.
+    */
+  doGetList: function() {
+    let tmp = [];
+
+    let res = {};
+    let done = false;
+
+    // We allow cloning the registry when the local processing has been done.
+    this.safeToClone.then( () => {
+      for (let id in this.webapps) {
+        tmp.push({ id: id });
+      }
+      this._readManifests(tmp).then(
+        function(manifests) {
+          manifests.forEach((item) => {
+            res[item.id] = item.manifest;
+          });
+          done = true;
+        }
+      );
+    });
+
+    let thread = Services.tm.currentThread;
+    while (!done) {
+      thread.processNextEvent(/* mayWait */ true);
+    }
+    return { webapps: this.webapps, manifests: res };
+  },
+
+
   doLaunch: function (aData, aMm) {
     this.launch(
       aData.manifestURL,
       aData.startPoint,
       aData.timestamp,
       function onsuccess() {
         aMm.sendAsyncMessage("Webapps:Launch:Return:OK", aData);
       },
@@ -1325,17 +1395,17 @@ this.DOMApplicationRegistry = {
     this._saveApps().then(() => {
       this.broadcastMessage("Webapps:UpdateState", {
         app: {
           progress: 0,
           installState: download.previousState,
           downloading: false
         },
         error: error,
-        manifestURL: app.manifestURL,
+        id: app.id
       })
       this.broadcastMessage("Webapps:FireEvent", {
         eventType: "downloaderror",
         manifestURL: app.manifestURL
       });
     });
     AppDownloadManager.remove(aManifestURL);
   },
@@ -1356,17 +1426,17 @@ this.DOMApplicationRegistry = {
       throw new Error("APP_IS_DOWNLOADING");
     }
 
     // If the caller is trying to start a download but we have nothing to
     // download, send an error.
     if (!app.downloadAvailable) {
       this.broadcastMessage("Webapps:UpdateState", {
         error: "NO_DOWNLOAD_AVAILABLE",
-        manifestURL: app.manifestURL
+        id: app.id
       });
       this.broadcastMessage("Webapps:FireEvent", {
         eventType: "downloaderror",
         manifestURL: app.manifestURL
       });
       throw new Error("NO_DOWNLOAD_AVAILABLE");
     }
 
@@ -1404,17 +1474,17 @@ this.DOMApplicationRegistry = {
         debug("No appcache found, sending 'downloaded' for " + aManifestURL);
         app.downloadAvailable = false;
 
         yield this._saveApps();
 
         this.broadcastMessage("Webapps:UpdateState", {
           app: app,
           manifest: jsonManifest,
-          manifestURL: aManifestURL
+          id: app.id
         });
         this.broadcastMessage("Webapps:FireEvent", {
           eventType: "downloadsuccess",
           manifestURL: aManifestURL
         });
       }
 
       return;
@@ -1458,17 +1528,17 @@ this.DOMApplicationRegistry = {
     app.downloadAvailable = false;
     app.readyToApplyDownload = true;
     app.updateTime = Date.now();
 
     yield this._saveApps();
 
     this.broadcastMessage("Webapps:UpdateState", {
       app: app,
-      manifestURL: aManifestURL
+      id: app.id
     });
     this.broadcastMessage("Webapps:FireEvent", {
       eventType: "downloadsuccess",
       manifestURL: aManifestURL
     });
     if (app.installState == "pending") {
       // We restarted a failed download, apply it automatically.
       this.applyDownload(aManifestURL);
@@ -1560,17 +1630,17 @@ this.DOMApplicationRegistry = {
           manifestURL: app.manifestURL },
         true);
     }
     this.updateDataStore(this.webapps[id].localId, app.origin,
                          app.manifestURL, newManifest);
     this.broadcastMessage("Webapps:UpdateState", {
       app: app,
       manifest: newManifest,
-      manifestURL: app.manifestURL
+      id: app.id
     });
     this.broadcastMessage("Webapps:FireEvent", {
       eventType: "downloadapplied",
       manifestURL: app.manifestURL
     });
   }),
 
   startOfflineCacheDownload: function(aManifest, aApp, aProfileDir, aIsUpdate) {
@@ -1599,17 +1669,17 @@ this.DOMApplicationRegistry = {
       DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", {
         // Clear any previous errors.
         error: null,
         app: {
           downloading: true,
           installState: aApp.installState,
           progress: 0
         },
-        manifestURL: aApp.manifestURL
+        id: aApp.id
       });
       let cacheUpdate = updateSvc.scheduleAppUpdate(
         appcacheURI, docURI, aApp.localId, false, aProfileDir);
 
       // We save the download details for potential further usage like
       // cancelling it.
       let download = {
         cacheUpdate: cacheUpdate,
@@ -1649,16 +1719,17 @@ this.DOMApplicationRegistry = {
       this.notifyAppsRegistryReady();
     }
   },
 
   checkForUpdate: function(aData, aMm) {
     debug("checkForUpdate for " + aData.manifestURL);
 
     function sendError(aError) {
+      debug("checkForUpdate error " + aError);
       aData.error = aError;
       aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData);
     }
 
     let id = this._appIdForManifestURL(aData.manifestURL);
     let app = this.webapps[id];
 
     // We cannot update an app that does not exists.
@@ -1678,71 +1749,67 @@ this.DOMApplicationRegistry = {
       sendError("APP_IS_DOWNLOADING");
       return;
     }
 
     // If the app is packaged and its manifestURL has an app:// scheme,
     // then we can't have an update.
     if (app.origin.startsWith("app://") &&
         app.manifestURL.startsWith("app://")) {
-      aData.error = "NOT_UPDATABLE";
-      aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData);
+      sendError("NOT_UPDATABLE");
       return;
     }
 
     // For non-removable hosted apps that lives in the core apps dir we
     // only check the appcache because we can't modify the manifest even
     // if it has changed.
     let onlyCheckAppCache = false;
 
 #ifdef MOZ_WIDGET_GONK
     let appDir = FileUtils.getDir("coreAppsDir", ["webapps"], false);
     onlyCheckAppCache = (app.basePath == appDir.path);
 #endif
 
     if (onlyCheckAppCache) {
       // Bail out for packaged apps.
       if (app.origin.startsWith("app://")) {
-        aData.error = "NOT_UPDATABLE";
-        aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData);
+        sendError("NOT_UPDATABLE");
         return;
       }
 
       // We need the manifest to check if we have an appcache.
       this._readManifests([{ id: id }]).then((aResult) => {
         let manifest = aResult[0].manifest;
         if (!manifest.appcache_path) {
-          aData.error = "NOT_UPDATABLE";
-          aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData);
+          sendError("NOT_UPDATABLE");
           return;
         }
 
         debug("Checking only appcache for " + aData.manifestURL);
         // Check if the appcache is updatable, and send "downloadavailable" or
         // "downloadapplied".
         let updateObserver = {
           observe: function(aSubject, aTopic, aObsData) {
             debug("onlyCheckAppCache updateSvc.checkForUpdate return for " +
                   app.manifestURL + " - event is " + aTopic);
             if (aTopic == "offline-cache-update-available") {
               app.downloadAvailable = true;
               this._saveApps().then(() => {
                 this.broadcastMessage("Webapps:UpdateState", {
                   app: app,
-                  manifestURL: app.manifestURL
+                  id: app.id
                 });
                 this.broadcastMessage("Webapps:FireEvent", {
                   eventType: "downloadavailable",
                   manifestURL: app.manifestURL,
                   requestID: aData.requestID
                 });
               });
             } else {
-              aData.error = "NOT_UPDATABLE";
-              aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData);
+              sendError("NOT_UPDATABLE");
             }
           }
         };
         let helper = new ManifestHelper(manifest, aData.manifestURL);
         debug("onlyCheckAppCache - launch updateSvc.checkForUpdate for " +
               helper.fullAppcachePath());
         updateSvc.checkForUpdate(Services.io.newURI(helper.fullAppcachePath(), null, null),
                                  app.localId, false, updateObserver);
@@ -1792,17 +1859,17 @@ this.DOMApplicationRegistry = {
             } else {
               this._saveApps().then(() => {
                 // Like if we got a 304, just send a 'downloadapplied'
                 // or downloadavailable event.
                 let eventType = app.downloadAvailable ? "downloadavailable"
                                                       : "downloadapplied";
                 aMm.sendAsyncMessage("Webapps:UpdateState", {
                   app: app,
-                  manifestURL: app.manifestURL
+                  id: app.id
                 });
                 aMm.sendAsyncMessage("Webapps:FireEvent", {
                   eventType: eventType,
                   manifestURL: app.manifestURL,
                   requestID: aData.requestID
                 });
               });
             }
@@ -1819,17 +1886,17 @@ this.DOMApplicationRegistry = {
           app.lastCheckedUpdate = Date.now();
           this._saveApps().then(() => {
             // If the app is a packaged app, we just send a 'downloadapplied'
             // or downloadavailable event.
             let eventType = app.downloadAvailable ? "downloadavailable"
                                                   : "downloadapplied";
             aMm.sendAsyncMessage("Webapps:UpdateState", {
               app: app,
-              manifestURL: app.manifestURL
+              id: app.id
             });
             aMm.sendAsyncMessage("Webapps:FireEvent", {
               eventType: eventType,
               manifestURL: app.manifestURL,
               requestID: aData.requestID
             });
           });
         } else {
@@ -1928,17 +1995,17 @@ this.DOMApplicationRegistry = {
     // event.
     aApp.downloadAvailable = true;
     aApp.downloadSize = manifest.size;
     aApp.updateManifest = aNewManifest;
     yield this._saveApps();
 
     this.broadcastMessage("Webapps:UpdateState", {
       app: aApp,
-      manifestURL: aApp.manifestURL
+      id: aApp.id
     });
     this.broadcastMessage("Webapps:FireEvent", {
       eventType: "downloadavailable",
       manifestURL: aApp.manifestURL,
       requestID: aData.requestID
     });
   }),
 
@@ -1994,17 +2061,17 @@ this.DOMApplicationRegistry = {
     // Update the registry.
     this.webapps[aId] = aApp;
     yield this._saveApps();
 
     if (!manifest.appcache_path) {
       this.broadcastMessage("Webapps:UpdateState", {
         app: aApp,
         manifest: aApp.manifest,
-        manifestURL: aApp.manifestURL
+        id: aApp.id
       });
       this.broadcastMessage("Webapps:FireEvent", {
         eventType: "downloadapplied",
         manifestURL: aApp.manifestURL,
         requestID: aData.requestID
       });
     } else {
       // Check if the appcache is updatable, and send "downloadavailable" or
@@ -2028,17 +2095,17 @@ this.DOMApplicationRegistry = {
                                                   : "downloadapplied";
 
       aApp.downloadAvailable = (eventType == "downloadavailable");
       yield this._saveApps();
 
       this.broadcastMessage("Webapps:UpdateState", {
         app: aApp,
         manifest: aApp.manifest,
-        manifestURL: aApp.manifestURL
+        id: aApp.id
       });
       this.broadcastMessage("Webapps:FireEvent", {
         eventType: eventType,
         manifestURL: aApp.manifestURL,
         requestID: aData.requestID
       });
     }
 
@@ -2459,17 +2526,18 @@ this.DOMApplicationRegistry = {
 
     // Store the manifest and the updateManifest.
     this._writeManifestFile(app.id, false, aManifest);
     if (aUpdateManifest) {
       this._writeManifestFile(app.id, true, aUpdateManifest);
     }
 
     this._saveApps().then(() => {
-      this.broadcastMessage("Webapps:AddApp", { id: app.id, app: app });
+      this.broadcastMessage("Webapps:AddApp",
+                            { id: app.id, app: app, manifest: aManifest });
     });
   }),
 
   confirmInstall: Task.async(function*(aData, aProfileDir, aInstallSuccessCallback) {
     debug("confirmInstall");
 
     let origin = Services.io.newURI(aData.app.origin, null, null);
     let id = this._appIdForManifestURL(aData.app.manifestURL);
@@ -2559,16 +2627,18 @@ this.DOMApplicationRegistry = {
       };
     }
 
     // We notify about the successful installation via mgmt.oninstall and the
     // corresponding DOMRequest.onsuccess event as soon as the app is properly
     // saved in the registry.
     yield this._saveApps();
 
+    aData.isPackage ? appObject.updateManifest = jsonManifest :
+                      appObject.manifest = jsonManifest;
     this.broadcastMessage("Webapps:AddApp", { id: id, app: appObject });
 
     if (!aData.isPackage) {
       this.updateAppHandlers(null, app.manifest, app);
       if (aInstallSuccessCallback) {
         try {
           yield aInstallSuccessCallback(app, app.manifest);
         } catch (e) {
@@ -2641,17 +2711,18 @@ this.DOMApplicationRegistry = {
     yield this._saveApps();
 
     this.updateAppHandlers(null, aManifest, aNewApp);
     // Clear the manifest cache in case it holds the update manifest.
     if (aId in this._manifestCache) {
       delete this._manifestCache[aId];
     }
 
-    this.broadcastMessage("Webapps:AddApp", { id: aId, app: aNewApp });
+    this.broadcastMessage("Webapps:AddApp",
+                          { id: aId, app: aNewApp, manifest: aManifest });
     Services.obs.notifyObservers(null, "webapps-installed",
       JSON.stringify({ manifestURL: aNewApp.manifestURL }));
 
     if (supportUseCurrentProfile()) {
       // Update the permissions for this app.
       PermissionsInstaller.installPermissions({
         manifest: aManifest,
         origin: aNewApp.origin,
@@ -2801,32 +2872,32 @@ this.DOMApplicationRegistry = {
     // Save the current state of the app to handle cases where we may be
     // retrying a past download.
     yield DOMApplicationRegistry._saveApps();
 
     DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", {
         // Clear any previous download errors.
         error: null,
         app: aOldApp,
-        manifestURL: aNewApp.manifestURL
+        id: aId
     });
 
     let zipFile = yield this._getPackage(requestChannel, aId, aOldApp, aNewApp);
     let hash = yield this._computeFileHash(zipFile.path);
 
     let responseStatus = requestChannel.responseStatus;
     let oldPackage = (responseStatus == 304 || hash == aOldApp.packageHash);
 
     if (oldPackage) {
       debug("package's etag or hash unchanged; sending 'applied' event");
       // The package's Etag or hash has not changed.
       // We send an "applied" event right away so code awaiting that event
       // can proceed to access the app. We also throw an error to alert
       // the caller that the package wasn't downloaded.
-      this._sendAppliedEvent(aNewApp, aOldApp, aId);
+      this._sendAppliedEvent(aOldApp);
       throw new Error("PACKAGE_UNCHANGED");
     }
 
     let newManifest = yield this._openAndReadPackage(zipFile, aOldApp, aNewApp,
             isLocalFileInstall, aIsUpdate, aManifest, requestChannel, hash);
 
     AppDownloadManager.remove(aNewApp.manifestURL);
 
@@ -2952,17 +3023,17 @@ this.DOMApplicationRegistry = {
     return requestChannel;
   },
 
   _sendDownloadProgressEvent: function(aNewApp, aProgress) {
     this.broadcastMessage("Webapps:UpdateState", {
       app: {
         progress: aProgress
       },
-      manifestURL: aNewApp.manifestURL
+      id: aNewApp.id
     });
     this.broadcastMessage("Webapps:FireEvent", {
       eventType: "progress",
       manifestURL: aNewApp.manifestURL
     });
   },
 
   _getPackage: function(aRequestChannel, aId, aOldApp, aNewApp) {
@@ -3069,56 +3140,53 @@ this.DOMApplicationRegistry = {
   /**
    * Send an "applied" event right away for the package being installed.
    *
    * XXX We use this to exit the app update process early when the downloaded
    * package is identical to the last one we installed.  Presumably we do
    * something similar after updating the app, and we could refactor both cases
    * to use the same code to send the "applied" event.
    *
-   * @param aNewApp {Object} the new app data
-   * @param aOldApp {Object} the currently stored app data
-   * @param aId {String} the unique id of the app
+   * @param aApp {Object} app data
    */
-  _sendAppliedEvent: function(aNewApp, aOldApp, aId) {
-    aOldApp.downloading = false;
-    aOldApp.downloadAvailable = false;
-    aOldApp.downloadSize = 0;
-    aOldApp.installState = "installed";
-    aOldApp.readyToApplyDownload = false;
-    if (aOldApp.staged && aOldApp.staged.manifestHash) {
+  _sendAppliedEvent: function(aApp) {
+    aApp.downloading = false;
+    aApp.downloadAvailable = false;
+    aApp.downloadSize = 0;
+    aApp.installState = "installed";
+    aApp.readyToApplyDownload = false;
+    if (aApp.staged && aApp.staged.manifestHash) {
       // If we're here then the manifest has changed but the package
       // hasn't. Let's clear this, so we don't keep offering
       // a bogus update to the user
-      aOldApp.manifestHash = aOldApp.staged.manifestHash;
-      aOldApp.etag = aOldApp.staged.etag || aOldApp.etag;
-      aOldApp.staged = {};
-
-      // Move the staged update manifest to a non staged one.
+      aApp.manifestHash = aApp.staged.manifestHash;
+      aApp.etag = aApp.staged.etag || aApp.etag;
+      aApp.staged = {};
+     // Move the staged update manifest to a non staged one.
       try {
-        let staged = this._getAppDir(aId);
+        let staged = this._getAppDir(aApp.id);
         staged.append("staged-update.webapp");
         staged.moveTo(staged.parent, "update.webapp");
       } catch (ex) {
         // We don't really mind much if this fails.
       }
     }
 
     // Save the updated registry, and cleanup the tmp directory.
     this._saveApps().then(() => {
       this.broadcastMessage("Webapps:UpdateState", {
-        app: aOldApp,
-        manifestURL: aNewApp.manifestURL
+        app: aApp,
+        id: aApp.id
       });
       this.broadcastMessage("Webapps:FireEvent", {
-        manifestURL: aNewApp.manifestURL,
+        manifestURL: aApp.manifestURL,
         eventType: ["downloadsuccess", "downloadapplied"]
       });
     });
-    let file = FileUtils.getFile("TmpD", ["webapps", aId], false);
+    let file = FileUtils.getFile("TmpD", ["webapps", aApp.id], false);
     if (file && file.exists()) {
       file.remove(true);
     }
   },
 
   _openAndReadPackage: function(aZipFile, aOldApp, aNewApp, aIsLocalFileInstall,
                                 aIsUpdate, aManifest, aRequestChannel, aHash) {
     return Task.spawn((function*() {
@@ -3427,19 +3495,20 @@ this.DOMApplicationRegistry = {
         delete this.webapps[oldId];
         // Rename the directories where the files are installed.
         [DIRECTORY_NAME, "TmpD"].forEach(function(aDir) {
           let parent = FileUtils.getDir(aDir, ["webapps"], true, true);
           let dir = FileUtils.getDir(aDir, ["webapps", oldId], true, true);
           dir.moveTo(parent, newId);
         });
         // Signals that we need to swap the old id with the new app.
-        this.broadcastMessage("Webapps:RemoveApp", { id: oldId });
-        this.broadcastMessage("Webapps:AddApp", { id: newId,
-                                                  app: aOldApp });
+        this.broadcastMessage("Webapps:UpdateApp", { oldId: oldId,
+                                                     newId: newId,
+                                                     app: aOldApp });
+
       }
     }
   },
 
   _getIds: function(aIsSigned, aZipReader, aConverter, aNewApp, aOldApp,
                     aIsUpdate) {
     // Get ids.json if the file is signed
     if (aIsSigned) {
@@ -3532,17 +3601,17 @@ this.DOMApplicationRegistry = {
     if (aOldApp.staged) {
       delete aOldApp.staged;
     }
 
     this._saveApps().then(() => {
       this.broadcastMessage("Webapps:UpdateState", {
         app: aOldApp,
         error: aError,
-        manifestURL: aNewApp.manifestURL
+        id: aNewApp.id
       });
       this.broadcastMessage("Webapps:FireEvent", {
         eventType: "downloaderror",
         manifestURL:  aNewApp.manifestURL
       });
     });
     AppDownloadManager.remove(aNewApp.manifestURL);
   },
@@ -3712,19 +3781,23 @@ this.DOMApplicationRegistry = {
     this._readManifests(tmp).then((aResult) => {
       for (let i = 0; i < aResult.length; i++)
         aData.apps[i].manifest = aResult[i].manifest;
       aMm.sendAsyncMessage("Webapps:GetNotInstalled:Return:OK", aData);
     });
   },
 
   doGetAll: function(aData, aMm) {
-    this.getAll(function (apps) {
-      aData.apps = apps;
-      aMm.sendAsyncMessage("Webapps:GetAll:Return:OK", aData);
+    // We can't do this until the registry is ready.
+    debug("doGetAll");
+    this.registryReady.then(() => {
+      this.getAll(function (apps) {
+        aData.apps = apps;
+        aMm.sendAsyncMessage("Webapps:GetAll:Return:OK", aData);
+      });
     });
   },
 
   getAll: function(aCallback) {
     debug("getAll");
     let apps = [];
     let tmp = [];
 
@@ -4082,17 +4155,17 @@ let AppcacheObserver = function(aApp) {
 };
 
 AppcacheObserver.prototype = {
   // nsIOfflineCacheUpdateObserver implementation
   _sendProgressEvent: function() {
     let app = this.app;
     DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", {
       app: app,
-      manifestURL: app.manifestURL
+      id: app.id
     });
     DOMApplicationRegistry.broadcastMessage("Webapps:FireEvent", {
       eventType: "progress",
       manifestURL: app.manifestURL
     });
   },
 
   updateStateChanged: function appObs_Update(aUpdate, aState) {
@@ -4114,17 +4187,17 @@ AppcacheObserver.prototype = {
         return;
       }
 
       app.updateTime = Date.now();
       app.downloading = false;
       app.downloadAvailable = false;
       DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", {
         app: app,
-        manifestURL: app.manifestURL
+        id: app.id
       });
       DOMApplicationRegistry.broadcastMessage("Webapps:FireEvent", {
         eventType: ["downloadsuccess", "downloadapplied"],
         manifestURL: app.manifestURL
       });
     }
 
     let setError = function appObs_setError(aError) {
@@ -4137,17 +4210,17 @@ AppcacheObserver.prototype = {
       if (app.isCanceling) {
         delete app.isCanceling;
         return;
       }
 
       DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", {
         app: app,
         error: aError,
-        manifestURL: app.manifestURL
+        id: app.id
       });
       DOMApplicationRegistry.broadcastMessage("Webapps:FireEvent", {
         eventType: "downloaderror",
         manifestURL: app.manifestURL
       });
     }
 
     switch (aState) {
--- a/dom/apps/tests/test_packaged_app_common.js
+++ b/dom/apps/tests/test_packaged_app_common.js
@@ -93,16 +93,17 @@ var PackagedTestHelper = (function Packa
       ok(false, "Got unexpected " + evt.target.error.name);
       finish();
     };
 
     navigator.mozApps.mgmt.oninstall = function(evt) {
       var aApp = evt.application;
       aApp.ondownloaderror = function(evt) {
         var error = aApp.downloadError.name;
+        ok(true, "Got downloaderror " + error);
         if (error == aExpectedError) {
           ok(true, "Got expected " + aExpectedError);
           var expected = {
             name: aName,
             manifestURL: aMiniManifestURL,
             installOrigin: gInstallOrigin,
             progress: 0,
             installState: "pending",
--- a/dom/apps/tests/test_packaged_app_update.html
+++ b/dom/apps/tests/test_packaged_app_update.html
@@ -74,25 +74,25 @@ function checkLastAppState(aMiniManifest
 
 function updateApp(aExpectedReady, aPreviousVersion, aNextVersion) {
   var lApp = PackagedTestHelper.gApp;
 
   var ondownloadappliedhandler =
     checkLastAppState.bind(PackagedTestHelper, miniManifestURL, false, false,
                            aNextVersion, PackagedTestHelper.next);
 
-    var ondownloadsuccesshandler =
-      checkLastAppState.bind(undefined, miniManifestURL,
-                             aExpectedReady, false, aPreviousVersion,
-                             function() {
-        navigator.mozApps.mgmt.applyDownload(lApp);
-    });
+  var ondownloadsuccesshandler =
+    checkLastAppState.bind(undefined, miniManifestURL,
+                           aExpectedReady, false, aPreviousVersion,
+                           function() {
+      navigator.mozApps.mgmt.applyDownload(lApp);
+  });
 
-    checkForUpdate(true, ondownloadsuccesshandler, ondownloadappliedhandler, null,
-                   true);
+  checkForUpdate(true, ondownloadsuccesshandler, ondownloadappliedhandler,
+                 null, true);
 
 }
 
 var initialPermissionState = {
   "geolocation": "prompt",
   "audio-capture": "prompt",
   "video-capture": "prompt",
   "test-permission-read": "prompt",
@@ -249,17 +249,17 @@ var steps = [
   },
   function() {
     info("== TEST == Update packaged app - Updating a pending app");
     miniManifestURL = PackagedTestHelper.gSJS +
                       "?getManifest=true" +
                       "&appName=arandomname" +
                       "&appToFail1";
     PackagedTestHelper.checkAppDownloadError(miniManifestURL,
-                                            "MANIFEST_MISMATCH", 2, false, true,
+                                            "MANIFEST_MISMATCH", 1, false, true,
                                              "arandomname",
                                              function () {
       checkForUpdate(false, null, null, null, false,
                      function (request) {
         if (request.error.name === "PENDING_APP_NOT_UPDATABLE") {
           info("Got expected PENDING_APP_NOT_UPDATEABLE");
         } else {
           ok(false, "Got unexpected " + request.error.name);
--- a/dom/apps/tests/test_receipt_operations.html
+++ b/dom/apps/tests/test_receipt_operations.html
@@ -238,9 +238,9 @@ function runTest() {
   ok(true, "App uninstalled");
 }
 
 addLoadEvent(go);
 
 </script>
 </pre>
 </body>
-</html>
\ No newline at end of file
+</html>
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -652,20 +652,41 @@ CreateConfig(EGLConfig* aConfig, int32_t
     }
 
     if (!sEGLLibrary.fChooseConfig(EGL_DISPLAY(), attribs,
                                    configs, ncfg, &ncfg) ||
         ncfg < 1) {
         return false;
     }
 
+#ifdef MOZ_WIDGET_GONK
+    // On gonk, it's important to select a configuration with the
+    // the correct order as well as bits per channel.
+    // EGL_NATIVE_VISUAL_ID gives us the Android pixel format which
+    // is an enum that tells us both order and bits per channel.
+    // For example -
+    //  HAL_PIXEL_FORMAT_RGBX_8888
+    //  HAL_PIXEL_FORMAT_BGRA_8888
+    //  HAL_PIXEL_FORMAT_RGB_565
+    for (int j = 0; j < ncfg; ++j) {
+        EGLConfig config = configs[j];
+        EGLint format;
+        if (sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config,
+                                         LOCAL_EGL_NATIVE_VISUAL_ID, &format) &&
+            format == GetGonkDisplay()->surfaceformat)
+        {
+            *aConfig = config;
+            return true;
+        }
+    }
+#endif
+
     for (int j = 0; j < ncfg; ++j) {
         EGLConfig config = configs[j];
         EGLint r, g, b, a;
-
         if (sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config,
                                          LOCAL_EGL_RED_SIZE, &r) &&
             sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config,
                                          LOCAL_EGL_GREEN_SIZE, &g) &&
             sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config,
                                          LOCAL_EGL_BLUE_SIZE, &b) &&
             sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config,
                                          LOCAL_EGL_ALPHA_SIZE, &a) &&
--- a/toolkit/devtools/client/dbg-client.jsm
+++ b/toolkit/devtools/client/dbg-client.jsm
@@ -61,16 +61,18 @@ this.makeInfallible = DevToolsUtils.make
 
 let LOG_PREF = "devtools.debugger.log";
 let VERBOSE_PREF = "devtools.debugger.log.verbose";
 let wantLogging = Services.prefs.getBoolPref(LOG_PREF);
 let wantVerbose =
   Services.prefs.getPrefType(VERBOSE_PREF) !== Services.prefs.PREF_INVALID &&
   Services.prefs.getBoolPref(VERBOSE_PREF);
 
+let noop = () => {};
+
 function dumpn(str) {
   if (wantLogging) {
     dump("DBG-CLIENT: " + str + "\n");
   }
 }
 
 function dumpv(msg) {
   if (wantVerbose) {
@@ -438,17 +440,17 @@ DebuggerClient.prototype = {
    * Attach to a tab actor.
    *
    * @param string aTabActor
    *        The actor ID for the tab to attach.
    * @param function aOnResponse
    *        Called with the response packet and a TabClient
    *        (which will be undefined on error).
    */
-  attachTab: function (aTabActor, aOnResponse) {
+  attachTab: function (aTabActor, aOnResponse = noop) {
     if (this._clients.has(aTabActor)) {
       let cachedTab = this._clients.get(aTabActor);
       let cachedResponse = {
         cacheDisabled: cachedTab.cacheDisabled,
         javascriptEnabled: cachedTab.javascriptEnabled,
         traits: cachedTab.traits,
       };
       setTimeout(() => aOnResponse(cachedResponse, cachedTab), 0);
@@ -473,17 +475,17 @@ DebuggerClient.prototype = {
    * Attach to an addon actor.
    *
    * @param string aAddonActor
    *        The actor ID for the addon to attach.
    * @param function aOnResponse
    *        Called with the response packet and a AddonClient
    *        (which will be undefined on error).
    */
-  attachAddon: function DC_attachAddon(aAddonActor, aOnResponse) {
+  attachAddon: function DC_attachAddon(aAddonActor, aOnResponse = noop) {
     let packet = {
       to: aAddonActor,
       type: "attach"
     };
     this.request(packet, aResponse => {
       let addonClient;
       if (!aResponse.error) {
         addonClient = new AddonClient(this, aAddonActor);
@@ -501,17 +503,17 @@ DebuggerClient.prototype = {
    *        The ID for the console actor to attach to.
    * @param array aListeners
    *        The console listeners you want to start.
    * @param function aOnResponse
    *        Called with the response packet and a WebConsoleClient
    *        instance (which will be undefined on error).
    */
   attachConsole:
-  function (aConsoleActor, aListeners, aOnResponse) {
+  function (aConsoleActor, aListeners, aOnResponse = noop) {
     let packet = {
       to: aConsoleActor,
       type: "startListeners",
       listeners: aListeners,
     };
 
     this.request(packet, (aResponse) => {
       let consoleClient;
@@ -534,17 +536,17 @@ DebuggerClient.prototype = {
    *        The actor ID for the thread to attach.
    * @param function aOnResponse
    *        Called with the response packet and a ThreadClient
    *        (which will be undefined on error).
    * @param object aOptions
    *        Configuration options.
    *        - useSourceMaps: whether to use source maps or not.
    */
-  attachThread: function (aThreadActor, aOnResponse, aOptions={}) {
+  attachThread: function (aThreadActor, aOnResponse = noop, aOptions={}) {
     if (this._clients.has(aThreadActor)) {
       setTimeout(() => aOnResponse({}, this._clients.get(aThreadActor)), 0);
       return;
     }
 
    let packet = {
       to: aThreadActor,
       type: "attach",
@@ -563,17 +565,17 @@ DebuggerClient.prototype = {
    * Attach to a trace actor.
    *
    * @param string aTraceActor
    *        The actor ID for the tracer to attach.
    * @param function aOnResponse
    *        Called with the response packet and a TraceClient
    *        (which will be undefined on error).
    */
-  attachTracer: function (aTraceActor, aOnResponse) {
+  attachTracer: function (aTraceActor, aOnResponse = noop) {
     if (this._clients.has(aTraceActor)) {
       setTimeout(() => aOnResponse({}, this._clients.get(aTraceActor)), 0);
       return;
     }
 
     let packet = {
       to: aTraceActor,
       type: "attach"
@@ -1272,17 +1274,17 @@ TabClient.prototype = {
    *
    * @param object aOptions
    *        Configuration options.
    *        - useSourceMaps: whether to use source maps or not.
    * @param function aOnResponse
    *        Called with the response packet and a ThreadClient
    *        (which will be undefined on error).
    */
-  attachThread: function(aOptions={}, aOnResponse) {
+  attachThread: function(aOptions={}, aOnResponse = noop) {
     if (this.thread) {
       setTimeout(() => aOnResponse({}, this.thread), 0);
       return;
     }
 
     let packet = {
       to: this._threadActor,
       type: "attach",
@@ -1620,17 +1622,17 @@ ThreadClient.prototype = {
    *
    * @param boolean aFlag
    *        Enables pausing if true, disables otherwise.
    * @param function aOnResponse
    *        Called with the response packet.
    */
   pauseOnExceptions: function (aPauseOnExceptions,
                                aIgnoreCaughtExceptions,
-                               aOnResponse) {
+                               aOnResponse = noop) {
     this._pauseOnExceptions = aPauseOnExceptions;
     this._ignoreCaughtExceptions = aIgnoreCaughtExceptions;
 
     // If the debuggee is paused, we have to send the flag via a reconfigure
     // request.
     if (this.paused) {
       this.reconfigure({
         pauseOnExceptions: aPauseOnExceptions,
@@ -1656,17 +1658,17 @@ ThreadClient.prototype = {
    *
    * @param array|string events
    *        An array of strings, representing the DOM event types to pause on,
    *        or "*" to pause on all DOM events. Pass an empty array to
    *        completely disable pausing on DOM events.
    * @param function onResponse
    *        Called with the response packet in a future turn of the event loop.
    */
-  pauseOnDOMEvents: function (events, onResponse) {
+  pauseOnDOMEvents: function (events, onResponse = noop) {
     this._pauseOnDOMEvents = events;
     // If the debuggee is paused, the value of the array will be communicated in
     // the next resumption. Otherwise we have to force a pause in order to send
     // the array.
     if (this.paused) {
       setTimeout(() => onResponse({}), 0);
       return;
     }
@@ -1734,17 +1736,18 @@ ThreadClient.prototype = {
   /**
    * Request to set a breakpoint in the specified location.
    *
    * @param object aLocation
    *        The source location object where the breakpoint will be set.
    * @param function aOnResponse
    *        Called with the thread's response.
    */
-  setBreakpoint: function ({ url, line, column, condition }, aOnResponse) {
+  setBreakpoint: function ({ url, line, column, condition },
+                           aOnResponse = noop) {
     // A helper function that sets the breakpoint.
     let doSetBreakpoint = (aCallback) => {
       const location = {
         url: url,
         line: line,
         column: column
       };
 
@@ -1762,19 +1765,17 @@ ThreadClient.prototype = {
           let root = this.client.mainRoot;
           bpClient = new BreakpointClient(
             this.client,
             aResponse.actor,
             location,
             root.traits.conditionalBreakpoints ? condition : undefined
           );
         }
-        if (aOnResponse) {
-          aOnResponse(aResponse, bpClient);
-        }
+        aOnResponse(aResponse, bpClient);
         if (aCallback) {
           aCallback();
         }
       });
     };
 
     // If the debuggee is paused, just set the breakpoint.
     if (this.paused) {
--- a/toolkit/devtools/server/actors/webapps.js
+++ b/toolkit/devtools/server/actors/webapps.js
@@ -257,17 +257,17 @@ WebappsActor.prototype = {
         aApp.manifest = manifest;
 
         // Needed to evict manifest cache on content side
         // (has to be dispatched first, otherwise other messages like
         // Install:Return:OK are going to use old manifest version)
         reg.broadcastMessage("Webapps:UpdateState", {
           app: aApp,
           manifest: manifest,
-          manifestURL: aApp.manifestURL
+          id: aApp.id
         });
         reg.broadcastMessage("Webapps:FireEvent", {
           eventType: ["downloadsuccess", "downloadapplied"],
           manifestURL: aApp.manifestURL
         });
         reg.broadcastMessage("Webapps:AddApp", { id: aId, app: aApp });
         reg.broadcastMessage("Webapps:Install:Return:OK", {
           app: aApp,