merge b2g-inbound to mozilla-central
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 24 Apr 2014 13:12:50 +0200
changeset 180319 9d3da41ad0b6ef945bd06c680a9e23fcfc1fa406
parent 180293 c8055a00235db1ddadfdd74e1d8181537117c23c (current diff)
parent 180318 f020075638857c8815682eb95e1adde1fd88049d (diff)
child 180320 fb7439e1e112b9320dfe7317b133f0a98a072e1c
child 180377 370c4a1ef06bae56af5b8068fa492234702f4a87
child 180421 27a7bd55597e1e746365578c38c4dae3fd53283b
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
milestone31.0a1
merge b2g-inbound to mozilla-central
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="2a165bebfa19b11b697837409f9550dd2917c46c">
     <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="d25852a189c9707b144eb5f82d08384eb066c0fd"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="6a2eb4b3664fb3e6f5c87db2af8485b7424f9ecb"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="0292e64ef8451df104dcf9ac3b2c6749b81684dd"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="85f9690323b235f4dcf2901ea2240d3c60fc22a0"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e6383e6e785cc3ea237e902beb1092f9aa88e29d">
     <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="d25852a189c9707b144eb5f82d08384eb066c0fd"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="6a2eb4b3664fb3e6f5c87db2af8485b7424f9ecb"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
   <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"/>
@@ -123,14 +123,14 @@
   <project name="platform/system/security" path="system/security" revision="f48ff68fedbcdc12b570b7699745abb6e7574907"/>
   <project name="platform/system/vold" path="system/vold" revision="8de05d4a52b5a91e7336e6baa4592f945a6ddbea"/>
   <default remote="caf" revision="refs/tags/android-4.3_r2.1" sync-j="4"/>
   <!-- Emulator specific things -->
   <project name="android-development" path="development" remote="b2g" revision="dab55669da8f48b6e57df95d5af9f16b4a87b0b1"/>
   <project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="3a9a17613cc685aa232432566ad6cc607eab4ec1"/>
   <project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="09485b73629856b21b2ed6073e327ab0e69a1189"/>
   <project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="7d33aaf740bbf6c7c6e9c34a92b371eda311b66b"/>
-  <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="9a00b3db898a83ef0766baa93e935e525572899e"/>
+  <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="bb11a0417efa7e6a08ed1cb2d5d6a13a3100abd3"/>
   <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="72e3a520e3c700839f07ba0113fd527b923c3330"/>
   <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="baaf899afb158b9530690002f3656e958e3eb047"/>
   <project name="android-sdk" path="sdk" remote="b2g" revision="8b1365af38c9a653df97349ee53a3f5d64fd590a"/>
 </manifest>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="65fba428f8d76336b33ddd9e15900357953600ba">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="d25852a189c9707b144eb5f82d08384eb066c0fd"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="6a2eb4b3664fb3e6f5c87db2af8485b7424f9ecb"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <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="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
   <!-- Stock Android things -->
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="2a165bebfa19b11b697837409f9550dd2917c46c">
     <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="d25852a189c9707b144eb5f82d08384eb066c0fd"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="6a2eb4b3664fb3e6f5c87db2af8485b7424f9ecb"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="0292e64ef8451df104dcf9ac3b2c6749b81684dd"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="85f9690323b235f4dcf2901ea2240d3c60fc22a0"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -13,17 +13,17 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e6383e6e785cc3ea237e902beb1092f9aa88e29d">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="d25852a189c9707b144eb5f82d08384eb066c0fd"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="6a2eb4b3664fb3e6f5c87db2af8485b7424f9ecb"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
   <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"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "git_revision": "", 
         "remote": "", 
         "branch": ""
     }, 
-    "revision": "9f480d1c52a46cac8e9a0be04bdb0d73810bc596", 
+    "revision": "2eae294604eb70e0e6eaee76b17e155ffd031107", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="2a165bebfa19b11b697837409f9550dd2917c46c">
     <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="d25852a189c9707b144eb5f82d08384eb066c0fd"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="6a2eb4b3664fb3e6f5c87db2af8485b7424f9ecb"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="2a165bebfa19b11b697837409f9550dd2917c46c">
     <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="d25852a189c9707b144eb5f82d08384eb066c0fd"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="6a2eb4b3664fb3e6f5c87db2af8485b7424f9ecb"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <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
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="2a165bebfa19b11b697837409f9550dd2917c46c">
     <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="d25852a189c9707b144eb5f82d08384eb066c0fd"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="6a2eb4b3664fb3e6f5c87db2af8485b7424f9ecb"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
   <!-- 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
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="2a165bebfa19b11b697837409f9550dd2917c46c">
     <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="d25852a189c9707b144eb5f82d08384eb066c0fd"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="6a2eb4b3664fb3e6f5c87db2af8485b7424f9ecb"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
   <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
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e6383e6e785cc3ea237e902beb1092f9aa88e29d">
     <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="d25852a189c9707b144eb5f82d08384eb066c0fd"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="6a2eb4b3664fb3e6f5c87db2af8485b7424f9ecb"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="2a165bebfa19b11b697837409f9550dd2917c46c">
     <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="d25852a189c9707b144eb5f82d08384eb066c0fd"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="6a2eb4b3664fb3e6f5c87db2af8485b7424f9ecb"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/dom/apps/src/InterAppCommService.js
+++ b/dom/apps/src/InterAppCommService.js
@@ -1,874 +1,28 @@
 /* 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 { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
-Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/AppsUtils.jsm");
+Cu.import("resource://gre/modules/InterAppCommService.jsm");
 
-const DEBUG = false;
-function debug(aMsg) {
-  dump("-- InterAppCommService: " + Date.now() + ": " + aMsg + "\n");
+function InterAppCommServiceProxy() {
 }
 
-XPCOMUtils.defineLazyServiceGetter(this, "appsService",
-                                   "@mozilla.org/AppsService;1",
-                                   "nsIAppsService");
-
-XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
-                                   "@mozilla.org/parentprocessmessagemanager;1",
-                                   "nsIMessageBroadcaster");
-
-XPCOMUtils.defineLazyServiceGetter(this, "UUIDGenerator",
-                                   "@mozilla.org/uuid-generator;1",
-                                   "nsIUUIDGenerator");
-
-XPCOMUtils.defineLazyServiceGetter(this, "messenger",
-                                   "@mozilla.org/system-message-internal;1",
-                                   "nsISystemMessagesInternal");
-
-const kMessages =["Webapps:Connect",
-                  "Webapps:GetConnections",
-                  "InterAppConnection:Cancel",
-                  "InterAppMessagePort:PostMessage",
-                  "InterAppMessagePort:Register",
-                  "InterAppMessagePort:Unregister",
-                  "child-process-shutdown"];
-
-function InterAppCommService() {
-  Services.obs.addObserver(this, "xpcom-shutdown", false);
-  Services.obs.addObserver(this, "inter-app-comm-select-app-result", false);
-
-  kMessages.forEach(function(aMsg) {
-    ppmm.addMessageListener(aMsg, this);
-  }, this);
-
-  // This matrix is used for saving the inter-app connection info registered in
-  // the app manifest. The object literal is defined as below:
-  //
-  // {
-  //   "keyword1": {
-  //     "subAppManifestURL1": {
-  //       /* subscribed info */
-  //     },
-  //     "subAppManifestURL2": {
-  //       /* subscribed info */
-  //     },
-  //     ...
-  //   },
-  //   "keyword2": {
-  //     "subAppManifestURL3": {
-  //       /* subscribed info */
-  //     },
-  //     ...
-  //   },
-  //   ...
-  // }
-  //
-  // For example:
-  //
-  // {
-  //   "foo": {
-  //     "app://subApp1.gaiamobile.org/manifest.webapp": {
-  //       pageURL: "app://subApp1.gaiamobile.org/handler.html",
-  //       description: "blah blah",
-  //       rules: { ... }
-  //     },
-  //     "app://subApp2.gaiamobile.org/manifest.webapp": {
-  //       pageURL: "app://subApp2.gaiamobile.org/handler.html",
-  //       description: "blah blah",
-  //       rules: { ... }
-  //     }
-  //   },
-  //   "bar": {
-  //     "app://subApp3.gaiamobile.org/manifest.webapp": {
-  //       pageURL: "app://subApp3.gaiamobile.org/handler.html",
-  //       description: "blah blah",
-  //       rules: { ... }
-  //     }
-  //   }
-  // }
-  //
-  // TODO Bug 908999 - Update registered connections when app gets uninstalled.
-  this._registeredConnections = {};
-
-  // This matrix is used for saving the permitted connections, which allows
-  // the messaging between publishers and subscribers. The object literal is
-  // defined as below:
-  //
-  // {
-  //   "keyword1": {
-  //     "pubAppManifestURL1": [
-  //       "subAppManifestURL1",
-  //       "subAppManifestURL2",
-  //       ...
-  //     ],
-  //     "pubAppManifestURL2": [
-  //       "subAppManifestURL3",
-  //       "subAppManifestURL4",
-  //       ...
-  //     ],
-  //     ...
-  //   },
-  //   "keyword2": {
-  //     "pubAppManifestURL3": [
-  //       "subAppManifestURL5",
-  //       ...
-  //     ],
-  //     ...
-  //   },
-  //   ...
-  // }
-  //
-  // For example:
-  //
-  // {
-  //   "foo": {
-  //     "app://pubApp1.gaiamobile.org/manifest.webapp": [
-  //       "app://subApp1.gaiamobile.org/manifest.webapp",
-  //       "app://subApp2.gaiamobile.org/manifest.webapp"
-  //     ],
-  //     "app://pubApp2.gaiamobile.org/manifest.webapp": [
-  //       "app://subApp3.gaiamobile.org/manifest.webapp",
-  //       "app://subApp4.gaiamobile.org/manifest.webapp"
-  //     ]
-  //   },
-  //   "bar": {
-  //     "app://pubApp3.gaiamobile.org/manifest.webapp": [
-  //       "app://subApp5.gaiamobile.org/manifest.webapp",
-  //     ]
-  //   }
-  // }
-  //
-  // TODO Bug 908999 - Update allowed connections when app gets uninstalled.
-  this._allowedConnections = {};
-
-  // This matrix is used for saving the caller info from the content process,
-  // which is indexed by a random UUID, to know where to return the promise
-  // resolvser's callback when the prompt UI for allowing connections returns.
-  // An example of the object literal is shown as below:
-  //
-  // {
-  //   "fooID": {
-  //     outerWindowID: 12,
-  //     requestID: 34,
-  //     target: pubAppTarget1
-  //   },
-  //   "barID": {
-  //     outerWindowID: 56,
-  //     requestID: 78,
-  //     target: pubAppTarget2
-  //   }
-  // }
-  //
-  // where |outerWindowID| is the ID of the window requesting the connection,
-  //       |requestID| is the ID specifying the promise resolver to return,
-  //       |target| is the target of the process requesting the connection.
-  this._promptUICallers = {};
-
-  // This matrix is used for saving the pair of message ports, which is indexed
-  // by a random UUID, so that each port can know whom it should talk to.
-  // An example of the object literal is shown as below:
-  //
-  // {
-  //   "UUID1": {
-  //     keyword: "keyword1",
-  //     publisher: {
-  //       manifestURL: "app://pubApp1.gaiamobile.org/manifest.webapp",
-  //       target: pubAppTarget1,
-  //       pageURL: "app://pubApp1.gaiamobile.org/caller.html",
-  //       messageQueue: [...]
-  //     },
-  //     subscriber: {
-  //       manifestURL: "app://subApp1.gaiamobile.org/manifest.webapp",
-  //       target: subAppTarget1,
-  //       pageURL: "app://pubApp1.gaiamobile.org/handler.html",
-  //       messageQueue: [...]
-  //     }
-  //   },
-  //   "UUID2": {
-  //     keyword: "keyword2",
-  //     publisher: {
-  //       manifestURL: "app://pubApp2.gaiamobile.org/manifest.webapp",
-  //       target: pubAppTarget2,
-  //       pageURL: "app://pubApp2.gaiamobile.org/caller.html",
-  //       messageQueue: [...]
-  //     },
-  //     subscriber: {
-  //       manifestURL: "app://subApp2.gaiamobile.org/manifest.webapp",
-  //       target: subAppTarget2,
-  //       pageURL: "app://pubApp2.gaiamobile.org/handler.html",
-  //       messageQueue: [...]
-  //     }
-  //   }
-  // }
-  this._messagePortPairs = {};
-}
-
-InterAppCommService.prototype = {
+InterAppCommServiceProxy.prototype = {
   registerConnection: function(aKeyword, aHandlerPageURI, aManifestURI,
                                aDescription, aRules) {
-    let manifestURL = aManifestURI.spec;
-    let pageURL = aHandlerPageURI.spec;
-
-    if (DEBUG) {
-      debug("registerConnection: aKeyword: " + aKeyword +
-            " manifestURL: " + manifestURL + " pageURL: " + pageURL +
-            " aDescription: " + aDescription +
-            " aRules.minimumAccessLevel: " + aRules.minimumAccessLevel +
-            " aRules.manifestURLs: " + aRules.manifestURLs +
-            " aRules.installOrigins: " + aRules.installOrigins);
-    }
-
-    let subAppManifestURLs = this._registeredConnections[aKeyword];
-    if (!subAppManifestURLs) {
-      subAppManifestURLs = this._registeredConnections[aKeyword] = {};
-    }
-
-    subAppManifestURLs[manifestURL] = {
-      pageURL: pageURL,
-      description: aDescription,
-      rules: aRules,
-      manifestURL: manifestURL
-    };
-  },
-
-  _matchMinimumAccessLevel: function(aRules, aAppStatus) {
-    if (!aRules || !aRules.minimumAccessLevel) {
-      if (DEBUG) {
-        debug("rules.minimumAccessLevel is not available. No need to match.");
-      }
-      return true;
-    }
-
-    let minAccessLevel = aRules.minimumAccessLevel;
-    switch (minAccessLevel) {
-      case "web":
-        if (aAppStatus == Ci.nsIPrincipal.APP_STATUS_INSTALLED ||
-            aAppStatus == Ci.nsIPrincipal.APP_STATUS_PRIVILEGED ||
-            aAppStatus == Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
-          return true;
-        }
-        break;
-      case "privileged":
-        if (aAppStatus == Ci.nsIPrincipal.APP_STATUS_PRIVILEGED ||
-            aAppStatus == Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
-          return true;
-        }
-        break;
-      case "certified":
-        if (aAppStatus == Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
-          return true;
-        }
-        break;
-    }
-
-    if (DEBUG) {
-      debug("rules.minimumAccessLevel is not matched!" +
-            " minAccessLevel: " + minAccessLevel +
-            " aAppStatus : " + aAppStatus);
-    }
-    return false;
-  },
-
-  _matchManifestURLs: function(aRules, aManifestURL) {
-    if (!aRules || !Array.isArray(aRules.manifestURLs)) {
-      if (DEBUG) {
-        debug("rules.manifestURLs is not available. No need to match.");
-      }
-      return true;
-    }
-
-    let manifestURLs = aRules.manifestURLs;
-    if (manifestURLs.indexOf(aManifestURL) != -1) {
-      return true;
-    }
-
-    if (DEBUG) {
-      debug("rules.manifestURLs is not matched!" +
-            " manifestURLs: " + manifestURLs +
-            " aManifestURL : " + aManifestURL);
-    }
-    return false;
-  },
-
-  _matchInstallOrigins: function(aRules, aInstallOrigin) {
-    if (!aRules || !Array.isArray(aRules.installOrigins)) {
-      if (DEBUG) {
-        debug("rules.installOrigins is not available. No need to match.");
-      }
-      return true;
-    }
-
-    let installOrigins = aRules.installOrigins;
-    if (installOrigins.indexOf(aInstallOrigin) != -1) {
-      return true;
-    }
-
-    if (DEBUG) {
-      debug("rules.installOrigins is not matched!" +
-            " installOrigins: " + installOrigins +
-            " installOrigin : " + aInstallOrigin);
-    }
-    return false;
-  },
-
-  _matchRules: function(aPubAppManifestURL, aPubRules,
-                        aSubAppManifestURL, aSubRules) {
-    let pubApp = appsService.getAppByManifestURL(aPubAppManifestURL);
-    let subApp = appsService.getAppByManifestURL(aSubAppManifestURL);
-
-    // TODO Bug 907068 In the initiative step, we only expose this API to
-    // certified apps to meet the time line. Eventually, we need to make
-    // it available for the non-certified apps as well. For now, only the
-    // certified apps can match the rules.
-    if (pubApp.appStatus != Ci.nsIPrincipal.APP_STATUS_CERTIFIED ||
-        subApp.appStatus != Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
-      if (DEBUG) {
-        debug("Only certified apps are allowed to do connections.");
-      }
-      return false;
-    }
-
-    if (!aPubRules && !aSubRules) {
-      if (DEBUG) {
-        debug("No rules for publisher and subscriber. No need to match.");
-      }
-      return true;
-    }
-
-    // Check minimumAccessLevel.
-    if (!this._matchMinimumAccessLevel(aPubRules, subApp.appStatus) ||
-        !this._matchMinimumAccessLevel(aSubRules, pubApp.appStatus)) {
-      return false;
-    }
-
-    // Check manifestURLs.
-    if (!this._matchManifestURLs(aPubRules, aSubAppManifestURL) ||
-        !this._matchManifestURLs(aSubRules, aPubAppManifestURL)) {
-      return false;
-    }
-
-    // Check installOrigins.
-    if (!this._matchInstallOrigins(aPubRules, subApp.installOrigin) ||
-        !this._matchInstallOrigins(aSubRules, pubApp.installOrigin)) {
-      return false;
-    }
-
-    // Check developers.
-    // TODO Do we really want to check this? This one seems naive.
-
-    if (DEBUG) debug("All rules are matched.");
-    return true;
-  },
-
-  _dispatchMessagePorts: function(aKeyword, aPubAppManifestURL,
-                                  aAllowedSubAppManifestURLs,
-                                  aTarget, aOuterWindowID, aRequestID) {
-    if (DEBUG) {
-      debug("_dispatchMessagePorts: aKeyword: " + aKeyword +
-            " aPubAppManifestURL: " + aPubAppManifestURL +
-            " aAllowedSubAppManifestURLs: " + aAllowedSubAppManifestURLs);
-    }
-
-    if (aAllowedSubAppManifestURLs.length == 0) {
-      if (DEBUG) debug("No apps are allowed to connect. Returning.");
-      aTarget.sendAsyncMessage("Webapps:Connect:Return:KO",
-                               { oid: aOuterWindowID, requestID: aRequestID });
-      return;
-    }
-
-    let subAppManifestURLs = this._registeredConnections[aKeyword];
-    if (!subAppManifestURLs) {
-      if (DEBUG) debug("No apps are subscribed to connect. Returning.");
-      aTarget.sendAsyncMessage("Webapps:Connect:Return:KO",
-                               { oid: aOuterWindowID, requestID: aRequestID });
-      return;
-    }
-
-    let messagePortIDs = [];
-    aAllowedSubAppManifestURLs.forEach(function(aAllowedSubAppManifestURL) {
-      let subscribedInfo = subAppManifestURLs[aAllowedSubAppManifestURL];
-      if (!subscribedInfo) {
-        if (DEBUG) {
-          debug("The sunscribed info is not available. Skipping: " +
-                aAllowedSubAppManifestURL);
-        }
-        return;
-      }
-
-      // The message port ID is aimed for identifying the coupling targets
-      // to deliver messages with each other. This ID is centrally generated
-      // by the parent and dispatched to both the sender and receiver ends
-      // for creating their own message ports respectively.
-      let messagePortID = UUIDGenerator.generateUUID().toString();
-      this._messagePortPairs[messagePortID] = {
-        keyword: aKeyword,
-        publisher: {
-          manifestURL: aPubAppManifestURL
-        },
-        subscriber: {
-          manifestURL: aAllowedSubAppManifestURL
-        }
-      };
-
-      // Fire system message to deliver the message port to the subscriber.
-      messenger.sendMessage("connection",
-        { keyword: aKeyword,
-          messagePortID: messagePortID },
-        Services.io.newURI(subscribedInfo.pageURL, null, null),
-        Services.io.newURI(subscribedInfo.manifestURL, null, null));
-
-      messagePortIDs.push(messagePortID);
-    }, this);
-
-    if (messagePortIDs.length == 0) {
-      if (DEBUG) debug("No apps are subscribed to connect. Returning.");
-      aTarget.sendAsyncMessage("Webapps:Connect:Return:KO",
-                               { oid: aOuterWindowID, requestID: aRequestID });
-      return;
-    }
-
-    // Return the message port IDs to open the message ports for the publisher.
-    if (DEBUG) debug("messagePortIDs: " + messagePortIDs);
-    aTarget.sendAsyncMessage("Webapps:Connect:Return:OK",
-                             { keyword: aKeyword,
-                               messagePortIDs: messagePortIDs,
-                               oid: aOuterWindowID, requestID: aRequestID });
-  },
-
-  _connect: function(aMessage, aTarget) {
-    let keyword = aMessage.keyword;
-    let pubRules = aMessage.rules;
-    let pubAppManifestURL = aMessage.manifestURL;
-    let outerWindowID = aMessage.outerWindowID;
-    let requestID = aMessage.requestID;
-
-    let subAppManifestURLs = this._registeredConnections[keyword];
-    if (!subAppManifestURLs) {
-      if (DEBUG) {
-        debug("No apps are subscribed for this connection. Returning.");
-      }
-      this._dispatchMessagePorts(keyword, pubAppManifestURL, [],
-                                 aTarget, outerWindowID, requestID);
-      return;
-    }
-
-    // Fetch the apps that used to be allowed to connect before, so that
-    // users don't need to select/allow them again. That is, we only pop up
-    // the prompt UI for the *new* connections.
-    let allowedSubAppManifestURLs = [];
-    let allowedPubAppManifestURLs = this._allowedConnections[keyword];
-    if (allowedPubAppManifestURLs &&
-        allowedPubAppManifestURLs[pubAppManifestURL]) {
-      allowedSubAppManifestURLs = allowedPubAppManifestURLs[pubAppManifestURL];
-    }
-
-    // Check rules to see if a subscribed app is allowed to connect.
-    let appsToSelect = [];
-    for (let subAppManifestURL in subAppManifestURLs) {
-      if (allowedSubAppManifestURLs.indexOf(subAppManifestURL) != -1) {
-        if (DEBUG) {
-          debug("Don't need to select again. Skipping: " + subAppManifestURL);
-        }
-        continue;
-      }
-
-      // Only rule-matched publishers/subscribers are allowed to connect.
-      let subscribedInfo = subAppManifestURLs[subAppManifestURL];
-      let subRules = subscribedInfo.rules;
-
-      let matched =
-        this._matchRules(pubAppManifestURL, pubRules,
-                         subAppManifestURL, subRules);
-      if (!matched) {
-        if (DEBUG) {
-          debug("Rules are not matched. Skipping: " + subAppManifestURL);
-        }
-        continue;
-      }
-
-      appsToSelect.push({
-        manifestURL: subAppManifestURL,
-        description: subscribedInfo.description
-      });
-    }
-
-    if (appsToSelect.length == 0) {
-      if (DEBUG) {
-        debug("No additional apps need to be selected for this connection. " +
-              "Just dispatch message ports for the existing connections.");
-      }
-
-      this._dispatchMessagePorts(keyword, pubAppManifestURL,
-                                 allowedSubAppManifestURLs,
-                                 aTarget, outerWindowID, requestID);
-      return;
-    }
-
-    // Remember the caller info with an UUID so that we can know where to
-    // return the promise resolver's callback when the prompt UI returns.
-    let callerID = UUIDGenerator.generateUUID().toString();
-    this._promptUICallers[callerID] = {
-      outerWindowID: outerWindowID,
-      requestID: requestID,
-      target: aTarget
-    };
-
-    // TODO Bug 897169 Temporarily disable the notification for popping up
-    // the prompt until the UX/UI for the prompt is confirmed.
-    //
-    // TODO Bug 908191 We need to change the way of interaction between API and
-    // run-time prompt from observer notification to xpcom-interface caller.
-    //
-    /*
-    if (DEBUG) debug("appsToSelect: " + appsToSelect);
-    Services.obs.notifyObservers(null, "inter-app-comm-select-app",
-      JSON.stringify({ callerID: callerID,
-                       manifestURL: pubAppManifestURL,
-                       keyword: keyword,
-                       appsToSelect: appsToSelect }));
-    */
-
-    // TODO Bug 897169 Simulate the return of the app-selected result by
-    // the prompt, which always allows the connection. This dummy codes
-    // will be removed when the UX/UI for the prompt is ready.
-    if (DEBUG) debug("appsToSelect: " + appsToSelect);
-    Services.obs.notifyObservers(null, 'inter-app-comm-select-app-result',
-      JSON.stringify({ callerID: callerID,
-                       manifestURL: pubAppManifestURL,
-                       keyword: keyword,
-                       selectedApps: appsToSelect }));
-  },
-
-  _getConnections: function(aMessage, aTarget) {
-    let outerWindowID = aMessage.outerWindowID;
-    let requestID = aMessage.requestID;
-
-    let connections = [];
-    for (let keyword in this._allowedConnections) {
-      let allowedPubAppManifestURLs = this._allowedConnections[keyword];
-      for (let allowedPubAppManifestURL in allowedPubAppManifestURLs) {
-        let allowedSubAppManifestURLs =
-          allowedPubAppManifestURLs[allowedPubAppManifestURL];
-        allowedSubAppManifestURLs.forEach(function(allowedSubAppManifestURL) {
-          connections.push({ keyword: keyword,
-                             pubAppManifestURL: allowedPubAppManifestURL,
-                             subAppManifestURL: allowedSubAppManifestURL });
-        });
-      }
-    }
-
-    aTarget.sendAsyncMessage("Webapps:GetConnections:Return:OK",
-                             { connections: connections,
-                               oid: outerWindowID, requestID: requestID });
-  },
-
-  _cancelConnection: function(aMessage) {
-    let keyword = aMessage.keyword;
-    let pubAppManifestURL = aMessage.pubAppManifestURL;
-    let subAppManifestURL = aMessage.subAppManifestURL;
-
-    let allowedPubAppManifestURLs = this._allowedConnections[keyword];
-    if (!allowedPubAppManifestURLs) {
-      if (DEBUG) debug("keyword is not found: " + keyword);
-      return;
-    }
-
-    let allowedSubAppManifestURLs =
-      allowedPubAppManifestURLs[pubAppManifestURL];
-    if (!allowedSubAppManifestURLs) {
-      if (DEBUG) debug("publisher is not found: " + pubAppManifestURL);
-      return;
-    }
-
-    let index = allowedSubAppManifestURLs.indexOf(subAppManifestURL);
-    if (index == -1) {
-      if (DEBUG) debug("subscriber is not found: " + subAppManifestURL);
-      return;
-    }
-
-    if (DEBUG) debug("Cancelling the connection.");
-    allowedSubAppManifestURLs.splice(index, 1);
-
-    // Clean up the parent entries if needed.
-    if (allowedSubAppManifestURLs.length == 0) {
-      delete allowedPubAppManifestURLs[pubAppManifestURL];
-      if (Object.keys(allowedPubAppManifestURLs).length == 0) {
-        delete this._allowedConnections[keyword];
-      }
-    }
-
-    if (DEBUG) debug("Unregistering message ports based on this connection.");
-    let messagePortIDs = [];
-    for (let messagePortID in this._messagePortPairs) {
-      let pair = this._messagePortPairs[messagePortID];
-      if (pair.keyword == keyword &&
-          pair.publisher.manifestURL == pubAppManifestURL &&
-          pair.subscriber.manifestURL == subAppManifestURL) {
-        messagePortIDs.push(messagePortID);
-      }
-    }
-    messagePortIDs.forEach(function(aMessagePortID) {
-      delete this._messagePortPairs[aMessagePortID];
-    }, this);
-  },
-
-  _identifyMessagePort: function(aMessagePortID, aManifestURL) {
-    let pair = this._messagePortPairs[aMessagePortID];
-    if (!pair) {
-      if (DEBUG) {
-        debug("Error! The message port ID is invalid: " + aMessagePortID +
-              ", which should have been generated by parent.");
-      }
-      return null;
-    }
-
-    // Check it the message port is for publisher.
-    if (pair.publisher.manifestURL == aManifestURL) {
-      return { pair: pair, isPublisher: true };
-    }
-
-    // Check it the message port is for subscriber.
-    if (pair.subscriber.manifestURL == aManifestURL) {
-      return { pair: pair, isPublisher: false };
-    }
-
-    if (DEBUG) {
-      debug("Error! The manifest URL is invalid: " + aManifestURL +
-            ", which might be a hacked app.");
-    }
-    return null;
-  },
-
-  _registerMessagePort: function(aMessage, aTarget) {
-    let messagePortID = aMessage.messagePortID;
-    let manifestURL = aMessage.manifestURL;
-    let pageURL = aMessage.pageURL;
-
-    let identity = this._identifyMessagePort(messagePortID, manifestURL);
-    if (!identity) {
-      if (DEBUG) {
-        debug("Cannot identify the message port. Failed to register.");
-      }
-      return;
-    }
-
-    if (DEBUG) debug("Registering message port for " + manifestURL);
-    let pair = identity.pair;
-    let isPublisher = identity.isPublisher;
-
-    let sender = isPublisher ? pair.publisher : pair.subscriber;
-    sender.target = aTarget;
-    sender.pageURL = pageURL;
-    sender.messageQueue = [];
-
-    // Check if the other port has queued messages. Deliver them if needed.
-    if (DEBUG) {
-      debug("Checking if the other port used to send messages but queued.");
-    }
-    let receiver = isPublisher ? pair.subscriber : pair.publisher;
-    if (receiver.messageQueue) {
-      while (receiver.messageQueue.length) {
-        let message = receiver.messageQueue.shift();
-        if (DEBUG) debug("Delivering message: " + JSON.stringify(message));
-        sender.target.sendAsyncMessage("InterAppMessagePort:OnMessage",
-                                       { message: message,
-                                         manifestURL: sender.manifestURL,
-                                         pageURL: sender.pageURL,
-                                         messagePortID: messagePortID });
-      }
-    }
-  },
-
-  _unregisterMessagePort: function(aMessage) {
-    let messagePortID = aMessage.messagePortID;
-    let manifestURL = aMessage.manifestURL;
-
-    let identity = this._identifyMessagePort(messagePortID, manifestURL);
-    if (!identity) {
-      if (DEBUG) {
-        debug("Cannot identify the message port. Failed to unregister.");
-      }
-      return;
-    }
-
-    if (DEBUG) {
-      debug("Unregistering message port for " + manifestURL);
-    }
-    delete this._messagePortPairs[messagePortID];
-  },
-
-  _removeTarget: function(aTarget) {
-    if (!aTarget) {
-      if (DEBUG) debug("Error! aTarget cannot be null/undefined in any way.");
-      return
-    }
-
-    if (DEBUG) debug("Unregistering message ports based on this target.");
-    let messagePortIDs = [];
-    for (let messagePortID in this._messagePortPairs) {
-      let pair = this._messagePortPairs[messagePortID];
-      if (pair.publisher.target === aTarget ||
-          pair.subscriber.target === aTarget) {
-        messagePortIDs.push(messagePortID);
-      }
-    }
-    messagePortIDs.forEach(function(aMessagePortID) {
-      delete this._messagePortPairs[aMessagePortID];
-    }, this);
-  },
-
-  _postMessage: function(aMessage) {
-    let messagePortID = aMessage.messagePortID;
-    let manifestURL = aMessage.manifestURL;
-    let message = aMessage.message;
-
-    let identity = this._identifyMessagePort(messagePortID, manifestURL);
-    if (!identity) {
-      if (DEBUG) debug("Cannot identify the message port. Failed to post.");
-      return;
-    }
-
-    let pair = identity.pair;
-    let isPublisher = identity.isPublisher;
-
-    let receiver = isPublisher ? pair.subscriber : pair.publisher;
-    if (!receiver.target) {
-      if (DEBUG) {
-        debug("The receiver's target is not ready yet. Queuing the message.");
-      }
-      let sender = isPublisher ? pair.publisher : pair.subscriber;
-      sender.messageQueue.push(message);
-      return;
-    }
-
-    if (DEBUG) debug("Delivering message: " + JSON.stringify(message));
-    receiver.target.sendAsyncMessage("InterAppMessagePort:OnMessage",
-                                     { manifestURL: receiver.manifestURL,
-                                       pageURL: receiver.pageURL,
-                                       messagePortID: messagePortID,
-                                       message: message });
-  },
-
-  _handleSelectcedApps: function(aData) {
-    let callerID = aData.callerID;
-    let caller = this._promptUICallers[callerID];
-    if (!caller) {
-      if (DEBUG) debug("Error! Cannot find the caller.");
-      return;
-    }
-
-    delete this._promptUICallers[callerID];
-
-    let outerWindowID = caller.outerWindowID;
-    let requestID = caller.requestID;
-    let target = caller.target;
-
-    let manifestURL = aData.manifestURL;
-    let keyword = aData.keyword;
-    let selectedApps = aData.selectedApps;
-
-    if (selectedApps.length == 0) {
-      if (DEBUG) debug("No apps are selected to connect.")
-      this._dispatchMessagePorts(keyword, manifestURL, [],
-                                 target, outerWindowID, requestID);
-      return;
-    }
-
-    // Find the entry of allowed connections to add the selected apps.
-    let allowedPubAppManifestURLs = this._allowedConnections[keyword];
-    if (!allowedPubAppManifestURLs) {
-      allowedPubAppManifestURLs = this._allowedConnections[keyword] = {};
-    }
-    let allowedSubAppManifestURLs = allowedPubAppManifestURLs[manifestURL];
-    if (!allowedSubAppManifestURLs) {
-      allowedSubAppManifestURLs = allowedPubAppManifestURLs[manifestURL] = [];
-    }
-
-    // Add the selected app into the existing set of allowed connections.
-    selectedApps.forEach(function(aSelectedApp) {
-      let allowedSubAppManifestURL = aSelectedApp.manifestURL;
-      if (allowedSubAppManifestURLs.indexOf(allowedSubAppManifestURL) == -1) {
-        allowedSubAppManifestURLs.push(allowedSubAppManifestURL);
-      }
-    });
-
-    // Finally, dispatch the message ports for the allowed connections,
-    // including the old connections and the newly selected connection.
-    this._dispatchMessagePorts(keyword, manifestURL, allowedSubAppManifestURLs,
-                               target, outerWindowID, requestID);
-  },
-
-  receiveMessage: function(aMessage) {
-    if (DEBUG) debug("receiveMessage: name: " + aMessage.name);
-    let message = aMessage.json;
-    let target = aMessage.target;
-
-    // To prevent the hacked child process from sending commands to parent
-    // to do illegal connections, we need to check its manifest URL.
-    if (aMessage.name !== "child-process-shutdown" &&
-        // TODO: fix bug 988142 to re-enable "InterAppMessagePort:Unregister".
-        aMessage.name !== "InterAppMessagePort:Unregister" &&
-        kMessages.indexOf(aMessage.name) != -1) {
-      if (!target.assertContainApp(message.manifestURL)) {
-        if (DEBUG) {
-          debug("Got message from a process carrying illegal manifest URL.");
-        }
-        return null;
-      }
-    }
-
-    switch (aMessage.name) {
-      case "Webapps:Connect":
-        this._connect(message, target);
-        break;
-      case "Webapps:GetConnections":
-        this._getConnections(message, target);
-        break;
-      case "InterAppConnection:Cancel":
-        this._cancelConnection(message);
-        break;
-      case "InterAppMessagePort:PostMessage":
-        this._postMessage(message);
-        break;
-      case "InterAppMessagePort:Register":
-        this._registerMessagePort(message, target);
-        break;
-      case "InterAppMessagePort:Unregister":
-        this._unregisterMessagePort(message);
-        break;
-      case "child-process-shutdown":
-        this._removeTarget(target);
-        break;
-    }
-  },
-
-  observe: function(aSubject, aTopic, aData) {
-    switch (aTopic) {
-      case "xpcom-shutdown":
-        Services.obs.removeObserver(this, "xpcom-shutdown");
-        Services.obs.removeObserver(this, "inter-app-comm-select-app-result");
-        kMessages.forEach(function(aMsg) {
-          ppmm.removeMessageListener(aMsg, this);
-        }, this);
-        ppmm = null;
-        break;
-      case "inter-app-comm-select-app-result":
-        if (DEBUG) debug("inter-app-comm-select-app-result: " + aData);
-        this._handleSelectcedApps(JSON.parse(aData));
-        break;
-    }
+    InterAppCommService.
+      registerConnection(aKeyword, aHandlerPageURI, aManifestURI,
+                         aDescription, aRules);
   },
 
   classID: Components.ID("{3dd15ce6-e7be-11e2-82bc-77967e7a63e6}"),
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIInterAppCommService,
-                                         Ci.nsIObserver])
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIInterAppCommService])
 }
 
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([InterAppCommService]);
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([InterAppCommServiceProxy]);
copy from dom/apps/src/InterAppCommService.js
copy to dom/apps/src/InterAppCommService.jsm
--- a/dom/apps/src/InterAppCommService.js
+++ b/dom/apps/src/InterAppCommService.jsm
@@ -1,16 +1,18 @@
 /* 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 { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
+this.EXPORTED_SYMBOLS = ["InterAppCommService"];
+
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/AppsUtils.jsm");
 
 const DEBUG = false;
 function debug(aMsg) {
   dump("-- InterAppCommService: " + Date.now() + ": " + aMsg + "\n");
 }
@@ -34,187 +36,205 @@ XPCOMUtils.defineLazyServiceGetter(this,
 const kMessages =["Webapps:Connect",
                   "Webapps:GetConnections",
                   "InterAppConnection:Cancel",
                   "InterAppMessagePort:PostMessage",
                   "InterAppMessagePort:Register",
                   "InterAppMessagePort:Unregister",
                   "child-process-shutdown"];
 
-function InterAppCommService() {
-  Services.obs.addObserver(this, "xpcom-shutdown", false);
-  Services.obs.addObserver(this, "inter-app-comm-select-app-result", false);
+/**
+ * This module contains helpers for Inter-App Communication API [1] related
+ * purposes, which plays the role of the central service receiving messages
+ * from and interacting with the content processes.
+ *
+ * [1] https://wiki.mozilla.org/WebAPI/Inter_App_Communication_Alt_proposal
+ */
 
-  kMessages.forEach(function(aMsg) {
-    ppmm.addMessageListener(aMsg, this);
-  }, this);
+this.InterAppCommService = {
+  init: function() {
+    Services.obs.addObserver(this, "xpcom-shutdown", false);
+    Services.obs.addObserver(this, "inter-app-comm-select-app-result", false);
+
+    kMessages.forEach(function(aMsg) {
+      ppmm.addMessageListener(aMsg, this);
+    }, this);
 
-  // This matrix is used for saving the inter-app connection info registered in
-  // the app manifest. The object literal is defined as below:
-  //
-  // {
-  //   "keyword1": {
-  //     "subAppManifestURL1": {
-  //       /* subscribed info */
-  //     },
-  //     "subAppManifestURL2": {
-  //       /* subscribed info */
-  //     },
-  //     ...
-  //   },
-  //   "keyword2": {
-  //     "subAppManifestURL3": {
-  //       /* subscribed info */
-  //     },
-  //     ...
-  //   },
-  //   ...
-  // }
-  //
-  // For example:
-  //
-  // {
-  //   "foo": {
-  //     "app://subApp1.gaiamobile.org/manifest.webapp": {
-  //       pageURL: "app://subApp1.gaiamobile.org/handler.html",
-  //       description: "blah blah",
-  //       rules: { ... }
-  //     },
-  //     "app://subApp2.gaiamobile.org/manifest.webapp": {
-  //       pageURL: "app://subApp2.gaiamobile.org/handler.html",
-  //       description: "blah blah",
-  //       rules: { ... }
-  //     }
-  //   },
-  //   "bar": {
-  //     "app://subApp3.gaiamobile.org/manifest.webapp": {
-  //       pageURL: "app://subApp3.gaiamobile.org/handler.html",
-  //       description: "blah blah",
-  //       rules: { ... }
-  //     }
-  //   }
-  // }
-  //
-  // TODO Bug 908999 - Update registered connections when app gets uninstalled.
-  this._registeredConnections = {};
+    // This matrix is used for saving the inter-app connection info registered in
+    // the app manifest. The object literal is defined as below:
+    //
+    // {
+    //   "keyword1": {
+    //     "subAppManifestURL1": {
+    //       /* subscribed info */
+    //     },
+    //     "subAppManifestURL2": {
+    //       /* subscribed info */
+    //     },
+    //     ...
+    //   },
+    //   "keyword2": {
+    //     "subAppManifestURL3": {
+    //       /* subscribed info */
+    //     },
+    //     ...
+    //   },
+    //   ...
+    // }
+    //
+    // For example:
+    //
+    // {
+    //   "foo": {
+    //     "app://subApp1.gaiamobile.org/manifest.webapp": {
+    //       pageURL: "app://subApp1.gaiamobile.org/handler.html",
+    //       description: "blah blah",
+    //       rules: { ... }
+    //     },
+    //     "app://subApp2.gaiamobile.org/manifest.webapp": {
+    //       pageURL: "app://subApp2.gaiamobile.org/handler.html",
+    //       description: "blah blah",
+    //       rules: { ... }
+    //     }
+    //   },
+    //   "bar": {
+    //     "app://subApp3.gaiamobile.org/manifest.webapp": {
+    //       pageURL: "app://subApp3.gaiamobile.org/handler.html",
+    //       description: "blah blah",
+    //       rules: { ... }
+    //     }
+    //   }
+    // }
+    //
+    // TODO Bug 908999 - Update registered connections when app gets uninstalled.
+    this._registeredConnections = {};
 
-  // This matrix is used for saving the permitted connections, which allows
-  // the messaging between publishers and subscribers. The object literal is
-  // defined as below:
-  //
-  // {
-  //   "keyword1": {
-  //     "pubAppManifestURL1": [
-  //       "subAppManifestURL1",
-  //       "subAppManifestURL2",
-  //       ...
-  //     ],
-  //     "pubAppManifestURL2": [
-  //       "subAppManifestURL3",
-  //       "subAppManifestURL4",
-  //       ...
-  //     ],
-  //     ...
-  //   },
-  //   "keyword2": {
-  //     "pubAppManifestURL3": [
-  //       "subAppManifestURL5",
-  //       ...
-  //     ],
-  //     ...
-  //   },
-  //   ...
-  // }
-  //
-  // For example:
-  //
-  // {
-  //   "foo": {
-  //     "app://pubApp1.gaiamobile.org/manifest.webapp": [
-  //       "app://subApp1.gaiamobile.org/manifest.webapp",
-  //       "app://subApp2.gaiamobile.org/manifest.webapp"
-  //     ],
-  //     "app://pubApp2.gaiamobile.org/manifest.webapp": [
-  //       "app://subApp3.gaiamobile.org/manifest.webapp",
-  //       "app://subApp4.gaiamobile.org/manifest.webapp"
-  //     ]
-  //   },
-  //   "bar": {
-  //     "app://pubApp3.gaiamobile.org/manifest.webapp": [
-  //       "app://subApp5.gaiamobile.org/manifest.webapp",
-  //     ]
-  //   }
-  // }
-  //
-  // TODO Bug 908999 - Update allowed connections when app gets uninstalled.
-  this._allowedConnections = {};
+    // This matrix is used for saving the permitted connections, which allows
+    // the messaging between publishers and subscribers. The object literal is
+    // defined as below:
+    //
+    // {
+    //   "keyword1": {
+    //     "pubAppManifestURL1": [
+    //       "subAppManifestURL1",
+    //       "subAppManifestURL2",
+    //       ...
+    //     ],
+    //     "pubAppManifestURL2": [
+    //       "subAppManifestURL3",
+    //       "subAppManifestURL4",
+    //       ...
+    //     ],
+    //     ...
+    //   },
+    //   "keyword2": {
+    //     "pubAppManifestURL3": [
+    //       "subAppManifestURL5",
+    //       ...
+    //     ],
+    //     ...
+    //   },
+    //   ...
+    // }
+    //
+    // For example:
+    //
+    // {
+    //   "foo": {
+    //     "app://pubApp1.gaiamobile.org/manifest.webapp": [
+    //       "app://subApp1.gaiamobile.org/manifest.webapp",
+    //       "app://subApp2.gaiamobile.org/manifest.webapp"
+    //     ],
+    //     "app://pubApp2.gaiamobile.org/manifest.webapp": [
+    //       "app://subApp3.gaiamobile.org/manifest.webapp",
+    //       "app://subApp4.gaiamobile.org/manifest.webapp"
+    //     ]
+    //   },
+    //   "bar": {
+    //     "app://pubApp3.gaiamobile.org/manifest.webapp": [
+    //       "app://subApp5.gaiamobile.org/manifest.webapp",
+    //     ]
+    //   }
+    // }
+    //
+    // TODO Bug 908999 - Update allowed connections when app gets uninstalled.
+    this._allowedConnections = {};
 
-  // This matrix is used for saving the caller info from the content process,
-  // which is indexed by a random UUID, to know where to return the promise
-  // resolvser's callback when the prompt UI for allowing connections returns.
-  // An example of the object literal is shown as below:
-  //
-  // {
-  //   "fooID": {
-  //     outerWindowID: 12,
-  //     requestID: 34,
-  //     target: pubAppTarget1
-  //   },
-  //   "barID": {
-  //     outerWindowID: 56,
-  //     requestID: 78,
-  //     target: pubAppTarget2
-  //   }
-  // }
-  //
-  // where |outerWindowID| is the ID of the window requesting the connection,
-  //       |requestID| is the ID specifying the promise resolver to return,
-  //       |target| is the target of the process requesting the connection.
-  this._promptUICallers = {};
+    // This matrix is used for saving the caller info from the content process,
+    // which is indexed by a random UUID, to know where to return the promise
+    // resolvser's callback when the prompt UI for allowing connections returns.
+    // An example of the object literal is shown as below:
+    //
+    // {
+    //   "fooID": {
+    //     outerWindowID: 12,
+    //     requestID: 34,
+    //     target: pubAppTarget1
+    //   },
+    //   "barID": {
+    //     outerWindowID: 56,
+    //     requestID: 78,
+    //     target: pubAppTarget2
+    //   }
+    // }
+    //
+    // where |outerWindowID| is the ID of the window requesting the connection,
+    //       |requestID| is the ID specifying the promise resolver to return,
+    //       |target| is the target of the process requesting the connection.
+    this._promptUICallers = {};
 
-  // This matrix is used for saving the pair of message ports, which is indexed
-  // by a random UUID, so that each port can know whom it should talk to.
-  // An example of the object literal is shown as below:
-  //
-  // {
-  //   "UUID1": {
-  //     keyword: "keyword1",
-  //     publisher: {
-  //       manifestURL: "app://pubApp1.gaiamobile.org/manifest.webapp",
-  //       target: pubAppTarget1,
-  //       pageURL: "app://pubApp1.gaiamobile.org/caller.html",
-  //       messageQueue: [...]
-  //     },
-  //     subscriber: {
-  //       manifestURL: "app://subApp1.gaiamobile.org/manifest.webapp",
-  //       target: subAppTarget1,
-  //       pageURL: "app://pubApp1.gaiamobile.org/handler.html",
-  //       messageQueue: [...]
-  //     }
-  //   },
-  //   "UUID2": {
-  //     keyword: "keyword2",
-  //     publisher: {
-  //       manifestURL: "app://pubApp2.gaiamobile.org/manifest.webapp",
-  //       target: pubAppTarget2,
-  //       pageURL: "app://pubApp2.gaiamobile.org/caller.html",
-  //       messageQueue: [...]
-  //     },
-  //     subscriber: {
-  //       manifestURL: "app://subApp2.gaiamobile.org/manifest.webapp",
-  //       target: subAppTarget2,
-  //       pageURL: "app://pubApp2.gaiamobile.org/handler.html",
-  //       messageQueue: [...]
-  //     }
-  //   }
-  // }
-  this._messagePortPairs = {};
-}
+    // This matrix is used for saving the pair of message ports, which is indexed
+    // by a random UUID, so that each port can know whom it should talk to.
+    // An example of the object literal is shown as below:
+    //
+    // {
+    //   "UUID1": {
+    //     keyword: "keyword1",
+    //     publisher: {
+    //       manifestURL: "app://pubApp1.gaiamobile.org/manifest.webapp",
+    //       target: pubAppTarget1,
+    //       pageURL: "app://pubApp1.gaiamobile.org/caller.html",
+    //       messageQueue: [...]
+    //     },
+    //     subscriber: {
+    //       manifestURL: "app://subApp1.gaiamobile.org/manifest.webapp",
+    //       target: subAppTarget1,
+    //       pageURL: "app://pubApp1.gaiamobile.org/handler.html",
+    //       messageQueue: [...]
+    //     }
+    //   },
+    //   "UUID2": {
+    //     keyword: "keyword2",
+    //     publisher: {
+    //       manifestURL: "app://pubApp2.gaiamobile.org/manifest.webapp",
+    //       target: pubAppTarget2,
+    //       pageURL: "app://pubApp2.gaiamobile.org/caller.html",
+    //       messageQueue: [...]
+    //     },
+    //     subscriber: {
+    //       manifestURL: "app://subApp2.gaiamobile.org/manifest.webapp",
+    //       target: subAppTarget2,
+    //       pageURL: "app://pubApp2.gaiamobile.org/handler.html",
+    //       messageQueue: [...]
+    //     }
+    //   }
+    // }
+    this._messagePortPairs = {};
+  },
 
-InterAppCommService.prototype = {
+  /**
+   * Registration of a page that wants to be connected to other apps through
+   * the Inter-App Communication API.
+   *
+   * @param aKeyword        The connection's keyword.
+   * @param aHandlerPageURI The URI of the handler's page.
+   * @param aManifestURI    The webapp's manifest URI.
+   * @param aDescription    The connection's description.
+   * @param aRules          The connection's rules.
+   */
   registerConnection: function(aKeyword, aHandlerPageURI, aManifestURI,
                                aDescription, aRules) {
     let manifestURL = aManifestURI.spec;
     let pageURL = aHandlerPageURI.spec;
 
     if (DEBUG) {
       debug("registerConnection: aKeyword: " + aKeyword +
             " manifestURL: " + manifestURL + " pageURL: " + pageURL +
@@ -858,17 +878,12 @@ InterAppCommService.prototype = {
         }, this);
         ppmm = null;
         break;
       case "inter-app-comm-select-app-result":
         if (DEBUG) debug("inter-app-comm-select-app-result: " + aData);
         this._handleSelectcedApps(JSON.parse(aData));
         break;
     }
-  },
-
-  classID: Components.ID("{3dd15ce6-e7be-11e2-82bc-77967e7a63e6}"),
+  }
+};
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIInterAppCommService,
-                                         Ci.nsIObserver])
-}
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([InterAppCommService]);
+InterAppCommService.init();
--- a/dom/apps/src/moz.build
+++ b/dom/apps/src/moz.build
@@ -22,16 +22,17 @@ EXTRA_COMPONENTS += [
     'Webapps.js',
     'Webapps.manifest',
 ]
 
 EXTRA_JS_MODULES += [
     'AppDownloadManager.jsm',
     'AppsServiceChild.jsm',
     'FreeSpaceWatcher.jsm',
+    'InterAppCommService.jsm',
     'OfflineCacheInstaller.jsm',
     'PermissionsInstaller.jsm',
     'PermissionsTable.jsm',
     'StoreTrustAnchor.jsm',
 ]
 
 EXTRA_PP_JS_MODULES += [
     'AppsUtils.jsm',
new file mode 100644
--- /dev/null
+++ b/dom/apps/tests/unit/test_inter_app_comm_service.js
@@ -0,0 +1,457 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+
+Cu.import("resource://gre/modules/InterAppCommService.jsm");
+
+let UUIDGenerator = Cc["@mozilla.org/uuid-generator;1"]
+                      .getService(Ci.nsIUUIDGenerator);
+
+const MESSAGE_PORT_ID = UUIDGenerator.generateUUID().toString();
+const FAKE_MESSAGE_PORT_ID = UUIDGenerator.generateUUID().toString();
+const OUTER_WINDOW_ID = UUIDGenerator.generateUUID().toString();
+const REQUEST_ID = UUIDGenerator.generateUUID().toString();
+
+const PUB_APP_MANIFEST_URL = "app://pubApp.gaiamobile.org/manifest.webapp";
+const SUB_APP_MANIFEST_URL = "app://subApp.gaiamobile.org/manifest.webapp";
+
+const PUB_APP_PAGE_URL = "app://pubApp.gaiamobile.org/handler.html";
+const SUB_APP_PAGE_URL = "app://subApp.gaiamobile.org/handler.html";
+
+const KEYWORD = "test";
+
+function create_message_port_pair(aMessagePortId,
+                                  aKeyword,
+                                  aPubManifestURL,
+                                  aSubManifestURL) {
+  InterAppCommService._messagePortPairs[aMessagePortId] = {
+    keyword: aKeyword,
+    publisher: {
+      manifestURL: aPubManifestURL
+    },
+    subscriber: {
+      manifestURL: aSubManifestURL
+    }
+  };
+}
+
+function clear_message_port_pairs() {
+  InterAppCommService._messagePortPairs = {};
+}
+
+function register_message_port(aMessagePortId,
+                               aManifestURL,
+                               aPageURL,
+                               aTargetSendAsyncMessage) {
+  let message = {
+    name: "InterAppMessagePort:Register",
+    json: {
+      messagePortID: aMessagePortId,
+      manifestURL: aManifestURL,
+      pageURL: aPageURL
+    },
+    target: {
+      sendAsyncMessage: function(aName, aData) {
+        if (aTargetSendAsyncMessage) {
+          aTargetSendAsyncMessage(aName, aData);
+        }
+      },
+      assertContainApp: function(_manifestURL) {
+        return (aManifestURL == _manifestURL);
+      }
+    }
+  };
+
+  InterAppCommService.receiveMessage(message);
+
+  return message.target;
+}
+
+function register_message_ports(aMessagePortId,
+                                aPubTargetSendAsyncMessage,
+                                aSubTargetSendAsyncMessage) {
+  let pubTarget = register_message_port(aMessagePortId,
+                                        PUB_APP_MANIFEST_URL,
+                                        PUB_APP_PAGE_URL,
+                                        aPubTargetSendAsyncMessage);
+
+  let subTarget = register_message_port(aMessagePortId,
+                                        SUB_APP_MANIFEST_URL,
+                                        SUB_APP_PAGE_URL,
+                                        aSubTargetSendAsyncMessage);
+
+  return { pubTarget: pubTarget, subTarget: subTarget };
+}
+
+function unregister_message_port(aMessagePortId,
+                                 aManifestURL) {
+  let message = {
+    name: "InterAppMessagePort:Unregister",
+    json: {
+      messagePortID: aMessagePortId,
+      manifestURL: aManifestURL
+    },
+    target: {
+      assertContainApp: function(_manifestURL) {
+        return (aManifestURL == _manifestURL);
+      }
+    }
+  };
+
+  InterAppCommService.receiveMessage(message);
+}
+
+function remove_target(aTarget) {
+  let message = {
+    name: "child-process-shutdown",
+    target: aTarget
+  };
+
+  InterAppCommService.receiveMessage(message);
+}
+
+function post_message(aMessagePortId,
+                      aManifestURL,
+                      aMessage) {
+  let message = {
+    name: "InterAppMessagePort:PostMessage",
+    json: {
+      messagePortID: aMessagePortId,
+      manifestURL: aManifestURL,
+      message: aMessage
+    },
+    target: {
+      assertContainApp: function(_manifestURL) {
+        return (aManifestURL == _manifestURL);
+      }
+    }
+  };
+
+  InterAppCommService.receiveMessage(message);
+}
+
+function create_allowed_connections(aKeyword,
+                                    aPubManifestURL,
+                                    aSubManifestURL) {
+  let allowedPubAppManifestURLs =
+    InterAppCommService._allowedConnections[aKeyword] = {};
+
+  allowedPubAppManifestURLs[aPubManifestURL] = [aSubManifestURL];
+}
+
+function clear_allowed_connections() {
+  InterAppCommService._allowedConnections = {};
+}
+
+function get_connections(aManifestURL,
+                         aOuterWindowID,
+                         aRequestID,
+                         aTargetSendAsyncMessage) {
+  let message = {
+    name: "Webapps:GetConnections",
+    json: {
+      manifestURL: aManifestURL,
+      outerWindowID: aOuterWindowID,
+      requestID: aRequestID
+    },
+    target: {
+      sendAsyncMessage: function(aName, aData) {
+        if (aTargetSendAsyncMessage) {
+          aTargetSendAsyncMessage(aName, aData);
+        }
+      },
+      assertContainApp: function(_manifestURL) {
+        return (aManifestURL == _manifestURL);
+      }
+    }
+  };
+
+  InterAppCommService.receiveMessage(message);
+}
+
+function cancel_connections(aManifestURL,
+                            aKeyword,
+                            aPubManifestURL,
+                            aSubManifestURL) {
+  let message = {
+    name: "InterAppConnection:Cancel",
+    json: {
+      manifestURL: aManifestURL,
+      keyword: aKeyword,
+      pubAppManifestURL: aPubManifestURL,
+      subAppManifestURL: aSubManifestURL
+    },
+    target: {
+      assertContainApp: function(_manifestURL) {
+        return (aManifestURL == _manifestURL);
+      }
+    }
+  };
+
+  InterAppCommService.receiveMessage(message);
+}
+
+add_test(function test_registerMessagePort() {
+  create_message_port_pair(MESSAGE_PORT_ID,
+                           KEYWORD,
+                           PUB_APP_MANIFEST_URL,
+                           SUB_APP_MANIFEST_URL);
+
+  let targets = register_message_ports(MESSAGE_PORT_ID);
+
+  let messagePortPair = InterAppCommService._messagePortPairs[MESSAGE_PORT_ID];
+
+  do_check_eq(PUB_APP_PAGE_URL, messagePortPair.publisher.pageURL);
+  do_check_eq(SUB_APP_PAGE_URL, messagePortPair.subscriber.pageURL);
+
+  do_check_true(targets.pubTarget === messagePortPair.publisher.target);
+  do_check_true(targets.subTarget === messagePortPair.subscriber.target);
+
+  clear_message_port_pairs();
+  run_next_test();
+});
+
+add_test(function test_failToRegisterMessagePort() {
+  create_message_port_pair(MESSAGE_PORT_ID,
+                           KEYWORD,
+                           PUB_APP_MANIFEST_URL,
+                           SUB_APP_MANIFEST_URL);
+
+  let targets = register_message_ports(FAKE_MESSAGE_PORT_ID);
+
+  let messagePortPair = InterAppCommService._messagePortPairs[MESSAGE_PORT_ID];
+
+  // Because it failed to register, the page URLs and targets don't exist.
+  do_check_true(messagePortPair.publisher.pageURL === undefined);
+  do_check_true(messagePortPair.subscriber.pageURL === undefined);
+
+  do_check_true(messagePortPair.publisher.target === undefined);
+  do_check_true(messagePortPair.subscriber.target === undefined);
+
+  clear_message_port_pairs();
+  run_next_test();
+});
+
+add_test(function test_unregisterMessagePort() {
+  create_message_port_pair(MESSAGE_PORT_ID,
+                           KEYWORD,
+                           PUB_APP_MANIFEST_URL,
+                           SUB_APP_MANIFEST_URL);
+
+  register_message_ports(MESSAGE_PORT_ID);
+
+  unregister_message_port(MESSAGE_PORT_ID, PUB_APP_MANIFEST_URL);
+
+  do_check_true(InterAppCommService._messagePortPairs[MESSAGE_PORT_ID]
+                === undefined);
+
+  clear_message_port_pairs();
+  run_next_test();
+});
+
+add_test(function test_failToUnregisterMessagePort() {
+  create_message_port_pair(MESSAGE_PORT_ID,
+                           KEYWORD,
+                           PUB_APP_MANIFEST_URL,
+                           SUB_APP_MANIFEST_URL);
+
+  register_message_ports(MESSAGE_PORT_ID);
+
+  unregister_message_port(FAKE_MESSAGE_PORT_ID, PUB_APP_MANIFEST_URL);
+
+  // Because it failed to unregister, the entry still exists.
+  do_check_true(InterAppCommService._messagePortPairs[MESSAGE_PORT_ID]
+                !== undefined);
+
+  clear_message_port_pairs();
+  run_next_test();
+});
+
+add_test(function test_removeTarget() {
+  create_message_port_pair(MESSAGE_PORT_ID,
+                           KEYWORD,
+                           PUB_APP_MANIFEST_URL,
+                           SUB_APP_MANIFEST_URL);
+
+  let targets = register_message_ports(MESSAGE_PORT_ID);
+
+  remove_target(targets.pubTarget);
+
+  do_check_true(InterAppCommService._messagePortPairs[MESSAGE_PORT_ID]
+                === undefined);
+
+  clear_message_port_pairs();
+  run_next_test();
+});
+
+add_test(function test_postMessage() {
+  create_message_port_pair(MESSAGE_PORT_ID,
+                           KEYWORD,
+                           PUB_APP_MANIFEST_URL,
+                           SUB_APP_MANIFEST_URL);
+
+  let countPubAppOnMessage = 0;
+  function pubAppOnMessage(aName, aData) {
+    countPubAppOnMessage++;
+
+    do_check_eq(aName, "InterAppMessagePort:OnMessage");
+    do_check_eq(aData.manifestURL, PUB_APP_MANIFEST_URL);
+    do_check_eq(aData.pageURL, PUB_APP_PAGE_URL);
+    do_check_eq(aData.messagePortID, MESSAGE_PORT_ID);
+
+    if (countPubAppOnMessage == 1) {
+      do_check_eq(aData.message.text, "sub app says world");
+
+      post_message(MESSAGE_PORT_ID,
+                   PUB_APP_MANIFEST_URL,
+                   { text: "pub app says hello again" });
+
+    } else if (countPubAppOnMessage == 2) {
+      do_check_eq(aData.message.text, "sub app says world again");
+
+      clear_message_port_pairs();
+      run_next_test();
+    } else {
+      do_throw("pub app receives an unexpected message")
+    }
+  };
+
+  let countSubAppOnMessage = 0;
+  function subAppOnMessage(aName, aData) {
+    countSubAppOnMessage++;
+
+    do_check_eq(aName, "InterAppMessagePort:OnMessage");
+    do_check_eq(aData.manifestURL, SUB_APP_MANIFEST_URL);
+    do_check_eq(aData.pageURL, SUB_APP_PAGE_URL);
+    do_check_eq(aData.messagePortID, MESSAGE_PORT_ID);
+
+    if (countSubAppOnMessage == 1) {
+      do_check_eq(aData.message.text, "pub app says hello");
+
+      post_message(MESSAGE_PORT_ID,
+                   SUB_APP_MANIFEST_URL,
+                   { text: "sub app says world" });
+
+    } else if (countSubAppOnMessage == 2) {
+      do_check_eq(aData.message.text, "pub app says hello again");
+
+      post_message(MESSAGE_PORT_ID,
+                   SUB_APP_MANIFEST_URL,
+                   { text: "sub app says world again" });
+    } else {
+      do_throw("sub app receives an unexpected message")
+    }
+  };
+
+  register_message_ports(MESSAGE_PORT_ID, pubAppOnMessage, subAppOnMessage);
+
+  post_message(MESSAGE_PORT_ID,
+               PUB_APP_MANIFEST_URL,
+               { text: "pub app says hello" });
+});
+
+add_test(function test_registerMessagePort_with_queued_messages() {
+  create_message_port_pair(MESSAGE_PORT_ID,
+                           KEYWORD,
+                           PUB_APP_MANIFEST_URL,
+                           SUB_APP_MANIFEST_URL);
+
+  register_message_port(MESSAGE_PORT_ID,
+                        PUB_APP_MANIFEST_URL,
+                        PUB_APP_PAGE_URL);
+
+  post_message(MESSAGE_PORT_ID,
+               PUB_APP_MANIFEST_URL,
+               { text: "pub app says hello" });
+
+  post_message(MESSAGE_PORT_ID,
+               PUB_APP_MANIFEST_URL,
+               { text: "pub app says hello again" });
+
+  let countSubAppOnMessage = 0;
+  function subAppOnMessage(aName, aData) {
+    countSubAppOnMessage++;
+
+    do_check_eq(aName, "InterAppMessagePort:OnMessage");
+    do_check_eq(aData.manifestURL, SUB_APP_MANIFEST_URL);
+    do_check_eq(aData.pageURL, SUB_APP_PAGE_URL);
+    do_check_eq(aData.messagePortID, MESSAGE_PORT_ID);
+
+    if (countSubAppOnMessage == 1) {
+      do_check_eq(aData.message.text, "pub app says hello");
+    } else if (countSubAppOnMessage == 2) {
+      do_check_eq(aData.message.text, "pub app says hello again");
+
+      clear_message_port_pairs();
+      run_next_test();
+    } else {
+      do_throw("sub app receives an unexpected message")
+    }
+  };
+
+  register_message_port(MESSAGE_PORT_ID,
+                        SUB_APP_MANIFEST_URL,
+                        SUB_APP_PAGE_URL,
+                        subAppOnMessage);
+});
+
+add_test(function test_getConnections() {
+  create_allowed_connections(KEYWORD,
+                             PUB_APP_MANIFEST_URL,
+                             SUB_APP_MANIFEST_URL);
+
+  function onGetConnections(aName, aData) {
+    do_check_eq(aName, "Webapps:GetConnections:Return:OK");
+    do_check_eq(aData.oid, OUTER_WINDOW_ID);
+    do_check_eq(aData.requestID, REQUEST_ID);
+
+    let connections = aData.connections;
+    do_check_eq(connections.length, 1);
+    do_check_eq(connections[0].keyword, KEYWORD);
+    do_check_eq(connections[0].pubAppManifestURL, PUB_APP_MANIFEST_URL);
+    do_check_eq(connections[0].subAppManifestURL, SUB_APP_MANIFEST_URL);
+
+    clear_allowed_connections();
+    run_next_test();
+  };
+
+  get_connections(PUB_APP_MANIFEST_URL,
+                  OUTER_WINDOW_ID,
+                  REQUEST_ID,
+                  onGetConnections);
+});
+
+add_test(function test_cancelConnection() {
+  create_allowed_connections(KEYWORD,
+                             PUB_APP_MANIFEST_URL,
+                             SUB_APP_MANIFEST_URL);
+
+  create_message_port_pair(MESSAGE_PORT_ID,
+                           KEYWORD,
+                           PUB_APP_MANIFEST_URL,
+                           SUB_APP_MANIFEST_URL);
+
+  register_message_ports(MESSAGE_PORT_ID);
+
+  cancel_connections(PUB_APP_MANIFEST_URL,
+                     KEYWORD,
+                     PUB_APP_MANIFEST_URL,
+                     SUB_APP_MANIFEST_URL);
+
+  do_check_true(InterAppCommService._allowedConnections[KEYWORD]
+                === undefined);
+
+  do_check_true(InterAppCommService._messagePortPairs[MESSAGE_PORT_ID]
+                === undefined);
+
+  clear_allowed_connections();
+  clear_message_port_pairs();
+  run_next_test();
+});
+
+function run_test() {
+  do_get_profile();
+
+  run_next_test();
+}
--- a/dom/apps/tests/unit/xpcshell.ini
+++ b/dom/apps/tests/unit/xpcshell.ini
@@ -1,5 +1,6 @@
 [DEFAULT]
 head =
 tail =
 
+[test_inter_app_comm_service.js]
 [test_manifestSanitizer.js]
--- a/layout/tools/reftest/b2g_desktop.py
+++ b/layout/tools/reftest/b2g_desktop.py
@@ -59,17 +59,18 @@ class B2GDesktopReftest(RefTest):
                 if mozinfo.info['debug']:
                     options.timeout = 420
                 else:
                     options.timeout = 300
             self.timeout = options.timeout + 30.0
 
         log.info("%s | Running tests: start.", os.path.basename(__file__))
         cmd, args = self.build_command_line(options.app,
-                            ignore_window_size=options.ignoreWindowSize)
+                            ignore_window_size=options.ignoreWindowSize,
+                            browser_arg=options.browser_arg)
         self.runner = FirefoxRunner(profile=self.profile,
                                     binary=cmd,
                                     cmdargs=args,
                                     env=env,
                                     process_class=ProcessHandler,
                                     symbols_path=options.symbolsPath,
                                     kp_kwargs=kp_kwargs)
 
@@ -118,20 +119,24 @@ class B2GDesktopReftest(RefTest):
         # Set a future policy version to avoid the telemetry prompt.
         prefs["toolkit.telemetry.prompted"] = 999
         prefs["toolkit.telemetry.notifiedOptOut"] = 999
 
         # Set the extra prefs.
         profile.set_preferences(prefs)
         return profile
 
-    def build_command_line(self, app, ignore_window_size=False):
+    def build_command_line(self, app, ignore_window_size=False,
+                           browser_arg=None):
         cmd = os.path.abspath(app)
         args = ['-marionette']
 
+        if browser_arg:
+            args += [browser_arg]
+
         if not ignore_window_size:
             args.extend(['--screen', '800x1000'])
         return cmd, args
 
     def _on_output(self, line):
         print(line)
         # TODO use structured logging
         if "TEST-START" in line and "|" in line:
--- a/layout/tools/reftest/runreftestb2g.py
+++ b/layout/tools/reftest/runreftestb2g.py
@@ -26,16 +26,21 @@ class B2GOptions(ReftestOptions):
 
     def __init__(self, automation=None, **kwargs):
         defaults = {}
         if not automation:
             automation = B2GRemoteAutomation(None, "fennec", context_chrome=True)
 
         ReftestOptions.__init__(self, automation)
 
+        self.add_option("--browser-arg", action="store",
+                    type = "string", dest = "browser_arg",
+                    help = "Optional command-line arg to pass to the browser")
+        defaults["browser_arg"] = None
+
         self.add_option("--b2gpath", action="store",
                     type = "string", dest = "b2gPath",
                     help = "path to B2G repo or qemu dir")
         defaults["b2gPath"] = None
 
         self.add_option("--marionette", action="store",
                     type = "string", dest = "marionette",
                     help = "host:port to use when connecting to Marionette")
--- a/testing/config/mozharness/b2g_desktop_config.py
+++ b/testing/config/mozharness/b2g_desktop_config.py
@@ -3,17 +3,18 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 config = {
     "mochitest_options": [
         "--console-level=INFO", "%(test_manifest)s",
         "--total-chunks=%(total_chunks)s", "--this-chunk=%(this_chunk)s",
         "--profile=%(gaia_profile)s", "--app=%(application)s", "--desktop",
         "--utility-path=%(utility_path)s", "--certificate-path=%(cert_path)s",
-        "--symbols-path=%(symbols_path)s",
+        "--symbols-path=%(symbols_path)s", "--browser-arg=%(browser_arg)s",
         "--quiet"
     ],
 
     "reftest_options": [
         "--desktop", "--profile=%(gaia_profile)s", "--appname=%(application)s",
-        "--symbols-path=%(symbols_path)s", "%(test_manifest)s",
+        "--browser-arg=%(browser_arg)s", "--symbols-path=%(symbols_path)s",
+        "%(test_manifest)s"
     ]
 }