merge b2g-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 17 Dec 2014 12:57:09 +0100
changeset 220108 cb8ad2251c0905cc4a968bbada220ca16e1f3f85
parent 220095 8549d93bb2486e3484d74d90ea66ef5763b14f9a (current diff)
parent 220107 80a4dc7438a54e0440037f80726c28fc2e90792a (diff)
child 220127 07e69caa9484b168661ede38615310033a8f5b53
child 220158 fbe5dc1dfcfcf4654340e15ae3b5e86aae02f47f
child 220189 1a16300913e735dc5a2e39b113aca3ba81253521
push id27977
push usercbook@mozilla.com
push dateWed, 17 Dec 2014 11:57:25 +0000
treeherdermozilla-central@cb8ad2251c09 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone37.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 b2g-inbound to mozilla-central a=merge
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -1032,16 +1032,19 @@ pref("gfx.canvas.willReadFrequently.enab
 
 // Disable autofocus until we can have it not bring up the keyboard.
 // https://bugzilla.mozilla.org/show_bug.cgi?id=965763
 pref("browser.autofocus", false);
 
 // Enable wakelock
 pref("dom.wakelock.enabled", true);
 
+// Enable webapps add-ons
+pref("dom.apps.customization.enabled", true);
+
 // Enable touch caret by default
 pref("touchcaret.enabled", true);
 
 // Enable selection caret by default
 pref("selectioncaret.enabled", true);
 
 // Enable sync and mozId with Firefox Accounts.
 pref("services.sync.fxaccounts.enabled", true);
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8ffb48c321bb9595ad437086c5eb450bc90aa714"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="d22dfece04fc00457e8369c660c11f945b088d2f"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="631dbd1b133f09d83f526292ab8996e46b1e7654"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="2b761dbeebac9126947315659419a0fb1f80b230"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="a1e239a0bb5cd1d69680bf1075883aa9a7bf2429"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="c7931763d41be602407ed9d71e2c0292c6597e00"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="83760d213fb3bec7b4117d266fcfbf6fe2ba14ab"/>
   <project name="device/common" path="device/common" revision="6a2995683de147791e516aae2ccb31fdfbe2ad30"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,23 +14,23 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <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="8ffb48c321bb9595ad437086c5eb450bc90aa714"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="d22dfece04fc00457e8369c660c11f945b088d2f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="6fa7a4936414ceb4055fd27f7a30e76790f834fb"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="631dbd1b133f09d83f526292ab8996e46b1e7654"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="2b761dbeebac9126947315659419a0fb1f80b230"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="425f8b5fadf5889834c5acd27d23c9e0b2129c28"/>
   <project name="device/common" path="device/common" revision="42b808b7e93d0619286ae8e59110b176b7732389"/>
   <project name="device/sample" path="device/sample" revision="237bd668d0f114d801a8d6455ef5e02cc3577587"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
   <project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="52a1a862a8bac319652b8f82d9541ba40bfa45ce"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="0e94c080bee081a50aa2097527b0b40852f9143f">
     <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="8ffb48c321bb9595ad437086c5eb450bc90aa714"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="d22dfece04fc00457e8369c660c11f945b088d2f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="631dbd1b133f09d83f526292ab8996e46b1e7654"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="2b761dbeebac9126947315659419a0fb1f80b230"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8ffb48c321bb9595ad437086c5eb450bc90aa714"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="d22dfece04fc00457e8369c660c11f945b088d2f"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="631dbd1b133f09d83f526292ab8996e46b1e7654"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="2b761dbeebac9126947315659419a0fb1f80b230"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="1950e4760fa14688b83cdbb5acaa1af9f82ef434"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="ac6eb97a37035c09fb5ede0852f0881e9aadf9ad"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="737f591c5f95477148d26602c7be56cbea0cdeb9"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="51da9b1981be481b92a59a826d4d78dc73d0989a"/>
   <project name="device/common" path="device/common" revision="798a3664597e6041985feab9aef42e98d458bc3d"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,23 +14,23 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <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="8ffb48c321bb9595ad437086c5eb450bc90aa714"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="d22dfece04fc00457e8369c660c11f945b088d2f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="6fa7a4936414ceb4055fd27f7a30e76790f834fb"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="631dbd1b133f09d83f526292ab8996e46b1e7654"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="2b761dbeebac9126947315659419a0fb1f80b230"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="425f8b5fadf5889834c5acd27d23c9e0b2129c28"/>
   <project name="device/common" path="device/common" revision="42b808b7e93d0619286ae8e59110b176b7732389"/>
   <project name="device/sample" path="device/sample" revision="237bd668d0f114d801a8d6455ef5e02cc3577587"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
   <project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="52a1a862a8bac319652b8f82d9541ba40bfa45ce"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8ffb48c321bb9595ad437086c5eb450bc90aa714"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="d22dfece04fc00457e8369c660c11f945b088d2f"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="631dbd1b133f09d83f526292ab8996e46b1e7654"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="2b761dbeebac9126947315659419a0fb1f80b230"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="a1e239a0bb5cd1d69680bf1075883aa9a7bf2429"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="c7931763d41be602407ed9d71e2c0292c6597e00"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="a32003194f707f66a2d8cdb913ed1869f1926c5d"/>
   <project name="device/common" path="device/common" revision="96d4d2006c4fcb2f19a3fa47ab10cb409faa017b"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="0e94c080bee081a50aa2097527b0b40852f9143f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8ffb48c321bb9595ad437086c5eb450bc90aa714"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="d22dfece04fc00457e8369c660c11f945b088d2f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="631dbd1b133f09d83f526292ab8996e46b1e7654"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="2b761dbeebac9126947315659419a0fb1f80b230"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="e95b4ce22c825da44d14299e1190ea39a5260bde"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "git_revision": "", 
         "remote": "", 
         "branch": ""
     }, 
-    "revision": "99bea97a4bd9d6af1d7462e1abe15c8aaf4d7981", 
+    "revision": "8175c3383310bf79cbfd01d36273620dede2a111", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,21 +12,21 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <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="8ffb48c321bb9595ad437086c5eb450bc90aa714"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="d22dfece04fc00457e8369c660c11f945b088d2f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="631dbd1b133f09d83f526292ab8996e46b1e7654"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="2b761dbeebac9126947315659419a0fb1f80b230"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="746bc48f34f5060f90801925dcdd964030c1ab6d"/>
   <project name="platform/development" path="development" revision="2460485184bc8535440bb63876d4e63ec1b4770c"/>
   <project name="device/common" path="device/common" revision="0dcc1e03659db33b77392529466f9eb685cdd3c7"/>
   <project name="device/sample" path="device/sample" revision="68b1cb978a20806176123b959cb05d4fa8adaea4"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <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="8ffb48c321bb9595ad437086c5eb450bc90aa714"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="d22dfece04fc00457e8369c660c11f945b088d2f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="0e94c080bee081a50aa2097527b0b40852f9143f">
     <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="8ffb48c321bb9595ad437086c5eb450bc90aa714"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="d22dfece04fc00457e8369c660c11f945b088d2f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="631dbd1b133f09d83f526292ab8996e46b1e7654"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="2b761dbeebac9126947315659419a0fb1f80b230"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,22 +12,22 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <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="8ffb48c321bb9595ad437086c5eb450bc90aa714"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="d22dfece04fc00457e8369c660c11f945b088d2f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="631dbd1b133f09d83f526292ab8996e46b1e7654"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="2b761dbeebac9126947315659419a0fb1f80b230"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="cd5dfce80bc3f0139a56b58aca633202ccaee7f8"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="e0a9ac010df3afaa47ba107192c05ac8b5516435"/>
   <project name="platform/development" path="development" revision="a384622f5fcb1d2bebb9102591ff7ae91fe8ed2d"/>
   <project name="device/common" path="device/common" revision="7c65ea240157763b8ded6154a17d3c033167afb7"/>
   <project name="device/sample" path="device/sample" revision="c328f3d4409db801628861baa8d279fb8855892f"/>
--- a/dom/apps/AppsService.js
+++ b/dom/apps/AppsService.js
@@ -10,16 +10,21 @@ function debug(s) {
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
+try {
+  if (Services.prefs.getBoolPref("dom.apps.customization.enabled")) {
+    Cu.import("resource://gre/modules/UserCustomizations.jsm");
+  }
+} catch(e) {}
 
 const APPS_SERVICE_CID = Components.ID("{05072afa-92fe-45bf-ae22-39b69c117058}");
 
 function AppsService()
 {
   debug("AppsService Constructor");
   this.inParent = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime)
                     .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
--- a/dom/apps/AppsUtils.jsm
+++ b/dom/apps/AppsUtils.jsm
@@ -474,26 +474,35 @@ this.AppsUtils = {
     if (aInstallOrigin != aWebappOrigin &&
         !(contentType == "application/x-web-app-manifest+json" ||
           contentType == "application/manifest+json")) {
       return false;
     }
     return true;
   },
 
+  allowUnsignedAddons: false, // for testing purposes.
+
   /**
-   * Checks if the app role is allowed.
+   * Checks if the app role is allowed:
    * Only certified apps can be themes.
+   * Only privileged or certified apps can be addons.
    * @param aRole   : the role assigned to this app.
    * @param aStatus : the APP_STATUS_* for this app.
    */
   checkAppRole: function(aRole, aStatus) {
     if (aRole == "theme" && aStatus !== Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
       return false;
     }
+    if (!this.allowUnsignedAddons &&
+        (aRole == "addon" &&
+         aStatus !== Ci.nsIPrincipal.APP_STATUS_CERTIFIED &&
+         aStatus !== Ci.nsIPrincipal.APP_STATUS_PRIVILEGED)) {
+      return false;
+    }
     return true;
   },
 
   /**
    * Method to apply modifications to webapp manifests file saved internally.
    * For now, only ensure app can't rename itself.
    */
   ensureSameAppName: function ensureSameAppName(aOldManifest, aNewManifest, aApp) {
@@ -713,16 +722,21 @@ this.AppsUtils = {
     let hash = hasher.finish(false);
 
     function toHexString(charCode) {
       return ("0" + charCode.toString(16)).slice(-2);
     }
 
     // Convert the binary hash data to a hex string.
     return [toHexString(hash.charCodeAt(i)) for (i in hash)].join("");
+  },
+
+  // Returns the hash for a JS object.
+  computeObjectHash: function(aObject) {
+    return this.computeHash(JSON.stringify(aObject));
   }
 }
 
 /**
  * Helper object to access manifest information with locale support
  */
 this.ManifestHelper = function(aManifest, aOrigin, aManifestURL) {
   // If the app is packaged, we resolve uris against the origin.
new file mode 100644
--- /dev/null
+++ b/dom/apps/UserCustomizations.jsm
@@ -0,0 +1,368 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const Cu = Components.utils;
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+this.EXPORTED_SYMBOLS = ["UserCustomizations"];
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/AppsUtils.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
+                                   "@mozilla.org/parentprocessmessagemanager;1",
+                                   "nsIMessageBroadcaster");
+
+XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
+                                   "@mozilla.org/childprocessmessagemanager;1",
+                                   "nsIMessageSender");
+
+XPCOMUtils.defineLazyServiceGetter(this, "console",
+                                   "@mozilla.org/consoleservice;1",
+                                   "nsIConsoleService");
+/**
+  * Customization scripts and CSS stylesheets can be specified in an
+  * application manifest with the following syntax:
+  * "customizations": [
+  *  {
+  *    "filter": "http://youtube.com",
+  *    "css": ["file1.css", "file2.css"],
+  *    "scripts": ["script1.js", "script2.js"]
+  *  }
+  * ]
+  */
+
+let debug = Services.prefs.getBoolPref("dom.mozApps.debug")
+  ? (aMsg) => {
+      dump("-*-*- UserCustomizations (" +
+           (UserCustomizations._inParent ? "parent" : "child") +
+           "): " + aMsg + "\n");
+    }
+  : (aMsg) => {};
+
+function log(aStr) {
+  console.logStringMessage(aStr);
+}
+
+this.UserCustomizations = {
+  _items: [],
+  _loaded : {},   // Keep track per manifestURL of css and scripts loaded.
+  _windows: null, // Set of currently opened windows.
+  _enabled: false,
+
+  _addItem: function(aItem) {
+    debug("_addItem: " + uneval(aItem));
+    this._items.push(aItem);
+    if (this._inParent) {
+      ppmm.broadcastAsyncMessage("UserCustomizations:Add", [aItem]);
+    }
+  },
+
+  _removeItem: function(aHash) {
+    debug("_removeItem: " + aHash);
+    let index = -1;
+    this._items.forEach((script, pos) => {
+      if (script.hash == aHash ) {
+        index = pos;
+      }
+    });
+
+    if (index != -1) {
+      this._items.splice(index, 1);
+    }
+
+    if (this._inParent) {
+      ppmm.broadcastAsyncMessage("UserCustomizations:Remove", aHash);
+    }
+  },
+
+  register: function(aManifest, aApp) {
+    debug("Starting customization registration for " + aApp.manifestURL);
+
+    if (!this._enabled || !aApp.enabled || aApp.role != "addon") {
+      debug("Rejecting registration (global enabled=" + this._enabled +
+            ") (app role=" + aApp.role +
+            ", enabled=" + aApp.enabled + ")");
+      debug(uneval(aApp));
+      return;
+    }
+
+    let customizations = aManifest.customizations;
+    if (customizations === undefined || !Array.isArray(customizations)) {
+      return;
+    }
+
+    let base = Services.io.newURI(aApp.origin, null, null);
+
+    customizations.forEach(item => {
+      // The filter property is mandatory.
+      if (!item.filter || (typeof item.filter !== "string")) {
+        log("Mandatory filter property not found in this customization item: " +
+            uneval(item) + " in " + aApp.manifestURL);
+        return;
+      }
+
+      // Create a new object with resolved urls and a hash that we reuse to
+      // remove items.
+      let custom = {
+        filter: item.filter,
+        status: aApp.appStatus,
+        manifestURL: aApp.manifestURL,
+        css: [],
+        scripts: []
+      };
+      custom.hash = AppsUtils.computeObjectHash(item);
+
+      if (item.css && Array.isArray(item.css)) {
+        item.css.forEach((css) => {
+          custom.css.push(base.resolve(css));
+        });
+      }
+
+      if (item.scripts && Array.isArray(item.scripts)) {
+        item.scripts.forEach((script) => {
+          custom.scripts.push(base.resolve(script));
+        });
+      }
+
+      this._addItem(custom);
+    });
+    this._updateAllWindows();
+  },
+
+  _updateAllWindows: function() {
+    debug("UpdateWindows");
+    if (this._inParent) {
+      ppmm.broadcastAsyncMessage("UserCustomizations:UpdateWindows", {});
+    }
+    // Inject in all currently opened windows.
+    this._windows.forEach(this._injectInWindow.bind(this));
+  },
+
+  unregister: function(aManifest, aApp) {
+    if (!this._enabled) {
+      return;
+    }
+
+    debug("Starting customization unregistration for " + aApp.manifestURL);
+    let customizations = aManifest.customizations;
+    if (customizations === undefined || !Array.isArray(customizations)) {
+      return;
+    }
+
+    customizations.forEach(item => {
+      this._removeItem(AppsUtils.computeObjectHash(item));
+    });
+    this._unloadForManifestURL(aApp.manifestURL);
+  },
+
+  _unloadForManifestURL: function(aManifestURL) {
+    debug("_unloadForManifestURL " + aManifestURL);
+
+    if (this._inParent) {
+      ppmm.broadcastAsyncMessage("UserCustomizations:Unload", aManifestURL);
+    }
+
+    if (!this._loaded[aManifestURL]) {
+      return;
+    }
+
+    if (this._loaded[aManifestURL].scripts &&
+        this._loaded[aManifestURL].scripts.length > 0) {
+      // We can't rollback script changes, so don't even try to unload in this
+      // situation.
+      return;
+    }
+
+    this._loaded[aManifestURL].css.forEach(aItem => {
+      try {
+        debug("unloading " + aItem.uri.spec);
+        let utils = aItem.window.QueryInterface(Ci.nsIInterfaceRequestor)
+                                .getInterface(Ci.nsIDOMWindowUtils);
+        utils.removeSheet(aItem.uri, Ci.nsIDOMWindowUtils.AUTHOR_SHEET);
+      } catch(e) {
+        log("Error unloading stylesheet " + aItem.uri.spec + " : " + e);
+      }
+    });
+
+    this._loaded[aManifestURL] = null;
+  },
+
+  _injectItem: function(aWindow, aItem, aInjected) {
+    debug("Injecting item " + uneval(aItem) + " in " + aWindow.location.href);
+    let utils = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                       .getInterface(Ci.nsIDOMWindowUtils);
+
+    let manifestURL = aItem.manifestURL;
+
+    // Load the stylesheets only in this window.
+    aItem.css.forEach(aCss => {
+      if (aInjected.indexOf(aCss) !== -1) {
+        debug("Skipping duplicated css: " + aCss);
+        return;
+      }
+
+      let uri = Services.io.newURI(aCss, null, null);
+      try {
+        utils.loadSheet(uri, Ci.nsIDOMWindowUtils.AUTHOR_SHEET);
+        if (!this._loaded[manifestURL]) {
+          this._loaded[manifestURL] = { css: [], scripts: [] };
+        }
+        this._loaded[manifestURL].css.push({ window: aWindow, uri: uri });
+        aInjected.push(aCss);
+      } catch(e) {
+        log("Error loading stylesheet " + aCss + " : " + e);
+      }
+    });
+
+    let sandbox;
+    if (aItem.scripts.length > 0) {
+      sandbox = Cu.Sandbox([aWindow],
+                           { wantComponents: false,
+                             sandboxPrototype: aWindow });
+    }
+
+    // Load the scripts using a sandbox.
+    aItem.scripts.forEach(aScript => {
+      debug("Sandboxing " + aScript);
+      if (aInjected.indexOf(aScript) !== -1) {
+        debug("Skipping duplicated script: " + aScript);
+        return;
+      }
+
+      try {
+        Services.scriptloader.loadSubScript(aScript, sandbox, "UTF-8");
+        if (!this._loaded[manifestURL]) {
+          this._loaded[manifestURL] = { css: [], scripts: [] };
+        }
+        this._loaded[manifestURL].scripts.push({ sandbox: sandbox, uri: aScript });
+        aInjected.push(aScript);
+      } catch(e) {
+        log("Error sandboxing " + aScript + " : " + e);
+      }
+    });
+
+    // Makes sure we get rid of the sandbox.
+    if (sandbox) {
+      aWindow.addEventListener("unload", () => {
+        Cu.nukeSandbox(sandbox);
+        sandbox = null;
+      });
+    }
+  },
+
+  _injectInWindow: function(aWindow) {
+    debug("_injectInWindow");
+
+    if (!aWindow || !aWindow.document) {
+      return;
+    }
+
+    let principal = aWindow.document.nodePrincipal;
+    debug("principal status: " + principal.appStatus);
+
+    let href = aWindow.location.href;
+
+    // The list of resources loaded in this window, used to filter out
+    // duplicates.
+    let injected = [];
+
+    this._items.forEach((aItem) => {
+      // We only allow customizations to apply to apps with an equal or lower
+      // privilege level.
+      if (principal.appStatus > aItem.status) {
+        return;
+      }
+
+      let regexp = new RegExp(aItem.filter, "g");
+      if (regexp.test(href)) {
+        this._injectItem(aWindow, aItem, injected);
+        debug("Currently injected: " + injected.toString());
+      }
+    });
+  },
+
+  observe: function(aSubject, aTopic, aData) {
+    if (aTopic === "content-document-global-created") {
+      let window = aSubject.QueryInterface(Ci.nsIDOMWindow);
+      let href = window.location.href;
+      if (!href || href == "about:blank") {
+        return;
+      }
+
+      let id = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                     .getInterface(Ci.nsIDOMWindowUtils)
+                     .currentInnerWindowID;
+      this._windows.set(id, window);
+
+      debug("document created: " + href);
+      this._injectInWindow(window);
+    } else if (aTopic === "inner-window-destroyed") {
+      let winId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
+      this._windows.delete(winId);
+    }
+  },
+
+  init: function() {
+    this._enabled = false;
+    try {
+      this._enabled = Services.prefs.getBoolPref("dom.apps.customization.enabled");
+    } catch(e) {}
+
+    if (!this._enabled) {
+      return;
+    }
+
+    this._windows = new Map(); // Can't be a WeakMap because we need to enumerate.
+    this._inParent = Cc["@mozilla.org/xre/runtime;1"]
+                       .getService(Ci.nsIXULRuntime)
+                       .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
+
+    debug("init");
+
+    Services.obs.addObserver(this, "content-document-global-created",
+                             /* ownsWeak */ false);
+    Services.obs.addObserver(this, "inner-window-destroyed",
+                             /* ownsWeak */ false);
+
+    if (this._inParent) {
+      ppmm.addMessageListener("UserCustomizations:List", this);
+    } else {
+      cpmm.addMessageListener("UserCustomizations:Add", this);
+      cpmm.addMessageListener("UserCustomizations:Remove", this);
+      cpmm.addMessageListener("UserCustomizations:Unload", this);
+      cpmm.addMessageListener("UserCustomizations:UpdateWindows", this);
+      cpmm.sendAsyncMessage("UserCustomizations:List", {});
+    }
+  },
+
+  receiveMessage: function(aMessage) {
+    let name = aMessage.name;
+    let data = aMessage.data;
+
+    switch(name) {
+      case "UserCustomizations:List":
+        aMessage.target.sendAsyncMessage("UserCustomizations:Add", this._items);
+        break;
+      case "UserCustomizations:Add":
+        data.forEach(this._addItem, this);
+        break;
+      case "UserCustomizations:Remove":
+        this._removeItem(data);
+        break;
+      case "UserCustomizations:Unload":
+        this._unloadForManifestURL(data);
+        break;
+      case "UserCustomizations:UpdateWindows":
+        this._updateAllWindows();
+        break;
+    }
+  }
+}
+
+UserCustomizations.init();
--- a/dom/apps/Webapps.jsm
+++ b/dom/apps/Webapps.jsm
@@ -38,16 +38,33 @@ Cu.import("resource://gre/modules/Servic
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import('resource://gre/modules/ActivitiesService.jsm');
 Cu.import("resource://gre/modules/AppsUtils.jsm");
 Cu.import("resource://gre/modules/AppDownloadManager.jsm");
 Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
 
+XPCOMUtils.defineLazyGetter(this, "UserCustomizations", function() {
+  let enabled = false;
+  try {
+    enabled = Services.prefs.getBoolPref("dom.apps.customization.enabled");
+  } catch(e) {}
+
+  if (enabled) {
+    return Cu.import("resource://gre/modules/UserCustomizations.jsm", {})
+             .UserCustomizations;
+  } else {
+    return {
+      register: function() {},
+      unregister: function() {}
+    };
+  }
+});
+
 XPCOMUtils.defineLazyModuleGetter(this, "TrustedRootCertificate",
   "resource://gre/modules/StoreTrustAnchor.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PermissionsInstaller",
   "resource://gre/modules/PermissionsInstaller.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "OfflineCacheInstaller",
   "resource://gre/modules/OfflineCacheInstaller.jsm");
@@ -401,16 +418,17 @@ this.DOMApplicationRegistry = {
 
         let localeManifest = new ManifestHelper(aResult.manifest, app.origin, app.manifestURL);
         this._saveWidgetsFullPath(localeManifest, app);
 
         if (app.appStatus >= Ci.nsIPrincipal.APP_STATUS_PRIVILEGED) {
           app.redirects = this.sanitizeRedirects(aResult.redirects);
         }
         app.kind = this.appKind(app, aResult.manifest);
+        UserCustomizations.register(aResult.manifest, app);
       });
 
       // Nothing else to do but notifying we're ready.
       this.notifyAppsRegistryReady();
     }
   }),
 
   updateDataStoreForApp: Task.async(function*(aId) {
@@ -1092,16 +1110,17 @@ this.DOMApplicationRegistry = {
 
         if (app.appStatus >= Ci.nsIPrincipal.APP_STATUS_PRIVILEGED) {
           app.redirects = this.sanitizeRedirects(manifest.redirects);
         }
         app.kind = this.appKind(app, aResult.manifest);
         this._registerSystemMessages(manifest, app);
         this._registerInterAppConnections(manifest, app);
         appsToRegister.push({ manifest: manifest, app: app });
+        UserCustomizations.register(manifest, app);
       });
       this._safeToClone.resolve();
       this._registerActivitiesForApps(appsToRegister, aRunUpdate);
     });
   },
 
   observe: function(aSubject, aTopic, aData) {
     if (aTopic == "xpcom-shutdown") {
@@ -1986,37 +2005,46 @@ this.DOMApplicationRegistry = {
   // Returns the MD5 hash of the manifest.
   computeManifestHash: function(aManifest) {
     return AppsUtils.computeHash(JSON.stringify(aManifest));
   },
 
   // Updates the redirect mapping, activities and system message handlers.
   // aOldManifest can be null if we don't have any handler to unregister.
   updateAppHandlers: function(aOldManifest, aNewManifest, aApp) {
-    debug("updateAppHandlers: old=" + aOldManifest + " new=" + aNewManifest);
+    debug("updateAppHandlers: old=" + uneval(aOldManifest) +
+          " new=" + uneval(aNewManifest));
     this.notifyAppsRegistryStart();
     if (aApp.appStatus >= Ci.nsIPrincipal.APP_STATUS_PRIVILEGED) {
       aApp.redirects = this.sanitizeRedirects(aNewManifest.redirects);
     }
 
     let manifest =
       new ManifestHelper(aNewManifest, aApp.origin, aApp.manifestURL);
     this._saveWidgetsFullPath(manifest, aApp);
 
+    aApp.role = manifest.role ? manifest.role : "";
+
     if (supportSystemMessages()) {
       if (aOldManifest) {
         this._unregisterActivities(aOldManifest, aApp);
       }
       this._registerSystemMessages(aNewManifest, aApp);
       this._registerActivities(aNewManifest, aApp, true);
       this._registerInterAppConnections(aNewManifest, aApp);
     } else {
       // Nothing else to do but notifying we're ready.
       this.notifyAppsRegistryReady();
     }
+
+    // Update user customizations.
+    if (aOldManifest) {
+      UserCustomizations.unregister(aOldManifest, aApp);
+    }
+    UserCustomizations.register(aNewManifest, aApp);
   },
 
   checkForUpdate: function(aData, aMm) {
     debug("checkForUpdate for " + aData.manifestURL);
 
     function sendError(aError) {
       debug("checkForUpdate error " + aError);
       aData.error = aError;
@@ -4014,16 +4042,17 @@ this.DOMApplicationRegistry = {
     this._clearPrivateData(aApp.localId, false);
 
     // Then notify observers.
     Services.obs.notifyObservers(null, "webapps-uninstall", JSON.stringify(aApp));
 
     if (supportSystemMessages()) {
       this._unregisterActivities(aApp.manifest, aApp);
     }
+    UserCustomizations.unregister(aApp.manifest, aApp);
 
     let dir = this._getAppDir(id);
     try {
       dir.remove(true);
     } catch (e) {}
 
     delete this.webapps[id];
 
@@ -4383,16 +4412,22 @@ this.DOMApplicationRegistry = {
     app.enabled = aData.enabled;
     this._saveApps().then(() => {
       DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", {
         app: app,
         id: app.id
       });
       this.broadcastMessage("Webapps:SetEnabled:Return", app);
     });
+
+    // Update customization.
+    this.getManifestFor(app.manifestURL).then((aManifest) => {
+      app.enabled ? UserCustomizations.register(aManifest, app)
+                  : UserCustomizations.unregister(aManifest, app);
+    });
   },
 
   getManifestFor: function(aManifestURL) {
     let id = this._appIdForManifestURL(aManifestURL);
     let app = this.webapps[id];
     if (!id || (app.installState == "pending" && !app.retryingDownload)) {
       return Promise.resolve(null);
     }
--- a/dom/apps/moz.build
+++ b/dom/apps/moz.build
@@ -33,16 +33,17 @@ EXTRA_JS_MODULES += [
     'AppDownloadManager.jsm',
     'AppsServiceChild.jsm',
     'FreeSpaceWatcher.jsm',
     'InterAppCommService.jsm',
     'OfflineCacheInstaller.jsm',
     'PermissionsInstaller.jsm',
     'PermissionsTable.jsm',
     'StoreTrustAnchor.jsm',
+    'UserCustomizations.jsm',
 ]
 
 EXTRA_PP_JS_MODULES += [
     'AppsUtils.jsm',
     'ImportExport.jsm',
     'OperatorApps.jsm',
     'ScriptPreloader.jsm',
     'TrustedHostedAppsUtils.jsm',
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..543587c8869ba38506c5889fa66d28755389274b
GIT binary patch
literal 1725
zc$^FHW@Zs#U|`^25Gk18%CYF^3_V5$1}P>61}+8}hRnQ_)C#?flH8oo5Kac>_SF+Y
zK)AGmn}Lz#1v3K!n3x)L(2LnnVDESBW45{cWiR9I>Se4|=nz}ebiBm)E!(D<s<R`)
zU+%Z|j1sY_^i-St{hm!+Q0yH$UGx0cqFdHo{<_3V+TrS=2@4-w(p<jr-j=L}xHp1)
zUfDO4N>r};Eo^zqvCdT>C~nfqt``403pcJR|C;jL_pyki#Dx>tz1MilQ{+99@+xK?
z5`XOa;`rw?58I<B6=vM|(zX6xYipZHWW<LW<(CGxm6dnhvz~H3?~3}u3vtn}A~S15
zZza!jjT1|~)N<p&=fY2qPghM=7q6G~%Bnf;*YwRRStI{g^~`idm6Vq&{^rL?b{-cA
zDfH92^8CYU-IIbRGK7jIx^~~xyivy2Rjtncl{xZ_!`J5bGwke}PsJ}VS{?86nB9Hu
zuP5QzL3?=gFJCd}+3z6u|A2V_Jj64rC%8WS_95;NkQWUMb$(!o=O*T5rll5_=#{4?
zB^DHbgZ<k62_bj(V+4EXgoAv|3OsG^?Oc!F^mvr{aog{E2i`a|xb`$`ygTXak9W<B
zP9IzN^8Wlf-8G^O9H*i$HtvuRW?oS#R;nl6T~%njQ({x~R+f8B+>5WW9Ay&a$v6`H
z@&f;o7k8td%86WXoPUZz`*_L2cWRrwm+`t3t1s=ZD&UP=@@`Jw8A&hUy*}9w^g{1(
zSF!|OI?(ch^-}gG-!M~=i*l!0m(QB>(>U_ZzA2xY<{qguo_1R2R;=ujc>Qka;%y?5
zn$s*ks(;$F=+-L!0F=bHFGBNs1CTcZh&h2lQJh?qSx};vRg5p~nfC7GI%FVl{JY)J
z%3!8T;v9Ne3tPp+4_bcl<2_>Sak;7EeV);dzd!0HoqOG_x_#b?+gv?~Cw1oV%+tT@
zsT%HUYxq#^-Rd)zD<rt*vn71yocD&4&*A@E_UC#<M;})!G+%w*a&2nOQ!|gq_v<f5
zMBOOv;WGD~SoUI8{>~4DiLdJrUJzQ|=PE0{N}(0Vo6i8rKM*e%!Ti9I)fXZM#29|?
z1^J=%yX{ef)htW6w;$}?ny~O_uQ<odK<2kBleR2)bk|p){C)d(JFdLgh@$MyUu!!q
zE<bZ9@Zn3NQpcjRO{!{l4(5tZuN7GO@Ge7+*`a3LgN!f!7eBvqKIZ91%L#{W72jEG
zmKxyudg-0{KyReoSgYB7bpMP4e-koh`77SE+-MdL@(33L2!P_EEMu;cB#<o$i;I%V
zoK(H!V!UyooRONCl3Jut&847_oS&0lWTjA)nqtjWi!fUi7)UQ|^zx;FY-v~^fy_37
z8?FirAS|gJYPb=y=}9@IsbJ$7ndF#pm0}WLi-BB*EsY=+a+$^o@e7(&$mU_@QDpOq
zfD}fKWrgHeJceSXc4R|qfzC!s`-I(ynevd0b6{pbNr$YEbcoeFTnPZ#JWdt{<m7<Q
hdAQ;n*|=1gaYk_SRIxdal?|ka4G13tH6CFB@c`i<Q~&?~
new file mode 100644
--- /dev/null
+++ b/dom/apps/tests/addons/index.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>This page will be modified by the add-on</title>
+    <script>
+function sendAlertsForNode(node) {
+  alert(node.textContent);
+  var color = window.getComputedStyle(node).getPropertyValue("color");
+  alert(color);
+}
+
+function run() {
+  sendAlertsForNode(document.getElementById("header"));
+  sendAlertsForNode(document.getElementById("header2"));
+}
+    </script>
+  </head>
+  <body onload="run()">
+    <h1 id="header">Lorem ipsum</h1>
+    <h2 id="header2">Uncustomized content</h2>
+  </body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/apps/tests/addons/manifest.webapp
@@ -0,0 +1,12 @@
+{
+  "name": "Addon app",
+  "description": "Let me inject script and css!",
+  "customizations" : [
+    {
+      "filter": "http://mochi.test:8888/tests/dom/apps/tests/addons",
+      "css": ["style.css", "style2.css", "invalid.css", "style.css"],
+      "scripts": ["script.js", "script2.js", "invalid.js", "script.js"]
+    }
+  ],
+  "role": "addon"
+}
new file mode 100644
--- /dev/null
+++ b/dom/apps/tests/addons/script.js
@@ -0,0 +1,4 @@
+document.addEventListener("DOMContentLoaded", function() {
+  var head = document.getElementById("header");
+  head.innerHTML = "Hello World!";
+}, false);
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/apps/tests/addons/style.css
@@ -0,0 +1,3 @@
+#header {
+  color: red;
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/apps/tests/addons/update.webapp
@@ -0,0 +1,5 @@
+{
+  "name": "Addon app",
+  "description": "Let me inject script and css!",
+  "package_path" : "application.zip"
+}
new file mode 100644
--- /dev/null
+++ b/dom/apps/tests/addons/update.webapp^headers^
@@ -0,0 +1,1 @@
+Content-Type: application/manifest+json
\ No newline at end of file
--- a/dom/apps/tests/mochitest.ini
+++ b/dom/apps/tests/mochitest.ini
@@ -1,11 +1,15 @@
 [DEFAULT]
 skip-if = e10s
 support-files =
+  addons/application.zip
+  addons/update.webapp
+  addons/update.webapp^headers^
+  addons/index.html
   chromeAddCert.js
   file_app.sjs
   file_app.template.html
   file_script.template.js
   file_cached_app.template.appcache
   file_cached_app.template.webapp
   file_hosted_app.template.webapp
   file_manifest.json
@@ -20,16 +24,18 @@ support-files =
   file_test_widget.js
   signed_app.sjs
   signed_app_template.webapp
   signed/*
   test_packaged_app_common.js
   marketplace/*
   pkg_install_iframe.html
 
+[test_app_addons.html]
+skip-if = os == "android" || toolkit == "gonk" # embed-apps doesn't work in mochitest app
 [test_app_enabled.html]
 [test_app_update.html]
 skip-if = os == "android" || toolkit == "gonk" # embed-apps doesn't work in mochitest app
 [test_bug_795164.html]
 [test_import_export.html]
 [test_install_multiple_apps_origin.html]
 [test_install_receipts.html]
 [test_marketplace_pkg_install.html]
new file mode 100644
--- /dev/null
+++ b/dom/apps/tests/test_app_addons.html
@@ -0,0 +1,186 @@
+<!DOCTYPE HTML><!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1042881
+-->
+<html>
+  <head>
+  <meta charset="utf-8">
+  <title>Test for Bug 923897 - Test apps as addons</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" />
+  <script type="application/javascript;version=1.7">
+/**
+  * Test for Bug 923897
+  * This file covers testing addons.
+  *
+  * The setup is as follows:
+  * - app is installed and offers both script and css to inject in
+  *   http://mochi.test:8888/tests/dom/apps/tests/addons/index.html
+  */
+
+SimpleTest.waitForExplicitFinish();
+
+let appManifestURL = "http://mochi.test:8888/tests/dom/apps/tests/addons/update.webapp";
+
+let gGenerator = runTest();
+
+function go() {
+  gGenerator.next();
+}
+
+function continueTest() {
+  try {
+    gGenerator.next();
+  } catch (e if e instanceof StopIteration) {
+    SimpleTest.finish();
+  }
+}
+
+function mozAppsError() {
+  ok(false, "mozApps error: " + this.error.name);
+  SimpleTest.finish();
+}
+
+// Triggers one navigation test to the given page.
+// Waits for alert() messages before tearing down the iframe.
+function openPage(pageURL, messages) {
+  info("Navigating to " + pageURL);
+  let ifr = document.createElement("iframe");
+  let listener = function(event) {
+    let message = messages.shift();
+    is(event.detail.message, message, "Checking alert message for " + pageURL);
+    if (messages.length == 0) {
+      ifr.removeEventListener("mozbrowsershowmodalprompt", listener);
+      ifr.parentNode.removeChild(ifr);
+      continueTest();
+    }
+  }
+
+  ifr.addEventListener("mozbrowsershowmodalprompt", listener, false);
+
+  // Open an the app url in an iframe.
+  ifr.setAttribute("mozbrowser", "true");
+  ifr.setAttribute("src", pageURL);
+  document.getElementById("container").appendChild(ifr);
+}
+
+let apps = [];
+
+function installApp(manifestURL) {
+  info("About to install app at " + manifestURL);
+  let req = navigator.mozApps.installPackage(manifestURL);
+  req.onsuccess = function() {
+    apps.push(req.result);
+    is(req.result.manifestURL, manifestURL, "app installed");
+    if (req.result.installState == "installed") {
+      is(req.result.installState, "installed", "app downloaded");
+      continueTest();
+    } else {
+      req.result.ondownloadapplied = function() {
+        is(req.result.installState, "installed", "app downloaded");
+        continueTest();
+      }
+    }
+  }
+  req.onerror = mozAppsError;
+}
+
+function runTest() {
+  // Set up.
+  SpecialPowers.setAllAppsLaunchable(true);
+  SpecialPowers.allowUnsignedAddons();
+  SpecialPowers.pushPrefEnv({'set': [
+    ["dom.mozBrowserFramesEnabled", true],
+    ["dom.apps.customization.enabled", true],
+    ]},continueTest);
+  yield undefined;
+
+  SpecialPowers.pushPermissions(
+    [{ "type": "webapps-manage", "allow": 1, "context": document },
+     { "type": "browser", "allow": 1, "context": document } ],
+    continueTest);
+  yield undefined;
+
+  SpecialPowers.autoConfirmAppInstall(continueTest);
+  yield undefined;
+
+  SpecialPowers.autoConfirmAppUninstall(continueTest);
+  yield undefined;
+
+  // Opens the iframe to the test page, initial state.
+  openPage("http://mochi.test:8888/tests/dom/apps/tests/addons/index.html",
+    ["Lorem ipsum", "rgb(0, 0, 0)",
+     "Uncustomized content", "rgb(0, 0, 0)"]);
+  yield undefined;
+
+  // Install addon app.
+  installApp(appManifestURL);
+  yield undefined;
+
+  // Opens the iframe to the test page, customized.
+  openPage("http://mochi.test:8888/tests/dom/apps/tests/addons/index.html",
+    ["Hello World!", "rgb(255, 0, 0)",
+     "Customized content", "rgb(0, 0, 255)"]);
+  yield undefined;
+
+  // Disable the app.
+  navigator.mozApps.mgmt.onenabledstatechange = function(event) {
+    ok(true, "onenabledstatechange received");
+    is(event.application.enabled, false, "Application is disabled");
+    is(apps[0].enabled, false, "Application is disabled");
+    continueTest();
+  }
+
+  navigator.mozApps.mgmt.setEnabled(apps[0], false);
+  yield undefined;
+
+  // Opens the iframe to the test page, back to initial state.
+  openPage("http://mochi.test:8888/tests/dom/apps/tests/addons/index.html",
+    ["Lorem ipsum", "rgb(0, 0, 0)",
+     "Uncustomized content", "rgb(0, 0, 0)"]);
+  yield undefined;
+
+  // Re-enable the app.
+  navigator.mozApps.mgmt.onenabledstatechange = function(event) {
+    ok(true, "onenabledstatechange received");
+    is(event.application.enabled, true, "Application is enabled");
+    is(apps[0].enabled, true, "Application is enabled");
+    continueTest();
+  }
+
+  navigator.mozApps.mgmt.setEnabled(apps[0], true);
+  yield undefined;
+
+  // Opens the iframe to the test page, customized.
+  openPage("http://mochi.test:8888/tests/dom/apps/tests/addons/index.html",
+    ["Hello World!", "rgb(255, 0, 0)",
+     "Customized content", "rgb(0, 0, 255)"]);
+  yield undefined;
+
+  // Clean up after ourselves by uninstalling apps.
+  while (apps.length) {
+    let app = apps.pop();
+    req = navigator.mozApps.mgmt.uninstall(app);
+    req.onsuccess = continueTest;
+    req.onerror = mozAppsError;
+    yield undefined;
+  }
+
+  // Opens the iframe to the test page, back to initial state.
+  openPage("http://mochi.test:8888/tests/dom/apps/tests/addons/index.html",
+    ["Lorem ipsum", "rgb(0, 0, 0)",
+     "Uncustomized content", "rgb(0, 0, 0)"]);
+  yield undefined;
+}
+
+  </script>
+  </head>
+<body onload="go()">
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+<div id="container"></div>
+</body>
+</html>
--- a/dom/ipc/preload.js
+++ b/dom/ipc/preload.js
@@ -22,16 +22,21 @@ const BrowserElementIsPreloaded = true;
   Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
   Cu.import("resource://gre/modules/FileUtils.jsm");
   Cu.import("resource://gre/modules/Geometry.jsm");
   Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
   Cu.import("resource://gre/modules/NetUtil.jsm");
   Cu.import("resource://gre/modules/Services.jsm");
   Cu.import("resource://gre/modules/SettingsDB.jsm");
   Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+  try {
+    if (Services.prefs.getBoolPref("dom.apps.customization.enabled")) {
+      Cu.import("resource://gre/modules/UserCustomizations.jsm");
+    }
+  } catch(e) {}
 
   Cc["@mozilla.org/appshell/appShellService;1"].getService(Ci["nsIAppShellService"]);
   Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci["nsIWindowMediator"]);
   Cc["@mozilla.org/AppsService;1"].getService(Ci["nsIAppsService"]);
   Cc["@mozilla.org/base/telemetry;1"].getService(Ci["nsITelemetry"]);
   Cc["@mozilla.org/categorymanager;1"].getService(Ci["nsICategoryManager"]);
   Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci["nsIMessageSender"]);
   Cc["@mozilla.org/consoleservice;1"].getService(Ci["nsIConsoleService"]);
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -489,39 +489,33 @@ XPCOMUtils.defineLazyGetter(this, "gRadi
 
       let msg = _pendingMessages.shift();
       this._handleMessage(msg);
     },
 
     _getNumCards: function() {
       let numCards = 0;
       for (let i = 0, N = _ril.numRadioInterfaces; i < N; ++i) {
-        if (this._isCardPresentAtClient(i)) {
+        if (_ril.getRadioInterface(i).isCardPresent()) {
           numCards++;
         }
       }
       return numCards;
     },
 
-    _isCardPresentAtClient: function(clientId) {
-      let cardState = _ril.getRadioInterface(clientId).rilContext.cardState;
-      return cardState !== Ci.nsIIccProvider.CARD_STATE_UNDETECTED &&
-             cardState !== Ci.nsIIccProvider.CARD_STATE_UNKNOWN;
-    },
-
     _isRadioAbleToEnableAtClient: function(clientId, numCards) {
       if (!RILQUIRKS_RADIO_OFF_WO_CARD) {
         return true;
       }
 
       // We could only turn on the radio for clientId if
       // 1. a SIM card is presented or
       // 2. it is the default clientId and there is no any SIM card at any client.
 
-      if (this._isCardPresentAtClient(clientId)) {
+      if (_ril.getRadioInterface(clientId).isCardPresent()) {
         return true;
       }
 
       numCards = numCards == null ? this._getNumCards() : numCards;
       if (clientId === HW_DEFAULT_CLIENT_ID && numCards === 0) {
         return true;
       }
 
@@ -1506,22 +1500,25 @@ RadioInterfaceLayer.prototype = {
    * nsIRadioInterfaceLayer interface methods.
    */
 
   getRadioInterface: function(clientId) {
     return this.radioInterfaces[clientId];
   },
 
   getClientIdForEmergencyCall: function() {
+    // Select the client with sim card first.
     for (let cid = 0; cid < this.numRadioInterfaces; ++cid) {
-      if (gRadioEnabledController._isRadioAbleToEnableAtClient(cid)) {
+      if (this.getRadioInterface(cid).isCardPresent()) {
         return cid;
       }
     }
-    return -1;
+
+    // Use the defualt client if no card presents.
+    return HW_DEFAULT_CLIENT_ID;
   },
 
   setMicrophoneMuted: function(muted) {
     for (let clientId = 0; clientId < this.numRadioInterfaces; clientId++) {
       let radioInterface = this.radioInterfaces[clientId];
       radioInterface.workerMessenger.send("setMute", { muted: muted });
     }
   }
@@ -1819,16 +1816,22 @@ RadioInterface.prototype = {
       if (srcInfo[key] !== destInfo[key]) {
         return true;
       }
     }
 
     return false;
   },
 
+  isCardPresent: function() {
+    let cardState = this.rilContext.cardState;
+    return cardState !== Ci.nsIIccProvider.CARD_STATE_UNDETECTED &&
+      cardState !== Ci.nsIIccProvider.CARD_STATE_UNKNOWN;
+  },
+
   /**
    * Process a message from the content process.
    */
   receiveMessage: function(msg) {
     switch (msg.name) {
       case "RIL:GetRilContext":
         // This message is sync.
         return this.rilContext;
--- a/dom/telephony/Telephony.cpp
+++ b/dom/telephony/Telephony.cpp
@@ -57,37 +57,27 @@ public:
   void
   Disconnect()
   {
     MOZ_ASSERT(mTelephony);
     mTelephony = nullptr;
   }
 };
 
-class Telephony::EnumerationAck : public nsRunnable
+Telephony::Telephony(nsPIDOMWindow* aOwner)
+  : DOMEventTargetHelper(aOwner)
 {
-  nsRefPtr<Telephony> mTelephony;
-
-public:
-  explicit EnumerationAck(Telephony* aTelephony)
-  : mTelephony(aTelephony)
-  {
-    MOZ_ASSERT(mTelephony);
-  }
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aOwner);
+  MOZ_ASSERT(global);
 
-  NS_IMETHOD Run()
-  {
-    mTelephony->NotifyEvent(NS_LITERAL_STRING("ready"));
-    return NS_OK;
-  }
-};
+  ErrorResult rv;
+  nsRefPtr<Promise> promise = Promise::Create(global, rv);
+  MOZ_ASSERT(!rv.Failed());
 
-Telephony::Telephony(nsPIDOMWindow* aOwner)
-  : DOMEventTargetHelper(aOwner), mEnumerated(false)
-{
+  mReadyPromise = promise;
 }
 
 Telephony::~Telephony()
 {
   Shutdown();
 }
 
 void
@@ -322,24 +312,26 @@ Telephony::GetCallFromEverywhere(uint32_
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(Telephony)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(Telephony,
                                                   DOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCalls)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallsList)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGroup)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReadyPromise)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Telephony,
                                                 DOMEventTargetHelper)
   tmp->Shutdown();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCalls)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallsList)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mGroup)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mReadyPromise)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(Telephony)
   // Telephony does not expose nsITelephonyListener.  mListener is the exposed
   // nsITelephonyListener and forwards the calls it receives to us.
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(Telephony, DOMEventTargetHelper)
@@ -457,24 +449,26 @@ Telephony::Calls() const
 
 already_AddRefed<TelephonyCallGroup>
 Telephony::ConferenceGroup() const
 {
   nsRefPtr<TelephonyCallGroup> group = mGroup;
   return group.forget();
 }
 
-// EventTarget
+already_AddRefed<Promise>
+Telephony::GetReady(ErrorResult& aRv) const
+{
+  if (!mReadyPromise) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
 
-void
-Telephony::EventListenerAdded(nsIAtom* aType)
-{
-  if (aType == nsGkAtoms::onready) {
-    EnqueueEnumerationAck();
-  }
+  nsRefPtr<Promise> promise = mReadyPromise;
+  return promise.forget();
 }
 
 // nsITelephonyListener
 
 NS_IMETHODIMP
 Telephony::CallStateChanged(uint32_t aServiceId, uint32_t aCallIndex,
                             uint16_t aCallState, const nsAString& aNumber,
                             uint16_t aNumberPresentation, const nsAString& aName,
@@ -543,37 +537,33 @@ Telephony::ConferenceCallStateChanged(ui
 {
   mGroup->ChangeState(aCallState);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 Telephony::EnumerateCallStateComplete()
 {
-  MOZ_ASSERT(!mEnumerated);
-
   // Set conference state.
   if (mGroup->CallsArray().Length() >= 2) {
     const nsTArray<nsRefPtr<TelephonyCall> > &calls = mGroup->CallsArray();
 
     uint16_t callState = calls[0]->CallState();
     for (uint32_t i = 1; i < calls.Length(); i++) {
       if (calls[i]->CallState() != callState) {
         callState = nsITelephonyService::CALL_STATE_UNKNOWN;
         break;
       }
     }
 
     mGroup->ChangeState(callState);
   }
 
-  mEnumerated = true;
-
-  if (NS_FAILED(NotifyEvent(NS_LITERAL_STRING("ready")))) {
-    NS_WARNING("Failed to notify ready!");
+  if (mReadyPromise) {
+    mReadyPromise->MaybeResolve(JS::UndefinedHandleValue);
   }
 
   if (NS_FAILED(mService->RegisterListener(mListener))) {
     NS_WARNING("Failed to register listener!");
   }
   return NS_OK;
 }
 
@@ -688,29 +678,16 @@ Telephony::DispatchCallEvent(const nsASt
   init.mCancelable = false;
   init.mCall = aCall;
 
   nsRefPtr<CallEvent> event = CallEvent::Constructor(this, aType, init);
 
   return DispatchTrustedEvent(event);
 }
 
-void
-Telephony::EnqueueEnumerationAck()
-{
-  if (!mEnumerated) {
-    return;
-  }
-
-  nsCOMPtr<nsIRunnable> task = new EnumerationAck(this);
-  if (NS_FAILED(NS_DispatchToCurrentThread(task))) {
-    NS_WARNING("Failed to dispatch to current thread!");
-  }
-}
-
 already_AddRefed<nsITelephonyService>
 NS_CreateTelephonyService()
 {
   nsCOMPtr<nsITelephonyService> service;
 
   if (XRE_GetProcessType() == GeckoProcessType_Content) {
     service = new mozilla::dom::telephony::TelephonyIPCService();
   } else {
--- a/dom/telephony/Telephony.h
+++ b/dom/telephony/Telephony.h
@@ -35,30 +35,28 @@ class Telephony MOZ_FINAL : public DOMEv
   /**
    * Class Telephony doesn't actually expose nsITelephonyListener.
    * Instead, it owns an nsITelephonyListener derived instance mListener
    * and passes it to nsITelephonyService. The onreceived events are first
    * delivered to mListener and then forwarded to its owner, Telephony. See
    * also bug 775997 comment #51.
    */
   class Listener;
-  class EnumerationAck;
 
-  friend class EnumerationAck;
   friend class telephony::TelephonyDialCallback;
 
   nsCOMPtr<nsITelephonyService> mService;
   nsRefPtr<Listener> mListener;
 
   nsTArray<nsRefPtr<TelephonyCall> > mCalls;
   nsRefPtr<CallsList> mCallsList;
 
   nsRefPtr<TelephonyCallGroup> mGroup;
 
-  bool mEnumerated;
+  nsRefPtr<Promise> mReadyPromise;
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSITELEPHONYLISTENER
   NS_REALLY_FORWARD_NSIDOMEVENTTARGET(DOMEventTargetHelper)
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Telephony,
                                            DOMEventTargetHelper)
 
@@ -104,17 +102,19 @@ public:
   GetActive(Nullable<OwningTelephonyCallOrTelephonyCallGroup>& aValue);
 
   already_AddRefed<CallsList>
   Calls() const;
 
   already_AddRefed<TelephonyCallGroup>
   ConferenceGroup() const;
 
-  IMPL_EVENT_HANDLER(ready)
+  already_AddRefed<Promise>
+  GetReady(ErrorResult& aRv) const;
+
   IMPL_EVENT_HANDLER(incoming)
   IMPL_EVENT_HANDLER(callschanged)
   IMPL_EVENT_HANDLER(remoteheld)
   IMPL_EVENT_HANDLER(remoteresumed)
 
   static already_AddRefed<Telephony>
   Create(nsPIDOMWindow* aOwner, ErrorResult& aRv);
 
@@ -141,18 +141,16 @@ public:
   }
 
   const nsTArray<nsRefPtr<TelephonyCall> >&
   CallsArray() const
   {
     return mCalls;
   }
 
-  virtual void EventListenerAdded(nsIAtom* aType) MOZ_OVERRIDE;
-
 private:
   explicit Telephony(nsPIDOMWindow* aOwner);
   ~Telephony();
 
   void
   Shutdown();
 
   static bool
@@ -193,19 +191,16 @@ private:
   NotifyEvent(const nsAString& aType);
 
   nsresult
   NotifyCallsChanged(TelephonyCall* aCall);
 
   nsresult
   DispatchCallEvent(const nsAString& aType, TelephonyCall* aCall);
 
-  void
-  EnqueueEnumerationAck();
-
   already_AddRefed<TelephonyCall>
   GetCall(uint32_t aServiceId, uint32_t aCallIndex);
 
   already_AddRefed<TelephonyCall>
   GetCallFromEverywhere(uint32_t aServiceId, uint32_t aCallIndex);
 };
 
 } // namespace dom
--- a/dom/telephony/test/marionette/test_ready.js
+++ b/dom/telephony/test/marionette/test_ready.js
@@ -8,28 +8,28 @@ SpecialPowers.addPermission("telephony",
 function cleanUp() {
   SpecialPowers.removePermission("telephony", document);
   finish();
 }
 
 let telephony = window.navigator.mozTelephony;
 ok(telephony);
 
-telephony.onready = function() {
-  log("Receive 'ready' event");
+telephony.ready.then(function() {
+  log("Telephony got ready");
 
-  // Test registering 'ready' event in another window.
+  // Test telephony.ready in another window.
   let iframe = document.createElement("iframe");
   iframe.addEventListener("load", function load() {
     iframe.removeEventListener("load", load);
 
     let iframeTelephony = iframe.contentWindow.navigator.mozTelephony;
     ok(iframeTelephony);
 
-    iframeTelephony.onready = function() {
-      log("Receive 'ready' event in iframe");
+    iframeTelephony.ready.then(function() {
+      log("Telephony in iframe got ready");
 
       cleanUp();
-    };
+    });
   });
 
   document.body.appendChild(iframe);
-};
+});
--- a/dom/webidl/Telephony.webidl
+++ b/dom/webidl/Telephony.webidl
@@ -41,16 +41,17 @@ interface Telephony : EventTarget {
   attribute boolean speakerEnabled;
 
   readonly attribute (TelephonyCall or TelephonyCallGroup)? active;
 
   // A call is contained either in Telephony or in TelephonyCallGroup.
   readonly attribute CallsList calls;
   readonly attribute TelephonyCallGroup conferenceGroup;
 
-  // The 'ready' event will be fired when the telephony object is ready.
-  attribute EventHandler onready;
+  // Async notification that object initialization is done.
+  [Throws]
+  readonly attribute Promise<void> ready;
 
   attribute EventHandler onincoming;
   attribute EventHandler oncallschanged;
   attribute EventHandler onremoteheld;
   attribute EventHandler onremoteresumed;
 };
--- a/hal/gonk/GonkHal.cpp
+++ b/hal/gonk/GonkHal.cpp
@@ -22,17 +22,21 @@
 #include <math.h>
 #include <regex.h>
 #include <sched.h>
 #include <stdio.h>
 #include <sys/klog.h>
 #include <sys/syscall.h>
 #include <sys/resource.h>
 #include <time.h>
+#if ANDROID_VERSION >= 21
+#include <limits.h>
+#else
 #include <asm/page.h>
+#endif
 
 #include "mozilla/DebugOnly.h"
 
 #include "android/log.h"
 #include "cutils/properties.h"
 #include "hardware/hardware.h"
 #include "hardware/lights.h"
 #include "hardware_legacy/uevent.h"
--- a/js/xpconnect/loader/mozJSSubScriptLoader.cpp
+++ b/js/xpconnect/loader/mozJSSubScriptLoader.cpp
@@ -341,17 +341,17 @@ mozJSSubScriptLoader::DoLoadSubScriptWit
         return ReportError(cx, LOAD_ERROR_NOSPEC);
     }
 
     rv = uri->GetScheme(scheme);
     if (NS_FAILED(rv)) {
         return ReportError(cx, LOAD_ERROR_NOSCHEME, uri);
     }
 
-    if (!scheme.EqualsLiteral("chrome")) {
+    if (!scheme.EqualsLiteral("chrome") && !scheme.EqualsLiteral("app")) {
         // This might be a URI to a local file, though!
         nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(uri);
         nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(innerURI);
         if (!fileURL) {
             return ReportError(cx, LOAD_ERROR_URI_NOT_LOCAL, uri);
         }
 
         // For file URIs prepend the filename with the filename of the
--- a/testing/profiles/prefs_general.js
+++ b/testing/profiles/prefs_general.js
@@ -246,16 +246,18 @@ user_pref("security.ssl.errorReporting.u
 user_pref("browser.translation.bing.authURL", "http://%(server)s/browser/browser/components/translation/test/bing.sjs");
 user_pref("browser.translation.bing.translateArrayURL", "http://%(server)s/browser/browser/components/translation/test/bing.sjs");
 
 // Make sure we don't try to load snippets from the network.
 user_pref("browser.aboutHomeSnippets.updateUrl", "nonexistent://test");
 
 // Enable debug logging in the mozApps implementation.
 user_pref("dom.mozApps.debug", true);
+// Enable apps customizations
+user_pref("dom.apps.customization.enabled", true);
 
 // Don't fetch or send directory tiles data from real servers
 user_pref("browser.newtabpage.directory.source", 'data:application/json,{"testing":1}');
 user_pref("browser.newtabpage.directory.ping", "");
 
 // Enable Loop
 user_pref("loop.enabled", true);
 user_pref("loop.throttled", false);
--- a/testing/specialpowers/content/SpecialPowersObserverAPI.js
+++ b/testing/specialpowers/content/SpecialPowersObserverAPI.js
@@ -348,16 +348,23 @@ SpecialPowersObserverAPI.prototype = {
       case "SPWebAppService": {
         let Webapps = {};
         Components.utils.import("resource://gre/modules/Webapps.jsm", Webapps);
         switch (aMessage.json.op) {
           case "set-launchable":
             let val = Webapps.DOMApplicationRegistry.allAppsLaunchable;
             Webapps.DOMApplicationRegistry.allAppsLaunchable = aMessage.json.launchable;
             return val;
+          case "allow-unsigned-addons":
+            {
+              let utils = {};
+              Components.utils.import("resource://gre/modules/AppsUtils.jsm", utils);
+              utils.AppsUtils.allowUnsignedAddons = true;
+              return;
+            }
           default:
             throw new SpecialPowersException("Invalid operation for SPWebAppsService");
         }
         return undefined;	// See comment at the beginning of this function.
       }
 
       case "SPObserverService": {
         switch (aMessage.json.op) {
--- a/testing/specialpowers/content/specialpowersAPI.js
+++ b/testing/specialpowers/content/specialpowersAPI.js
@@ -1094,16 +1094,23 @@ SpecialPowersAPI.prototype = {
   // test higher level WebApp functionality without full platform support.
   setAllAppsLaunchable: function(launchable) {
     this._sendSyncMessage("SPWebAppService", {
       op: "set-launchable",
       launchable: launchable
     });
   },
 
+  // Allow tests to install addons without signing the package, for convenience.
+  allowUnsignedAddons: function() {
+    this._sendSyncMessage("SPWebAppService", {
+      op: "allow-unsigned-addons"
+    });
+  },
+
   // Restore the launchable property to its default value.
   flushAllAppsLaunchable: function() {
     this._sendSyncMessage("SPWebAppService", {
       op: "set-launchable",
       launchable: false
     });
   },