Merge m-c to fx-team.
authorRyan VanderMeulen <ryanvm@gmail.com>
Mon, 24 Feb 2014 16:22:48 -0500
changeset 170426 76b34c6338f4c39b70f00fc62a39691d0808d112
parent 170425 8a77900b6fbeedc7a8bf069ce0c34267af3a03e1 (current diff)
parent 170205 4b6103d24d1eeace490a82002e734c1cb73922ee (diff)
child 170427 3e84bd445574ff1a624b15f832d4c49883996f72
push id26288
push userryanvm@gmail.com
push dateTue, 25 Feb 2014 20:20:43 +0000
treeherdermozilla-central@22650589a724 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone30.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to fx-team.
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -7,17 +7,17 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <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="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <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="0eadf61ef60f13324fe8290d8c2b516d98230fdc"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e0f39c7179c8b297326c0e2313950610be1f5c52"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="022eadd5917615ff00c47eaaafa792b45e9c8a28"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a314508e397c8f1814228d36259ea8708034444e"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -6,17 +6,17 @@
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="97a5b461686757dbb8ecab2aac5903e41d2e1afe">
     <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="0eadf61ef60f13324fe8290d8c2b516d98230fdc"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e0f39c7179c8b297326c0e2313950610be1f5c52"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a314508e397c8f1814228d36259ea8708034444e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="905bfa3548eb75cf1792d0d8412b92113bbd4318"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="c3d7efc45414f1b44cd9c479bb2758c91c4707c0"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -7,17 +7,17 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <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="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <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="0eadf61ef60f13324fe8290d8c2b516d98230fdc"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e0f39c7179c8b297326c0e2313950610be1f5c52"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="022eadd5917615ff00c47eaaafa792b45e9c8a28"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a314508e397c8f1814228d36259ea8708034444e"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "35ef07425e808811af0462a6ba08c36409236846", 
+    "revision": "7e08d1fefea803f39389bb009e146aaa692d126a", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -6,17 +6,17 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <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="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <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="0eadf61ef60f13324fe8290d8c2b516d98230fdc"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e0f39c7179c8b297326c0e2313950610be1f5c52"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a314508e397c8f1814228d36259ea8708034444e"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
@@ -90,17 +90,17 @@
   <project name="platform/system/netd" path="system/netd" revision="2e226e6e636ca0a8cc4c51093e46f4baba1ffcce"/>
   <project name="platform/system/vold" path="system/vold" revision="8ac5eef8ea3a456b96d52ce2091bf6d814782d8c"/>
   <!-- hamachi specific things -->
   <project name="quic/lf/b2g/build" path="device/qcom/b2g_common" revision="43829d56fab6ad16883f1ac9f55ffb9aa5863543"/>
   <project name="quic/lf/b2g/external/jsmin" path="external/jsmin" revision="cec896f0affaa0226c02605ad28d42df1bc0e393"/>
   <project name="device/qcom/common" path="device/qcom/common" revision="d13aaf080177b7c48f243d51827db5c7a7873cd0"/>
   <project name="platform/vendor/qcom/msm7627a" path="device/qcom/msm7627a" revision="f06bcacc6f13cec895dc5d4c2385c076396194ec"/>
   <project name="android-device-hamachi" path="device/qcom/hamachi" remote="b2g" revision="9071ac8f0830979fe4a96ce47c7443d8adf0929d"/>
-  <project name="kernel/msm" path="kernel" revision="8072055e7094023e2cac8eea425bb785fe1d4066"/>
+  <project name="kernel/msm" path="kernel" revision="a6578b9cacf9079f2dcf5bfe77c31b1be18809e3"/>
   <project name="platform/hardware/qcom/camera" path="hardware/qcom/camera" revision="19933e5d182a4799c6217b19a18562193a419298"/>
   <project name="platform/hardware/qcom/display" path="hardware/qcom/display" revision="5a58382180c70d0c446badc9c9837918ab69ec60"/>
   <project name="platform/hardware/qcom/media" path="hardware/qcom/media" revision="20d83ab382a1f813702421e76c2f9f994585990e"/>
   <project name="platform/hardware/qcom/gps" path="hardware/qcom/gps" revision="1698e6e9ed7cf1d543508845fa05ed86c7e5e241"/>
   <project name="platform/hardware/msm7k" path="hardware/msm7k" revision="693e65da9905d88c23653b45800e6509143f6a78"/>
   <project name="platform/vendor/qcom-opensource/omx/mm-core" path="vendor/qcom/opensource/omx/mm-core" revision="0365db6af2d4df11184a421f97c5043db47a0c0d"/>
   <project name="platform/hardware/ril" path="hardware/ril" revision="ec40c0aee736052fc4fe01c1b8dc16929da5dc45"/>
 </manifest>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -5,17 +5,17 @@
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <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="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <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="0eadf61ef60f13324fe8290d8c2b516d98230fdc"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e0f39c7179c8b297326c0e2313950610be1f5c52"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/inari/sources.xml
+++ b/b2g/config/inari/sources.xml
@@ -7,17 +7,17 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <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="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <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="0eadf61ef60f13324fe8290d8c2b516d98230fdc"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e0f39c7179c8b297326c0e2313950610be1f5c52"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a314508e397c8f1814228d36259ea8708034444e"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="cd5dfce80bc3f0139a56b58aca633202ccaee7f8"/>
--- a/b2g/config/leo/sources.xml
+++ b/b2g/config/leo/sources.xml
@@ -6,17 +6,17 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <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="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <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="0eadf61ef60f13324fe8290d8c2b516d98230fdc"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e0f39c7179c8b297326c0e2313950610be1f5c52"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a314508e397c8f1814228d36259ea8708034444e"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/b2g/config/mako/sources.xml
+++ b/b2g/config/mako/sources.xml
@@ -6,17 +6,17 @@
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="97a5b461686757dbb8ecab2aac5903e41d2e1afe">
     <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="0eadf61ef60f13324fe8290d8c2b516d98230fdc"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e0f39c7179c8b297326c0e2313950610be1f5c52"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a314508e397c8f1814228d36259ea8708034444e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="905bfa3548eb75cf1792d0d8412b92113bbd4318"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="c3d7efc45414f1b44cd9c479bb2758c91c4707c0"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -6,17 +6,17 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <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="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <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="0eadf61ef60f13324fe8290d8c2b516d98230fdc"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e0f39c7179c8b297326c0e2313950610be1f5c52"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a314508e397c8f1814228d36259ea8708034444e"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/content/media/encoder/EncodedFrameContainer.h
+++ b/content/media/encoder/EncodedFrameContainer.h
@@ -43,23 +43,27 @@ class EncodedFrame
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(EncodedFrame)
 public:
   EncodedFrame() :
     mTimeStamp(0),
     mDuration(0),
     mFrameType(UNKNOWN)
   {}
   enum FrameType {
-    I_FRAME,      // intraframe
-    P_FRAME,      // predicted frame
-    B_FRAME,      // bidirectionally predicted frame
-    AUDIO_FRAME,  // audio frame
-    AAC_CSD,      // AAC codec specific data
-    AVC_CSD,      // AVC codec specific data
-    UNKNOWN       // FrameType not set
+    VP8_I_FRAME,      // VP8 intraframe
+    VP8_P_FRAME,      // VP8 predicted frame
+    OPUS_AUDIO_FRAME, // Opus audio frame
+    VORBIS_AUDIO_FRAME,
+    AVC_I_FRAME,
+    AVC_P_FRAME,
+    AVC_B_FRAME,
+    AVC_CSD,          // AVC codec specific data
+    AAC_AUDIO_FRAME,
+    AAC_CSD,          // AAC codec specific data
+    UNKNOWN           // FrameType not set
   };
   nsresult SwapInFrameData(nsTArray<uint8_t>& aData)
   {
     mFrameData.SwapElements(aData);
     return NS_OK;
   }
   nsresult SwapOutFrameData(nsTArray<uint8_t>& aData)
   {
--- a/content/media/encoder/OmxTrackEncoder.cpp
+++ b/content/media/encoder/OmxTrackEncoder.cpp
@@ -132,17 +132,17 @@ OmxVideoTrackEncoder::GetEncodedTrack(En
   mEncoder->GetNextEncodedFrame(&buffer, &outTimeStampUs, &outFlags,
                                 GET_ENCODED_VIDEO_FRAME_TIMEOUT);
   if (!buffer.IsEmpty()) {
     nsRefPtr<EncodedFrame> videoData = new EncodedFrame();
     if (outFlags & OMXCodecWrapper::BUFFER_CODEC_CONFIG) {
       videoData->SetFrameType(EncodedFrame::AVC_CSD);
     } else {
       videoData->SetFrameType((outFlags & OMXCodecWrapper::BUFFER_SYNC_FRAME) ?
-                              EncodedFrame::I_FRAME : EncodedFrame::P_FRAME);
+                              EncodedFrame::AVC_I_FRAME : EncodedFrame::AVC_P_FRAME);
     }
     rv = videoData->SwapInFrameData(buffer);
     NS_ENSURE_SUCCESS(rv, rv);
     videoData->SetTimeStamp(outTimeStampUs);
     aData.AppendEncodedFrame(videoData);
   }
 
   if (outFlags & OMXCodecWrapper::BUFFER_EOS) {
@@ -212,17 +212,17 @@ OmxAudioTrackEncoder::AppendEncodedFrame
     if (outFlags & OMXCodecWrapper::BUFFER_CODEC_CONFIG) { // codec specific data
       isCSD = true;
     } else if (outFlags & OMXCodecWrapper::BUFFER_EOS) { // last frame
       mEncodingComplete = true;
     }
 
     nsRefPtr<EncodedFrame> audiodata = new EncodedFrame();
     audiodata->SetFrameType(isCSD ?
-      EncodedFrame::AAC_CSD : EncodedFrame::AUDIO_FRAME);
+      EncodedFrame::AAC_CSD : EncodedFrame::AAC_AUDIO_FRAME);
     audiodata->SetTimeStamp(outTimeUs);
     rv = audiodata->SwapInFrameData(frameData);
     NS_ENSURE_SUCCESS(rv, rv);
     aContainer.AppendEncodedFrame(audiodata);
   }
 
   return NS_OK;
 }
--- a/content/media/encoder/OpusTrackEncoder.cpp
+++ b/content/media/encoder/OpusTrackEncoder.cpp
@@ -322,17 +322,17 @@ OpusTrackEncoder::GetEncodedTrack(Encode
              frameToCopy * mChannels * sizeof(AudioDataValue));
     }
 
     frameCopied += frameToCopy;
     iter.Next();
   }
 
   nsRefPtr<EncodedFrame> audiodata = new EncodedFrame();
-  audiodata->SetFrameType(EncodedFrame::AUDIO_FRAME);
+  audiodata->SetFrameType(EncodedFrame::OPUS_AUDIO_FRAME);
   int framesInPCM = frameCopied;
   if (mResampler) {
     nsAutoTArray<AudioDataValue, 9600> resamplingDest;
     // We want to consume all the input data, so we slightly oversize the
     // resampled data buffer so we can fit the output data in. We cannot really
     // predict the output frame count at each call.
     uint32_t outframes = frameCopied * kOpusSamplingRate / mSamplingRate + 1;
     uint32_t inframes = frameCopied;
--- a/content/media/encoder/VP8TrackEncoder.cpp
+++ b/content/media/encoder/VP8TrackEncoder.cpp
@@ -157,17 +157,17 @@ VP8TrackEncoder::GetMetadata()
 
   return meta.forget();
 }
 
 nsresult
 VP8TrackEncoder::GetEncodedPartitions(EncodedFrameContainer& aData)
 {
   vpx_codec_iter_t iter = nullptr;
-  EncodedFrame::FrameType frameType = EncodedFrame::P_FRAME;
+  EncodedFrame::FrameType frameType = EncodedFrame::VP8_P_FRAME;
   nsTArray<uint8_t> frameData;
   nsresult rv;
   const vpx_codec_cx_pkt_t *pkt = nullptr;
   while ((pkt = vpx_codec_get_cx_data(mVPXContext, &iter)) != nullptr) {
     switch (pkt->kind) {
       case VPX_CODEC_CX_FRAME_PKT: {
         // Copy the encoded data from libvpx to frameData
         frameData.AppendElements((uint8_t*)pkt->data.frame.buf,
@@ -176,17 +176,17 @@ VP8TrackEncoder::GetEncodedPartitions(En
       }
       default: {
         break;
       }
     }
     // End of frame
     if ((pkt->data.frame.flags & VPX_FRAME_IS_FRAGMENT) == 0) {
       if (pkt->data.frame.flags & VPX_FRAME_IS_KEY) {
-        frameType = EncodedFrame::I_FRAME;
+        frameType = EncodedFrame::VP8_I_FRAME;
       }
       break;
     }
   }
 
   if (!frameData.IsEmpty() &&
       (pkt->data.frame.pts == mEncodedTimestamp)) {
     // Copy the encoded data to aData.
--- a/content/media/encoder/VorbisTrackEncoder.cpp
+++ b/content/media/encoder/VorbisTrackEncoder.cpp
@@ -139,17 +139,17 @@ VorbisTrackEncoder::GetEncodedFrames(Enc
   // vorbis does some data preanalysis, then divvies up blocks for
   // more involved (potentially parallel) processing. Get a single
   // block for encoding now.
   while (vorbis_analysis_blockout(&mVorbisDsp, &mVorbisBlock) == 1) {
     ogg_packet oggPacket;
     if (vorbis_analysis(&mVorbisBlock, &oggPacket) == 0) {
       VORBISLOG("vorbis_analysis_blockout block size %d", oggPacket.bytes);
       EncodedFrame* audiodata = new EncodedFrame();
-      audiodata->SetFrameType(EncodedFrame::AUDIO_FRAME);
+      audiodata->SetFrameType(EncodedFrame::VORBIS_AUDIO_FRAME);
       nsTArray<uint8_t> frameData;
       frameData.AppendElements(oggPacket.packet, oggPacket.bytes);
       audiodata->SwapInFrameData(frameData);
       aData.AppendEncodedFrame(audiodata);
     }
   }
 }
 
--- a/content/media/encoder/fmp4_muxer/ISOMediaBoxes.cpp
+++ b/content/media/encoder/fmp4_muxer/ISOMediaBoxes.cpp
@@ -136,17 +136,17 @@ TrackRunBox::fillSampleTable()
       table_size += sizeof(uint32_t);
     }
 
     // Sample flags.
     sample_info_table[i].sample_flags = 0;
     if (flags.to_ulong() & flags_sample_flags_present) {
       sample_info_table[i].sample_flags =
         set_sample_flags(
-          (frames.ElementAt(i)->GetFrameType() == EncodedFrame::I_FRAME));
+          (frames.ElementAt(i)->GetFrameType() == EncodedFrame::AVC_I_FRAME));
       table_size += sizeof(uint32_t);
     }
 
     // Sample duration.
     sample_info_table[i].sample_duration = 0;
     if (flags.to_ulong() & flags_sample_duration_present) {
       // Calculate each frame's duration, it is decided by "current frame
       // timestamp - last frame timestamp".
--- a/content/media/encoder/fmp4_muxer/ISOMediaWriter.cpp
+++ b/content/media/encoder/fmp4_muxer/ISOMediaWriter.cpp
@@ -93,22 +93,22 @@ ISOMediaWriter::WriteEncodedTrack(const 
 
   if (!len) {
     // no frame? why bother to WriteEncodedTrack
     return NS_OK;
   }
   for (uint32_t i = 0; i < len; i++) {
     nsRefPtr<EncodedFrame> frame(aData.GetEncodedFrames()[i]);
     EncodedFrame::FrameType type = frame->GetFrameType();
-    if (type == EncodedFrame::AUDIO_FRAME ||
+    if (type == EncodedFrame::AAC_AUDIO_FRAME ||
         type == EncodedFrame::AAC_CSD) {
       frag = mAudioFragmentBuffer;
-    } else if (type == EncodedFrame::I_FRAME ||
-               type == EncodedFrame::P_FRAME ||
-               type == EncodedFrame::B_FRAME ||
+    } else if (type == EncodedFrame::AVC_I_FRAME ||
+               type == EncodedFrame::AVC_P_FRAME ||
+               type == EncodedFrame::AVC_B_FRAME ||
                type == EncodedFrame::AVC_CSD) {
       frag = mVideoFragmentBuffer;
     } else {
       MOZ_ASSERT(0);
       return NS_ERROR_FAILURE;
     }
 
     frag->AddFrame(frame);
--- a/content/media/ogg/OggWriter.cpp
+++ b/content/media/ogg/OggWriter.cpp
@@ -45,17 +45,17 @@ OggWriter::Init()
   return (rc == 0) ? NS_OK : NS_ERROR_NOT_INITIALIZED;
 }
 
 nsresult
 OggWriter::WriteEncodedTrack(const EncodedFrameContainer& aData,
                              uint32_t aFlags)
 {
   for (uint32_t i = 0; i < aData.GetEncodedFrames().Length(); i++) {
-    if (aData.GetEncodedFrames()[i]->GetFrameType() != EncodedFrame::AUDIO_FRAME) {
+    if (aData.GetEncodedFrames()[i]->GetFrameType() != EncodedFrame::OPUS_AUDIO_FRAME) {
       LOG("[OggWriter] wrong encoded data type!");
       return NS_ERROR_FAILURE;
     }
 
     nsresult rv = WriteEncodedData(aData.GetEncodedFrames()[i]->GetFrameData(),
                                    aData.GetEncodedFrames()[i]->GetDuration(),
                                    aFlags);
     if (NS_FAILED(rv)) {
--- a/content/media/webm/EbmlComposer.cpp
+++ b/content/media/webm/EbmlComposer.cpp
@@ -81,39 +81,39 @@ void EbmlComposer::FinishCluster()
 }
 
 void
 EbmlComposer::WriteSimpleBlock(EncodedFrame* aFrame)
 {
   EbmlGlobal ebml;
   ebml.offset = 0;
 
-  if (aFrame->GetFrameType() == EncodedFrame::FrameType::I_FRAME && mClusterHeaderIndex > 0) {
+  if (aFrame->GetFrameType() == EncodedFrame::FrameType::VP8_I_FRAME && mClusterHeaderIndex > 0) {
     FinishCluster();
   }
 
   mClusterBuffs.AppendElement();
   mClusterBuffs.LastElement().SetLength(aFrame->GetFrameData().Length() + DEFAULT_HEADER_SIZE);
   ebml.buf = mClusterBuffs.LastElement().Elements();
 
-  if (aFrame->GetFrameType() == EncodedFrame::FrameType::I_FRAME) {
+  if (aFrame->GetFrameType() == EncodedFrame::FrameType::VP8_I_FRAME) {
     EbmlLoc ebmlLoc;
     Ebml_StartSubElement(&ebml, &ebmlLoc, Cluster);
     mClusterHeaderIndex = mClusterBuffs.Length() - 1; // current cluster header array index
     mClusterLengthLoc = ebmlLoc.offset;
-    if (aFrame->GetFrameType() != EncodedFrame::FrameType::AUDIO_FRAME) {
+    if (aFrame->GetFrameType() != EncodedFrame::FrameType::VORBIS_AUDIO_FRAME) {
       mClusterTimecode = aFrame->GetTimeStamp() / PR_USEC_PER_MSEC;
     }
     Ebml_SerializeUnsigned(&ebml, Timecode, mClusterTimecode);
   }
 
-  if (aFrame->GetFrameType() != EncodedFrame::FrameType::AUDIO_FRAME) {
+  if (aFrame->GetFrameType() != EncodedFrame::FrameType::VORBIS_AUDIO_FRAME) {
     short timeCode = aFrame->GetTimeStamp() / PR_USEC_PER_MSEC - mClusterTimecode;
     writeSimpleBlock(&ebml, 0x1, timeCode, aFrame->GetFrameType() ==
-                     EncodedFrame::FrameType::I_FRAME,
+                     EncodedFrame::FrameType::VP8_I_FRAME,
                      0, 0, (unsigned char*)aFrame->GetFrameData().Elements(),
                      aFrame->GetFrameData().Length());
   } else {
     writeSimpleBlock(&ebml, 0x2, 0, false,
                      0, 0, (unsigned char*)aFrame->GetFrameData().Elements(),
                      aFrame->GetFrameData().Length());
   }
   MOZ_ASSERT_IF(ebml.offset > DEFAULT_HEADER_SIZE + aFrame->GetFrameData().Length(),
--- a/dom/apps/src/AppsServiceChild.jsm
+++ b/dom/apps/src/AppsServiceChild.jsm
@@ -3,76 +3,287 @@
  * 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: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 (!(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: {},
+
   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];
+    // XXX should we do this async and block callers if it's not yet there?
+    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.
+    // And 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;
         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: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 (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++) {
+      if (!apps[i].get()) {
+        apps.splice(i);
+      }
+    }
+
+    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();
+      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 (aMessage.error) {
+      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();
+        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);
--- 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 = Cu.createArrayIn(aWindow);
   for (let i = 0; i < aApps.length; i++) {
@@ -259,134 +260,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;
@@ -420,17 +408,17 @@ WebappsApplication.prototype = {
     this._ondownloadapplied = aCallback;
   },
 
   get ondownloadapplied() {
     return this._ondownloadapplied;
   },
 
   get downloadError() {
-    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() {
@@ -462,52 +450,56 @@ 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,
-                              appStatus: this._appStatus,
-                              requestID: this.getPromiseResolverId({
-                                resolve: aResolve,
-                                reject: aReject
-                              })});
+      cpmm.sendAsyncMessage("Webapps:Connect", {
+        keyword: aKeyword,
+        rules: aRules,
+        manifestURL: this.manifestURL,
+        outerWindowID: this._id,
+        appStatus: this._appStatus,
+        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"]);
@@ -546,134 +538,91 @@ 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;
     }
-
-    if (aMsg.error) {
-      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 ("_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);
         });
@@ -845,22 +794,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.origin);
         break;
       case "Webapps:Uninstall:Return:KO":
         Services.DOMRequest.fireError(req, "NOT_INSTALLED");
@@ -879,12 +824,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
@@ -1137,18 +1137,17 @@ this.DOMApplicationRegistry = {
         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;
+        return { webapps: this.webapps, manifests: this._manifestCache };
       case "Webapps:Download":
         this.startDownload(msg.manifestURL);
         break;
       case "Webapps:CancelDownload":
         this.cancelDownload(msg.manifestURL);
         break;
       case "Webapps:CheckForUpdate":
         this.checkForUpdate(msg, mm);
@@ -1283,17 +1282,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);
   },
@@ -1312,17 +1311,17 @@ this.DOMApplicationRegistry = {
       return;
     }
 
     // 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
       });
       return;
     }
 
@@ -1357,17 +1356,17 @@ this.DOMApplicationRegistry = {
           // Hosted app with no appcache, nothing to do, but we fire a
           // downloaded event.
           debug("No appcache found, sending 'downloaded' for " + aManifestURL);
           app.downloadAvailable = false;
           this._saveApps().then(() => {
             this.broadcastMessage("Webapps:UpdateState", {
               app: app,
               manifest: jsonManifest,
-              manifestURL: aManifestURL
+              id: app.id
             });
             this.broadcastMessage("Webapps:FireEvent", {
               eventType: "downloadsuccess",
               manifestURL: aManifestURL
             });
           });
         }
       });
@@ -1400,17 +1399,17 @@ this.DOMApplicationRegistry = {
           // Set state and fire events.
           app.downloading = false;
           app.downloadAvailable = false;
           app.readyToApplyDownload = true;
           app.updateTime = Date.now();
           DOMApplicationRegistry._saveApps().then(() => {
             DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", {
               app: app,
-              manifestURL: aManifestURL
+              id: app.id
             });
             DOMApplicationRegistry.broadcastMessage("Webapps:FireEvent", {
               eventType: "downloadsuccess",
               manifestURL: aManifestURL
             });
             if (app.installState == "pending") {
               // We restarted a failed download, apply it automatically.
               DOMApplicationRegistry.applyDownload(aManifestURL);
@@ -1494,17 +1493,17 @@ this.DOMApplicationRegistry = {
                 manifestURL: app.manifestURL },
               true);
           }
           this.updateDataStore(this.webapps[id].localId, app.origin,
                                app.manifestURL, aData, app.appStatus);
           this.broadcastMessage("Webapps:UpdateState", {
             app: app,
             manifest: aData,
-            manifestURL: app.manifestURL
+            id: app.id
           });
           this.broadcastMessage("Webapps:FireEvent", {
             eventType: "downloadapplied",
             manifestURL: app.manifestURL
           });
         });
       });
     });
@@ -1534,17 +1533,17 @@ this.DOMApplicationRegistry = {
     aApp.progress = 0;
     DOMApplicationRegistry._saveApps().then(() => {
       DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", {
         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,
@@ -1584,16 +1583,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.
@@ -1613,71 +1613,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);
         debug("onlyCheckAppCache - launch updateSvc.checkForUpdate for " +
               helper.fullAppcachePath());
         updateSvc.checkForUpdate(Services.io.newURI(helper.fullAppcachePath(), null, null),
                                  app.localId, false, updateObserver);
@@ -1727,17 +1723,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
                 });
               });
             }
@@ -1754,17 +1750,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 {
@@ -1862,17 +1858,17 @@ this.DOMApplicationRegistry = {
     // A package is available: set downloadAvailable to fire the matching
     // event.
     aApp.downloadAvailable = true;
     aApp.downloadSize = manifest.size;
     aApp.updateManifest = aNewManifest;
     this._saveApps().then(() => {
       this.broadcastMessage("Webapps:UpdateState", {
         app: aApp,
-        manifestURL: aApp.manifestURL
+        id: aApp.id
       });
       this.broadcastMessage("Webapps:FireEvent", {
         eventType: "downloadavailable",
         manifestURL: aApp.manifestURL,
         requestID: aData.requestID
       });
     });
   },
@@ -1926,17 +1922,17 @@ this.DOMApplicationRegistry = {
     // Update the registry.
     this.webapps[aId] = aApp;
     this._saveApps().then(() => {
       let reg = DOMApplicationRegistry;
       if (!manifest.appcache_path) {
         reg.broadcastMessage("Webapps:UpdateState", {
           app: aApp,
           manifest: aApp.manifest,
-          manifestURL: aApp.manifestURL
+          id: aApp.id
         });
         reg.broadcastMessage("Webapps:FireEvent", {
           eventType: "downloadapplied",
           manifestURL: aApp.manifestURL,
           requestID: aData.requestID
         });
       } else {
         // Check if the appcache is updatable, and send "downloadavailable" or
@@ -1948,17 +1944,17 @@ this.DOMApplicationRegistry = {
             let eventType =
               aTopic == "offline-cache-update-available" ? "downloadavailable"
                                                          : "downloadapplied";
             aApp.downloadAvailable = (eventType == "downloadavailable");
             reg._saveApps().then(() => {
               reg.broadcastMessage("Webapps:UpdateState", {
                 app: aApp,
                 manifest: aApp.manifest,
-                manifestURL: aApp.manifestURL
+                id: aApp.id
               });
               reg.broadcastMessage("Webapps:FireEvent", {
                 eventType: eventType,
                 manifestURL: aApp.manifestURL,
                 requestID: aData.requestID
               });
             });
           }
@@ -2213,18 +2209,18 @@ this.DOMApplicationRegistry = {
   },
 
   // This function is called after we called the onsuccess callback on the
   // content side. This let the webpage the opportunity to set event handlers
   // on the app before we start firing progress events.
   queuedDownload: {},
   queuedPackageDownload: {},
 
-onInstallSuccessAck: function onInstallSuccessAck(aManifestURL,
-                                                  aDontNeedNetwork) {
+  onInstallSuccessAck: function onInstallSuccessAck(aManifestURL,
+                                                    aDontNeedNetwork) {
     // If we are offline, register to run when we'll be online.
     if ((Services.io.offline) && !aDontNeedNetwork) {
       let onlineWrapper = {
         observe: function(aSubject, aTopic, aData) {
           Services.obs.removeObserver(onlineWrapper,
                                       "network:offline-status-changed");
           DOMApplicationRegistry.onInstallSuccessAck(aManifestURL);
         }
@@ -2344,17 +2340,16 @@ onInstallSuccessAck: function onInstallS
       localId = this._nextLocalId();
     }
 
     let app = this._setupApp(aData, id);
 
     let jsonManifest = aData.isPackage ? app.updateManifest : app.manifest;
     this._writeManifestFile(id, aData.isPackage, jsonManifest);
 
-    debug("app.origin: " + app.origin);
     let manifest = new ManifestHelper(jsonManifest, app.origin);
 
     let appObject = this._cloneApp(aData, app, manifest, id, localId);
 
     this.webapps[id] = appObject;
 
     // For package apps, the permissions are not in the mini-manifest, so
     // don't update the permissions yet.
@@ -2388,16 +2383,18 @@ onInstallSuccessAck: function onInstallS
         profileDir: aProfileDir
       }
     }
 
     // We notify about the successful installation via mgmt.oninstall and the
     // corresponging DOMRequest.onsuccess event as soon as the app is properly
     // saved in the registry.
     this._saveApps().then(() => {
+      aData.isPackage ? appObject.updateManifest = jsonManifest :
+                        appObject.manifest = jsonManifest;
       this.broadcastMessage("Webapps:AddApp", { id: id, app: appObject });
       if (aData.isPackage && aData.autoInstall) {
         // Skip directly to onInstallSuccessAck, since there isn't
         // a WebappsRegistry to receive Webapps:Install:Return:OK and respond
         // Webapps:Install:Return:Ack when an app is being auto-installed.
         this.onInstallSuccessAck(app.manifestURL);
       } else {
         // Broadcast Webapps:Install:Return:OK so the WebappsRegistry can notify
@@ -2504,17 +2501,17 @@ onInstallSuccessAck: function onInstallS
       }
 
       this.updateDataStore(this.webapps[aId].localId, aNewApp.origin,
                            aNewApp.manifestURL, aManifest, aNewApp.appStatus);
 
       this.broadcastMessage("Webapps:UpdateState", {
         app: app,
         manifest: aManifest,
-        manifestURL: aNewApp.manifestURL
+        id: app.id
       });
       this.broadcastMessage("Webapps:FireEvent", {
         eventType: ["downloadsuccess", "downloadapplied"],
         manifestURL: aNewApp.manifestURL
       });
       if (aInstallSuccessCallback) {
         aInstallSuccessCallback(aManifest, zipFile.path);
       }
@@ -2614,24 +2611,21 @@ onInstallSuccessAck: function onInstallS
 
       debug("About to download " + fullPackagePath);
 
       let requestChannel = this._getRequestChannel(fullPackagePath,
                                                    isLocalFileInstall,
                                                    oldApp,
                                                    aNewApp);
 
-      AppDownloadManager.add(
-        aNewApp.manifestURL,
-        {
-          channel: requestChannel,
-          appId: id,
-          previousState: aIsUpdate ? "installed" : "pending"
-        }
-      );
+      AppDownloadManager.add(aNewApp.manifestURL, {
+        channel: requestChannel,
+        appId: id,
+        previousState: aIsUpdate ? "installed" : "pending"
+      });
 
       // We set the 'downloading' flag to true right before starting the fetch.
       oldApp.downloading = true;
 
       // We determine the app's 'installState' according to its previous
       // state. Cancelled download should remain as 'pending'. Successfully
       // installed apps should morph to 'updating'.
       oldApp.installState = aIsUpdate ? "updating" : "pending";
@@ -2644,17 +2638,17 @@ onInstallSuccessAck: function onInstallS
 
       let responseStatus = requestChannel.responseStatus;
       let oldPackage = (responseStatus == 304 || hash == oldApp.packageHash);
 
       if (oldPackage) {
         debug("package's etag or hash unchanged; sending 'applied' event");
         // The package's Etag or hash has not changed.
         // We send a "applied" event right away.
-        this._sendAppliedEvent(aNewApp, oldApp, id);
+        this._sendAppliedEvent(oldApp);
         return;
       }
 
       let newManifest = yield this._openAndReadPackage(zipFile, oldApp, aNewApp,
               isLocalFileInstall, aIsUpdate, aManifest, requestChannel, hash);
 
       AppDownloadManager.remove(aNewApp.manifestURL);
 
@@ -2784,17 +2778,17 @@ onInstallSuccessAck: function onInstallS
     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) {
@@ -2912,49 +2906,49 @@ onInstallSuccessAck: function onInstallS
    * 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
    */
-  _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 = {};
+      aApp.manifestHash = aApp.staged.manifestHash;
+      aApp.etag = aApp.staged.etag || aApp.etag;
+      aApp.staged = {};
       // Move the staged update manifest to a non staged one.
-      let dirPath = this._getAppDir(aId).path;
+      let dirPath = this._getAppDir(aApp.id).path;
 
       // We don't really mind much if this fails.
       OS.File.move(OS.Path.join(dirPath, "staged-update.webapp"),
                    OS.Path.join(dirPath, "update.webapp"));
     }
 
     // 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*() {
@@ -3323,17 +3317,17 @@ onInstallSuccessAck: function onInstallS
     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);
   },
@@ -3873,17 +3867,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) {
@@ -3905,30 +3899,30 @@ 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) {
       debug("Offlinecache setError to " + aError);
       app.downloading = false;
       DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", {
         app: app,
-        manifestURL: app.manifestURL
+        id: app.id
       });
       DOMApplicationRegistry.broadcastMessage("Webapps:FireEvent", {
         error: aError,
         eventType: "downloaderror",
         manifestURL: app.manifestURL
       });
       mustSave = true;
     }
--- a/dom/apps/tests/test_packaged_app_common.js
+++ b/dom/apps/tests/test_packaged_app_common.js
@@ -94,16 +94,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,26 +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 steps = [
   function() {
     // Set up
     PackagedTestHelper.launchableValue =
       SpecialPowers.setAllAppsLaunchable(true);
@@ -170,17 +169,17 @@ var steps = [
   },
   function() {
     ok(true, "== 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") {
           ok(true, "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
@@ -242,9 +242,9 @@ function runTest() {
   finish();
 }
 
 addLoadEvent(go);
 
 </script>
 </pre>
 </body>
-</html>
\ No newline at end of file
+</html>
--- a/dom/camera/CameraControlImpl.cpp
+++ b/dom/camera/CameraControlImpl.cpp
@@ -235,17 +235,17 @@ CameraControlImpl::OnError(CameraControl
   RwLockAutoEnterRead lock(mListenerLock);
 
 #ifdef PR_LOGGING
   const char* error[] = {
     "api-failed",
     "init-failed",
     "invalid-configuration",
     "service-failed",
-    "set-picture-size-failred",
+    "set-picture-size-failed",
     "set-thumbnail-size-failed",
     "unknown"
   };
   const char* context[] = {
     "StartCamera",
     "StopCamera",
     "AutoFocus",
     "TakePicture",
--- a/dom/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -119,17 +119,17 @@ StartRecordingHelper::HandleEvent(nsIDOM
 {
   nsString eventType;
   aEvent->GetType(eventType);
 
   mDOMCameraControl->OnCreatedFileDescriptor(eventType.EqualsLiteral("success"));
   return NS_OK;
 }
 
-NS_IMPL_ISUPPORTS0(mozilla::StartRecordingHelper)
+NS_IMPL_ISUPPORTS1(mozilla::StartRecordingHelper, nsIDOMEventListener)
 
 nsDOMCameraControl::DOMCameraConfiguration::DOMCameraConfiguration()
   : CameraConfiguration()
   , mMaxFocusAreas(0)
   , mMaxMeteringAreas(0)
 {
   MOZ_COUNT_CTOR(nsDOMCameraControl::DOMCameraConfiguration);
 }
@@ -616,17 +616,17 @@ nsDOMCameraControl::GetExposureCompensat
   return compensation;
 }
 
 int32_t
 nsDOMCameraControl::SensorAngle()
 {
   MOZ_ASSERT(mCameraControl);
 
-  int32_t angle;
+  int32_t angle = 0;
   mCameraControl->Get(CAMERA_PARAM_SENSORANGLE, angle);
   return angle;
 }
 
 already_AddRefed<CameraShutterCallback>
 nsDOMCameraControl::GetOnShutter()
 {
   nsCOMPtr<CameraShutterCallback> cb = mOnShutterCb;
@@ -1155,16 +1155,18 @@ nsDOMCameraControl::OnTakePictureComplet
   nsCOMPtr<CameraTakePictureCallback> cb = mTakePictureOnSuccessCb.forget();
   mTakePictureOnErrorCb = nullptr;
   cb->Call(aPicture, ignored);
 }
 
 void
 nsDOMCameraControl::OnError(CameraControlListener::CameraErrorContext aContext, const nsAString& aError)
 {
+  DOM_CAMERA_LOGI("DOM OnError context=%d, error='%s'\n", aContext,
+    NS_LossyConvertUTF16toASCII(aError).get());
   MOZ_ASSERT(NS_IsMainThread());
 
   nsCOMPtr<CameraErrorCallback>* errorCb;
   switch (aContext) {
     case CameraControlListener::kInStartCamera:
       mGetCameraOnSuccessCb = nullptr;
       errorCb = &mGetCameraOnErrorCb;
       break;
@@ -1190,42 +1192,54 @@ nsDOMCameraControl::OnError(CameraContro
       break;
 
     case CameraControlListener::kInStartRecording:
       mStartRecordingOnSuccessCb = nullptr;
       errorCb = &mStartRecordingOnErrorCb;
       break;
 
     case CameraControlListener::kInStopRecording:
-      NS_WARNING("Failed to stop recording (which shouldn't happen)!");
-      MOZ_CRASH();
-      break;
+      // This method doesn't have any callbacks, so all we can do is log the
+      // failure. This only happens after the hardware has been released.
+      NS_WARNING("Failed to stop recording");
+      return;
 
     case CameraControlListener::kInStartPreview:
-      NS_WARNING("Failed to (re)start preview!");
-      MOZ_CRASH();
-      break;
+      // This method doesn't have any callbacks, so all we can do is log the
+      // failure. This only happens after the hardware has been released.
+      NS_WARNING("Failed to (re)start preview");
+      return;
 
     case CameraControlListener::kInUnspecified:
       if (aError.EqualsASCII("ErrorServiceFailed")) {
         // If the camera service fails, we will get preview-stopped and
-        //  hardware-closed events, so nothing to do here.
+        // hardware-closed events, so nothing to do here.
+        NS_WARNING("Camera service failed");
+        return;
+      }
+      if (aError.EqualsASCII("ErrorSetPictureSizeFailed") ||
+          aError.EqualsASCII("ErrorSetThumbnailSizeFailed")) {
+        // We currently don't handle attribute setter failure. Practically,
+        // this only ever happens if a setter is called after the hardware
+        // has gone away before an asynchronous set gets to happen, so we
+        // swallow these.
+        NS_WARNING("Failed to set either picture or thumbnail size");
         return;
       }
       // fallthrough
 
     default:
       MOZ_ASSUME_UNREACHABLE("Error occurred in unanticipated camera state");
       return;
   }
 
   MOZ_ASSERT(errorCb);
 
   if (!*errorCb) {
-    DOM_CAMERA_LOGW("DOM No error handler for error '%s' at %d\n",
+    DOM_CAMERA_LOGW("DOM No error handler for error '%s' in context=%d\n",
       NS_LossyConvertUTF16toASCII(aError).get(), aContext);
     return;
   }
 
   // kung-fu death grip
   nsCOMPtr<CameraErrorCallback> cb = (*errorCb).forget();
   ErrorResult ignored;
   cb->Call(aError, ignored);
--- a/dom/camera/DOMCameraControlListener.cpp
+++ b/dom/camera/DOMCameraControlListener.cpp
@@ -323,16 +323,24 @@ DOMCameraControlListener::OnError(Camera
     {
       nsString error;
 
       switch (mError) {
         case kErrorServiceFailed:
           error = NS_LITERAL_STRING("ErrorServiceFailed");
           break;
 
+        case kErrorSetPictureSizeFailed:
+          error = NS_LITERAL_STRING("ErrorSetPictureSizeFailed");
+          break;
+
+        case kErrorSetThumbnailSizeFailed:
+          error = NS_LITERAL_STRING("ErrorSetThumbnailSizeFailed");
+          break;
+
         case kErrorApiFailed:
           // XXXmikeh legacy error placeholder
           error = NS_LITERAL_STRING("FAILURE");
           break;
 
         default:
           error = NS_LITERAL_STRING("ErrorUnknown");
           break;
--- a/dom/camera/GonkCameraControl.cpp
+++ b/dom/camera/GonkCameraControl.cpp
@@ -450,28 +450,27 @@ nsGonkCameraControl::Get(uint32_t aKey, 
   return mParams.Get(aKey, aSize);
 }
 
 // Signed int parameter accessors.
 nsresult
 nsGonkCameraControl::Set(uint32_t aKey, int aValue)
 {
   if (aKey == CAMERA_PARAM_PICTURE_ROTATION) {
+    RETURN_IF_NO_CAMERA_HW();
     aValue = RationalizeRotation(aValue + mCameraHw->GetSensorOrientation());
   }
   return SetAndPush(aKey, aValue);
 }
 
 nsresult
 nsGonkCameraControl::Get(uint32_t aKey, int& aRet)
 {
   if (aKey == CAMERA_PARAM_SENSORANGLE) {
-    if (!mCameraHw.get()) {
-      return NS_ERROR_NOT_AVAILABLE;
-    }
+    RETURN_IF_NO_CAMERA_HW();
     aRet = mCameraHw->GetSensorOrientation();
     return NS_OK;
   }
 
   return mParams.Get(aKey, aRet);
 }
 
 // GPS location parameter accessors.
@@ -598,17 +597,17 @@ nsGonkCameraControl::SetThumbnailSizeImp
 
   if (smallestDeltaIndex == UINT32_MAX) {
     DOM_CAMERA_LOGW("Unable to find a thumbnail size close to %ux%u\n",
       aSize.width, aSize.height);
     return NS_ERROR_INVALID_ARG;
   }
 
   Size size = supportedSizes[smallestDeltaIndex];
-  DOM_CAMERA_LOGI("camera-param set picture-size = %ux%u (requested %ux%u)\n",
+  DOM_CAMERA_LOGI("camera-param set thumbnail-size = %ux%u (requested %ux%u)\n",
     size.width, size.height, aSize.width, aSize.height);
   if (size.width > INT32_MAX || size.height > INT32_MAX) {
     DOM_CAMERA_LOGE("Supported thumbnail size is too big, no change\n");
     return NS_ERROR_FAILURE;
   }
 
   return SetAndPush(CAMERA_PARAM_THUMBNAILSIZE, size);
 }
@@ -1032,37 +1031,23 @@ nsGonkCameraControl::SetPreviewSize(cons
       delta = abs((long int)(size.width - aSize.width));
       if (delta < minSizeDelta) {
         minSizeDelta = delta;
         best = size;
       }
     }
   }
 
-  {
-    ICameraControlParameterSetAutoEnter set(this);
-
-    // Some camera drivers will ignore our preview size if it's larger
-    // that the currently set video recording size, so we need to set
-    // both here just in case.
-    rv = SetAndPush(CAMERA_PARAM_PREVIEWSIZE, best);
-    if (NS_FAILED(rv)) {
-      DOM_CAMERA_LOGE("Failed to set picture mode preview size (0x%x)\n", rv);
-      return rv;
-    }
-
-    rv = SetAndPush(CAMERA_PARAM_VIDEOSIZE, best);
-    if (NS_FAILED(rv)) {
-      DOM_CAMERA_LOGE("Failed to bump up picture mode video size (0x%x)\n", rv);
-      return rv;
-    }
-  }
-
+  // Some camera drivers will ignore our preview size if it's larger
+  // that the currently set video recording size, so we need to set
+  // both here just in case.
+  mParams.Set(CAMERA_PARAM_PREVIEWSIZE, best);
+  mParams.Set(CAMERA_PARAM_VIDEOSIZE, best);
   mCurrentConfiguration.mPreviewSize = best;
-  return NS_OK;
+  return PushParameters();
 }
 
 nsresult
 nsGonkCameraControl::SetupVideoMode(const nsAString& aProfile)
 {
   DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
 
   // read preferences for camcorder
--- a/dom/camera/test/mochitest.ini
+++ b/dom/camera/test/mochitest.ini
@@ -1,8 +1,9 @@
 [DEFAULT]
 support-files = camera_common.js
 
 [test_camera.html]
 [test_camera_2.html]
 [test_camera_3.html]
 [test_camera_hardware_init_failure.html]
 [test_camera_hardware_failures.html]
+[test_bug975472.html]
new file mode 100644
--- /dev/null
+++ b/dom/camera/test/test_bug975472.html
@@ -0,0 +1,222 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for bug 975472</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<video id="viewfinder" width="200" height="200" autoplay></video>
+<img src="#" alt="This image is going to load" id="testimage"/>
+<script class="testbody" type="text/javascript;version=1.7">
+
+var whichCamera = navigator.mozCameras.getListOfCameras()[0];
+var config = {
+  mode: 'picture',
+  recorderProfile: 'cif',
+  previewSize: {
+    width: 352,
+    height: 288
+  }
+};
+var options = {
+  rotation: 0,
+  position: {
+    latitude: 43.645687,
+    longitude: -79.393661
+  },
+  dateTime: Date.now()
+};
+
+function onError(e) {
+  ok(false, "Error" + JSON.stringify(e));
+}
+function next() {
+  Camera.nextTest();
+}
+
+// The array of tests
+var tests = [
+  {
+    key: "release-after-release",
+    func: function testAutoFocus(camera) {
+      function onSuccess(success) {
+        ok(true, "release() succeeded");
+        next();
+      }
+      function onError(error) {
+        ok(false, "release() failed with: " + error);
+      }
+      camera.release(onSuccess, onError);
+    }
+  },
+  {
+    key: "set-picture-size-after-release",
+    func: function testSetPictureSize(camera) {
+      camera.pictureSize = { width: 0, height: 0 };
+      next();
+    }
+  },
+  {
+    key: "set-thumbnail-size-after-release",
+    func: function testSetThumbnailSize(camera) {
+      camera.thumbnailSize = { width: 0, height: 0 };
+      next();
+    }
+  },
+  {
+    key: "get-sensor-angle-after-release",
+    func: function testGetSensorAngle(camera) {
+      ok(camera.sensorAngle == 0, "camera.sensorAngle = " + camera.sensorAngle);
+      next();
+    }
+  },
+  {
+    key: "resume-preview-after-release",
+    func: function testResumePreview(camera) {
+      camera.resumePreview();
+      next();
+    }
+  },
+  {
+    key: "auto-focus-after-release",
+    func: function testAutoFocus(camera) {
+      function onSuccess(success) {
+        ok(false, "autoFocus() succeeded incorrectly");
+      }
+      function onError(error) {
+        ok(true, "autoFocus() failed correctly with: " + error);
+        next();
+      }
+      camera.autoFocus(onSuccess, onError);
+    }
+  },
+  {
+    key: "take-picture-after-release",
+    func: function testTakePicture(camera) {
+      function onSuccess(picture) {
+        ok(false, "takePicture() succeeded incorrectly");
+      }
+      function onError(error) {
+        ok(true, "takePicture() failed correctly with: " + error);
+        next();
+      }
+      camera.takePicture(null, onSuccess, onError);
+    }
+  },
+  {
+    key: "start-recording-after-release",
+    func: function testStartRecording(camera) {
+      function onSuccess(picture) {
+        ok(false, "startRecording() process succeeded incorrectly");
+      }
+      function onError(error) {
+        ok(true, "startRecording() process failed correctly with: " + error);
+        next();
+      }
+      var recordingOptions = {
+        profile: 'cif',
+        rotation: 0
+      };
+      camera.startRecording(recordingOptions,
+                            navigator.getDeviceStorage('videos'),
+                            'bug975472.mp4',
+                            onSuccess, onError);
+    }
+  },
+  {
+    key: "stop-recording-after-release",
+    func: function testStopRecording(camera) {
+      camera.stopRecording();
+      next();
+    }
+  },
+  {
+    key: "set-configuration-after-release",
+    func: function testSetConfiguration(camera) {
+      function onSuccess(picture) {
+        ok(false, "setConfiguration() process succeeded incorrectly");
+      }
+      function onError(error) {
+        ok(true, "setConfiguration() process failed correctly with: " + error);
+        next();
+      }
+      camera.setConfiguration(config, onSuccess, onError);
+    }
+  },
+];
+
+var testGenerator = function() {
+  for (var i = 0; i < tests.length; ++i ) {
+    yield tests[i];
+  }
+}();
+
+var Camera = {
+  cameraObj: null,
+  _otherPictureSize: null,
+  get viewfinder() {
+    return document.getElementById('viewfinder');
+  },
+  onCameraReady: function () {
+    Camera.nextTest = function() {
+      try {
+        var t = testGenerator.next();
+        info("test: " + t.key);
+        t.func(Camera.cameraObj);
+      } catch(e) {
+        if (e instanceof StopIteration) {
+          SimpleTest.finish();
+        } else {
+          throw e;
+        }
+      }
+    };
+    // Release the camera hardware, and call all of the asynchronous methods
+    // to make sure they properly handle being in this state.
+    Camera.cameraObj.release();
+    next();
+  },
+  release: function release() {
+    cameraObj = null;
+  },
+  start: function run_test() {
+    function onSuccess(camera, config) {
+      Camera.cameraObj = camera;
+      Camera.viewfinder.mozSrcObject = camera;
+      Camera.viewfinder.play();
+      ok(camera.capabilities.pictureSizes.length > 0,
+        "capabilities.pictureSizes.length = " +
+        camera.capabilities.pictureSizes.length);
+      Camera._otherPictureSize = camera.capabilities.pictureSizes.slice(-1)[0];
+      camera.pictureSize = camera.capabilities.pictureSizes[0];
+      options.pictureSize = Camera._otherPictureSize;
+      options.fileFormat = camera.capabilities.fileFormats[0];
+      info("getCamera callback, setting pictureSize = " + options.pictureSize.toSource());
+      Camera.cameraObj.onPreviewStateChange = function(state) {
+        if (state === 'started') {
+          info("viewfinder is ready and playing");
+          Camera.cameraObj.onPreviewStateChange = null;
+          Camera.onCameraReady();
+        }
+      };
+    };
+    navigator.mozCameras.getCamera(whichCamera, config, onSuccess, onError);
+  }
+}
+
+SimpleTest.waitForExplicitFinish();
+
+window.addEventListener('beforeunload', function() {
+  Camera.viewfinder.mozSrcObject = null;
+  Camera.cameraObj.release();
+  Camera.cameraObj = null;
+});
+
+Camera.start();
+
+</script>
+</body>
+
+</html>
--- a/dom/contacts/fallback/ContactDB.jsm
+++ b/dom/contacts/fallback/ContactDB.jsm
@@ -789,16 +789,31 @@ ContactDB.prototype = {
       debug("Contacts DB upgrade error: " + why);
       aTransaction.abort();
     }
 
     if (aNewVersion > steps.length) {
       fail("No migration steps for the new version!");
     }
 
+    this.cpuLock = Cc["@mozilla.org/power/powermanagerservice;1"]
+                     .getService(Ci.nsIPowerManagerService)
+                     .newWakeLock("cpu");
+
+    function unlockCPU() {
+      if (outer.cpuLock) {
+        if (DEBUG) debug("unlocking cpu wakelock");
+        outer.cpuLock.unlock();
+        outer.cpuLock = null;
+      }
+    }
+
+    aTransaction.addEventListener("complete", unlockCPU);
+    aTransaction.addEventListener("abort", unlockCPU);
+
     next();
   },
 
   makeImport: function makeImport(aContact) {
     let contact = {properties: {}};
 
     contact.search = {
       name:            [],
--- a/dom/system/gonk/NetworkService.js
+++ b/dom/system/gonk/NetworkService.js
@@ -7,17 +7,17 @@
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 
 const NETWORKSERVICE_CONTRACTID = "@mozilla.org/network/service;1";
-const NETWORKSERVICE_CID = Components.ID("{c14cabaf-bb8e-470d-a2f1-2cb6de6c5e5c}");
+const NETWORKSERVICE_CID = Components.ID("{baec696c-c78d-42db-8b44-603f8fbfafb4}");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gNetworkWorker",
                                    "@mozilla.org/network/worker;1",
                                    "nsINetworkWorker");
 
 // 1xx - Requested action is proceeding
 const NETD_COMMAND_PROCEEDING   = 100;
 // 2xx - Requested action has been successfully completed
@@ -360,16 +360,40 @@ NetworkService.prototype = {
       cmd: "removeHostRoute",
       ifname: network.name,
       gateway: network.gateway,
       hostnames: hosts
     };
     this.controlMessage(options, function() {});
   },
 
+  addSecondaryRoute: function(ifname, route) {
+    if(DEBUG) debug("Going to add route to secondary table on " + ifname);
+    let options = {
+      cmd: "addSecondaryRoute",
+      ifname: ifname,
+      ip: route.ip,
+      prefix: route.prefix,
+      gateway: route.gateway
+    };
+    this.controlMessage(options, function() {});
+  },
+
+  removeSecondaryRoute: function(ifname, route) {
+    if(DEBUG) debug("Going to remove route from secondary table on " + ifname);
+    let options = {
+      cmd: "removeSecondaryRoute",
+      ifname: ifname,
+      ip: route.ip,
+      prefix: route.prefix,
+      gateway: route.gateway
+    };
+    this.controlMessage(options, function() {});
+  },
+
   setNetworkProxy: function(network) {
     try {
       if (!network.httpProxyHost || network.httpProxyHost === "") {
         // Sets direct connection to internet.
         Services.prefs.clearUserPref("network.proxy.type");
         Services.prefs.clearUserPref("network.proxy.share_proxy_settings");
         Services.prefs.clearUserPref("network.proxy.http");
         Services.prefs.clearUserPref("network.proxy.http_port");
--- a/dom/system/gonk/NetworkService.manifest
+++ b/dom/system/gonk/NetworkService.manifest
@@ -1,3 +1,3 @@
 # NetworkService.js
-component {c14cabaf-bb8e-470d-a2f1-2cb6de6c5e5c} NetworkService.js
-contract @mozilla.org/network/service;1 {c14cabaf-bb8e-470d-a2f1-2cb6de6c5e5c}
+component {baec696c-c78d-42db-8b44-603f8fbfafb4} NetworkService.js
+contract @mozilla.org/network/service;1 {baec696c-c78d-42db-8b44-603f8fbfafb4}
--- a/dom/system/gonk/NetworkUtils.cpp
+++ b/dom/system/gonk/NetworkUtils.cpp
@@ -902,16 +902,20 @@ void NetworkUtils::ExecuteCommand(Networ
   } else if (aOptions.mCmd.EqualsLiteral("removeDefaultRoute")) {
     removeDefaultRoute(aOptions);
   } else if (aOptions.mCmd.EqualsLiteral("addHostRoute")) {
     addHostRoute(aOptions);
   } else if (aOptions.mCmd.EqualsLiteral("removeHostRoute")) {
     removeHostRoute(aOptions);
   } else if (aOptions.mCmd.EqualsLiteral("removeHostRoutes")) {
     removeHostRoutes(aOptions);
+  } else if (aOptions.mCmd.EqualsLiteral("addSecondaryRoute")) {
+    addSecondaryRoute(aOptions);
+  } else if (aOptions.mCmd.EqualsLiteral("removeSecondaryRoute")) {
+    removeSecondaryRoute(aOptions);
   } else if (aOptions.mCmd.EqualsLiteral("getNetworkInterfaceStats")) {
     getNetworkInterfaceStats(aOptions);
   } else if (aOptions.mCmd.EqualsLiteral("setNetworkInterfaceAlarm")) {
     setNetworkInterfaceAlarm(aOptions);
   } else if (aOptions.mCmd.EqualsLiteral("enableNetworkInterfaceAlarm")) {
     enableNetworkInterfaceAlarm(aOptions);
   } else if (aOptions.mCmd.EqualsLiteral("disableNetworkInterfaceAlarm")) {
     disableNetworkInterfaceAlarm(aOptions);
@@ -1128,16 +1132,44 @@ bool NetworkUtils::removeNetworkRoute(Ne
   addr.s_addr = subnet;
   const char* dst = inet_ntoa(addr);
 
   mNetUtils->do_ifc_remove_default_route(GET_CHAR(mIfname));
   mNetUtils->do_ifc_remove_route(GET_CHAR(mIfname), dst, prefixLength, gateway);
   return true;
 }
 
+bool NetworkUtils::addSecondaryRoute(NetworkParams& aOptions)
+{
+  char command[MAX_COMMAND_SIZE];
+  snprintf(command, MAX_COMMAND_SIZE - 1,
+           "interface route add %s secondary %s %s %s",
+           GET_CHAR(mIfname),
+           GET_CHAR(mIp),
+           GET_CHAR(mPrefix),
+           GET_CHAR(mGateway));
+
+  doCommand(command, nullptr, nullptr);
+  return true;
+}
+
+bool NetworkUtils::removeSecondaryRoute(NetworkParams& aOptions)
+{
+  char command[MAX_COMMAND_SIZE];
+  snprintf(command, MAX_COMMAND_SIZE - 1,
+           "interface route remove %s secondary %s %s %s",
+           GET_CHAR(mIfname),
+           GET_CHAR(mIp),
+           GET_CHAR(mPrefix),
+           GET_CHAR(mGateway));
+
+  doCommand(command, nullptr, nullptr);
+  return true;
+}
+
 bool NetworkUtils::getNetworkInterfaceStats(NetworkParams& aOptions)
 {
   DEBUG("getNetworkInterfaceStats: %s", GET_CHAR(mIfname));
   aOptions.mRxBytes = -1;
   aOptions.mTxBytes = -1;
 
   RUN_CHAIN(aOptions, sNetworkInterfaceStatsChain, networkInterfaceStatsFail);
   return  true;
--- a/dom/system/gonk/NetworkUtils.h
+++ b/dom/system/gonk/NetworkUtils.h
@@ -263,16 +263,18 @@ private:
    */
   bool setDNS(NetworkParams& aOptions);
   bool setDefaultRouteAndDNS(NetworkParams& aOptions);
   bool addHostRoute(NetworkParams& aOptions);
   bool removeDefaultRoute(NetworkParams& aOptions);
   bool removeHostRoute(NetworkParams& aOptions);
   bool removeHostRoutes(NetworkParams& aOptions);
   bool removeNetworkRoute(NetworkParams& aOptions);
+  bool addSecondaryRoute(NetworkParams& aOptions);
+  bool removeSecondaryRoute(NetworkParams& aOptions);
   bool getNetworkInterfaceStats(NetworkParams& aOptions);
   bool setNetworkInterfaceAlarm(NetworkParams& aOptions);
   bool enableNetworkInterfaceAlarm(NetworkParams& aOptions);
   bool disableNetworkInterfaceAlarm(NetworkParams& aOptions);
   bool setWifiOperationMode(NetworkParams& aOptions);
   bool setDhcpServer(NetworkParams& aOptions);
   bool setWifiTethering(NetworkParams& aOptions);
   bool setUSBTethering(NetworkParams& aOptions);
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -681,17 +681,17 @@ XPCOMUtils.defineLazyGetter(this, "gRadi
         let deferred = _deactivatingDeferred[clientId] = Promise.defer();
         let dataConnectionHandler = gDataConnectionManager.getConnectionHandler(clientId);
         dataConnectionHandler.deactivateDataCalls();
         return deferred.promise;
       };
     },
 
     _createTimer: function() {
-      if (_timer) {
+      if (!_timer) {
         _timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
       }
       _timer.initWithCallback(this._executeRequest.bind(this),
                               RADIO_POWER_OFF_TIMEOUT,
                               Ci.nsITimer.TYPE_ONE_SHOT);
     },
 
     _cancelTimer: function() {
--- a/dom/system/gonk/nsINetworkService.idl
+++ b/dom/system/gonk/nsINetworkService.idl
@@ -99,17 +99,17 @@ interface nsIUpdateUpStreamCallback : ns
    *        The external interface name.
    */
   void updateUpStreamResult(in boolean success, in DOMString externalIfname);
 };
 
 /**
  * Provide network services.
  */
-[scriptable, uuid(c14cabaf-bb8e-470d-a2f1-2cb6de6c5e5c)]
+[scriptable, uuid(baec696c-c78d-42db-8b44-603f8fbfafb4)]
 interface nsINetworkService : nsISupports
 {
   /**
    * Enable or disable Wifi Tethering
    *
    * @param enabled
    *        Boolean that indicates whether tethering should be enabled (true) or disabled (false).
    * @param config
@@ -291,16 +291,42 @@ interface nsINetworkService : nsISupport
    * @param network
    *        The network interface we want to remove from the host route.
    * @param hosts
    *        The array of host names we want to remove.
    */
   void removeHostRouteWithResolve(in nsINetworkInterface network, in jsval hosts);
 
   /**
+   * Add route to secondary routing table.
+   *
+   * @param interfaceName
+   *        The network interface for this route.
+   * @param route
+   *        The route info should have the following fields:
+   *        .ip: destination ip address
+   *        .prefix: destination prefix
+   *        .gateway: gateway ip address
+   */
+  void addSecondaryRoute(in DOMString interfaceName, in jsval route);
+
+  /**
+   * Remove route from secondary routing table.
+   *
+   * @param interfaceName
+   *        The network interface for the route we want to remove.
+   * @param route
+   *        The route info should have the following fields:
+   *        .ip: destination ip address
+   *        .prefix: destination prefix
+   *        .gateway: gateway ip address
+   */
+  void removeSecondaryRoute(in DOMString interfaceName, in jsval route);
+
+  /**
    * Enable or disable usb rndis.
    *
    * @param enable
    *        Boolean to indicate we want enable or disable usb rndis.
    * @param callback
    *        Callback function to report the result.
    */
   void enableUsbRndis(in boolean enable,
--- a/media/mtransport/gonk_addrs.cpp
+++ b/media/mtransport/gonk_addrs.cpp
@@ -13,16 +13,17 @@ extern "C" {
 #include <vector>
 #include <string>
 #include "nsINetworkManager.h"
 #include "nsINetworkInterfaceListService.h"
 #include "runnable_utils.h"
 #include "nsCOMPtr.h"
 #include "nsThreadUtils.h"
 #include "nsServiceManagerUtils.h"
+#include "mozilla/SyncRunnable.h"
 
 namespace {
 struct NetworkInterface {
   struct sockaddr_in addr;
   std::string name;
   // See NR_INTERFACE_TYPE_* in nICEr/src/net/local_addrs.h
   int type;
 };
@@ -99,22 +100,21 @@ int
 nr_stun_get_addrs(nr_local_addr aAddrs[], int aMaxAddrs,
                   int aDropLoopback, int* aCount)
 {
   nsresult rv;
   int r;
 
   // Get network interface list.
   std::vector<NetworkInterface> interfaces;
-  if (NS_FAILED(NS_DispatchToMainThread(
-                    mozilla::WrapRunnableNMRet(&GetInterfaces, &interfaces, &rv),
-                    NS_DISPATCH_SYNC))) {
-    return R_FAILED;
-  }
-
+  nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+  mozilla::SyncRunnable::DispatchToThread(
+    mainThread.get(),
+    mozilla::WrapRunnableNMRet(&GetInterfaces, &interfaces, &rv),
+    false);
   if (NS_FAILED(rv)) {
     return R_FAILED;
   }
 
   // Translate to nr_transport_addr.
   int32_t n = 0;
   size_t num_interface = std::min(interfaces.size(), (size_t)aMaxAddrs);
   for (size_t i = 0; i < num_interface; ++i) {
--- a/toolkit/devtools/server/actors/webapps.js
+++ b/toolkit/devtools/server/actors/webapps.js
@@ -174,17 +174,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,