Merge m-c to mozilla-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 02 May 2014 14:55:29 +0200
changeset 181757 484e51c7322abcdd0d9a159ca5fc37e51e2cb832
parent 181731 cc840267a394359e88f08824f01a53742f14e5bc (current diff)
parent 181756 35c124cab3a89562af2e173ef17ed981625fa907 (diff)
child 181758 93b29053da8e964a4593d1bc6105bd13f2583c85
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
milestone32.0a1
Merge m-c to mozilla-inbound
--- 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="386b5478eb9c3970972966123517e993e8a1092a"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e3fc29c5d4a9196b0e32e246f3d0150ae0c39221"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f313e6d3aaaefe8c82eaed15912a09b120fb7260"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="ca283b9db2b151d465cfd2e19346cf58fe89e413"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="d5800c36b2d5822fc3fe1899b9280401de466e1e"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a054c7385854b0e71b8d3071a465b9bc21581ee0"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
@@ -97,12 +97,12 @@
   <project name="platform/system/netd" path="system/netd" revision="3d298fde142bee3fc4f07f63f16f2d8ce42339c0"/>
   <project name="platform/system/vold" path="system/vold" revision="919829940468066a32f403980b43f6ebfee5d314"/>
   <!-- Emulator specific things -->
   <project name="android-development" path="development" remote="b2g" revision="9abf0ab68376afae3e1c7beefa3e9cbee2fde202"/>
   <project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="2fee3bbbfc236b883ef8507e27d88b17b203fe25"/>
   <project name="platform/external/iproute2" path="external/iproute2" revision="c66c5716d5335e450f7a7b71ccc6a604fb2f41d2"/>
   <project 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="d2685281e2e54ca14d1df304867aa82c37b27162"/>
   <project 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="627f9b20fc518937b93747a7ff1ed4f5ed46e06f"/>
-  <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="d7755abaf703f86228e3938a3e204121b254638d"/>
+  <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="10977f28df27f79534f079c0a9852b21a5a0f6da"/>
   <project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="431afac2ebfdd9c1c8402b413ff5914ed448e961"/>
   <project name="android-sdk" path="sdk" remote="b2g" revision="4f46930827957afbce500a4a920755a218bf3155"/>
 </manifest>
--- 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="386b5478eb9c3970972966123517e993e8a1092a"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e3fc29c5d4a9196b0e32e246f3d0150ae0c39221"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f313e6d3aaaefe8c82eaed15912a09b120fb7260"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a054c7385854b0e71b8d3071a465b9bc21581ee0"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="65fba428f8d76336b33ddd9e15900357953600ba">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="386b5478eb9c3970972966123517e993e8a1092a"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e3fc29c5d4a9196b0e32e246f3d0150ae0c39221"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f313e6d3aaaefe8c82eaed15912a09b120fb7260"/>
   <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="a054c7385854b0e71b8d3071a465b9bc21581ee0"/>
   <!-- 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="386b5478eb9c3970972966123517e993e8a1092a"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e3fc29c5d4a9196b0e32e246f3d0150ae0c39221"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f313e6d3aaaefe8c82eaed15912a09b120fb7260"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="ca283b9db2b151d465cfd2e19346cf58fe89e413"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="d5800c36b2d5822fc3fe1899b9280401de466e1e"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a054c7385854b0e71b8d3071a465b9bc21581ee0"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
@@ -97,12 +97,12 @@
   <project name="platform/system/netd" path="system/netd" revision="3d298fde142bee3fc4f07f63f16f2d8ce42339c0"/>
   <project name="platform/system/vold" path="system/vold" revision="919829940468066a32f403980b43f6ebfee5d314"/>
   <!-- Emulator specific things -->
   <project name="android-development" path="development" remote="b2g" revision="9abf0ab68376afae3e1c7beefa3e9cbee2fde202"/>
   <project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="2fee3bbbfc236b883ef8507e27d88b17b203fe25"/>
   <project name="platform/external/iproute2" path="external/iproute2" revision="c66c5716d5335e450f7a7b71ccc6a604fb2f41d2"/>
   <project 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="d2685281e2e54ca14d1df304867aa82c37b27162"/>
   <project 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="627f9b20fc518937b93747a7ff1ed4f5ed46e06f"/>
-  <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="d7755abaf703f86228e3938a3e204121b254638d"/>
+  <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="10977f28df27f79534f079c0a9852b21a5a0f6da"/>
   <project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="431afac2ebfdd9c1c8402b413ff5914ed448e961"/>
   <project name="android-sdk" path="sdk" remote="b2g" revision="4f46930827957afbce500a4a920755a218bf3155"/>
 </manifest>
--- 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="386b5478eb9c3970972966123517e993e8a1092a"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e3fc29c5d4a9196b0e32e246f3d0150ae0c39221"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f313e6d3aaaefe8c82eaed15912a09b120fb7260"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a054c7385854b0e71b8d3071a465b9bc21581ee0"/>
   <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": "689e41080c6c21f0d2b51363304776c70967c9c5", 
+    "revision": "2796d30073cc12313afc0deef9d7700d25acdc77", 
     "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="386b5478eb9c3970972966123517e993e8a1092a"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e3fc29c5d4a9196b0e32e246f3d0150ae0c39221"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f313e6d3aaaefe8c82eaed15912a09b120fb7260"/>
   <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="a054c7385854b0e71b8d3071a465b9bc21581ee0"/>
   <!-- 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="386b5478eb9c3970972966123517e993e8a1092a"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e3fc29c5d4a9196b0e32e246f3d0150ae0c39221"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f313e6d3aaaefe8c82eaed15912a09b120fb7260"/>
   <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="386b5478eb9c3970972966123517e993e8a1092a"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e3fc29c5d4a9196b0e32e246f3d0150ae0c39221"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f313e6d3aaaefe8c82eaed15912a09b120fb7260"/>
   <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="a054c7385854b0e71b8d3071a465b9bc21581ee0"/>
   <!-- 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="386b5478eb9c3970972966123517e993e8a1092a"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e3fc29c5d4a9196b0e32e246f3d0150ae0c39221"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f313e6d3aaaefe8c82eaed15912a09b120fb7260"/>
   <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="a054c7385854b0e71b8d3071a465b9bc21581ee0"/>
   <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="386b5478eb9c3970972966123517e993e8a1092a"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e3fc29c5d4a9196b0e32e246f3d0150ae0c39221"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f313e6d3aaaefe8c82eaed15912a09b120fb7260"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a054c7385854b0e71b8d3071a465b9bc21581ee0"/>
   <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="386b5478eb9c3970972966123517e993e8a1092a"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e3fc29c5d4a9196b0e32e246f3d0150ae0c39221"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f313e6d3aaaefe8c82eaed15912a09b120fb7260"/>
   <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="a054c7385854b0e71b8d3071a465b9bc21581ee0"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/browser/base/content/aboutDialog.js
+++ b/browser/base/content/aboutDialog.js
@@ -47,21 +47,22 @@ function init(aEvent)
     document.getElementById("version").textContent += " (" + buildDate + ")";
     document.getElementById("experimental").hidden = false;
     document.getElementById("communityDesc").hidden = true;
   }
 
 #ifdef MOZ_UPDATER
   gAppUpdater = new appUpdater();
 
-#if MOZ_UPDATE_CHANNEL != release
   let defaults = Services.prefs.getDefaultBranch("");
   let channelLabel = document.getElementById("currentChannel");
+  let currentChannelText = document.getElementById("currentChannelText");
   channelLabel.value = defaults.getCharPref("app.update.channel");
-#endif
+  if (channelLabel.value == "release")
+      currentChannelText.hidden = true;
 #endif
 
 #ifdef XP_MACOSX
   // it may not be sized at this point, and we need its width to calculate its position
   window.sizeToContent();
   window.moveTo((screen.availWidth / 2) - (window.outerWidth / 2), screen.availHeight / 5);
 #endif
 }
--- a/browser/base/content/aboutDialog.xul
+++ b/browser/base/content/aboutDialog.xul
@@ -103,23 +103,21 @@
               </hbox>
               <hbox id="unsupportedSystem" align="center">
                 <label>&update.unsupported.start;</label><label id="unsupportedLink" class="text-link">&update.unsupported.linkText;</label><label>&update.unsupported.end;</label>
               </hbox>
             </deck>
 #endif
           </vbox>
 
-#if MOZ_UPDATE_CHANNEL != release
 #ifdef MOZ_UPDATER
           <description class="text-blurb" id="currentChannelText">
             &channel.description.start;<label id="currentChannel"/>&channel.description.end;
           </description>
 #endif
-#endif
           <vbox id="experimental" hidden="true">
             <description class="text-blurb" id="warningDesc">
               &warningDesc.version;
 #ifdef MOZ_TELEMETRY_ON_BY_DEFAULT
               &warningDesc.telemetryDesc;
 #endif
             </description>
             <description class="text-blurb" id="communityExperimentalDesc">
--- a/dom/nfc/tests/marionette/test_nfc_peer.js
+++ b/dom/nfc/tests/marionette/test_nfc_peer.js
@@ -1,15 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 MARIONETTE_TIMEOUT = 30000;
 MARIONETTE_HEAD_JS = 'head.js';
 
 let MANIFEST_URL = "app://system.gaiamobile.org/manifest.webapp";
+let INCORRECT_MANIFEST_URL = "app://xyz.gaiamobile.org/manifest.webapp";
 
 function peerReadyCb(evt) {
   log("peerReadyCb called");
   let peer = nfc.getNFCPeer(evt.detail);
   ok(peer instanceof MozNFCPeer, "Should get a NFCPeer object.");
 
   // reset callback and NFC Hardware.
   nfc.onpeerready = null;
@@ -20,25 +21,49 @@ function handleTechnologyDiscoveredRE0(m
   log("Received \'nfc-manager-tech-discovered\'");
   is(msg.type, "techDiscovered", "check for correct message type");
   is(msg.techList[0], "P2P", "check for correct tech type");
 
   nfc.onpeerready = peerReadyCb;
 
   let request = nfc.checkP2PRegistration(MANIFEST_URL);
   request.onsuccess = function (evt) {
+    is(request.result, true, "check for P2P registration result");
     nfc.notifyUserAcceptedP2P(MANIFEST_URL);
   }
 
   request.onerror = function () {
     ok(false, "checkP2PRegistration failed.");
     toggleNFC(false, runNextTest);
   }
 }
 
+function handleTechnologyDiscoveredRE0ForP2PRegFailure(msg) {
+  log("Received \'nfc-manager-tech-discovered\'");
+  is(msg.type, "techDiscovered", "check for correct message type");
+  is(msg.techList[0], "P2P", "check for correct tech type");
+
+  nfc.onpeerready = peerReadyCb;
+
+  let request = nfc.checkP2PRegistration(INCORRECT_MANIFEST_URL);
+  request.onsuccess = function (evt) {
+    is(request.result, false, "check for P2P registration result");
+
+    nfc.onpeerready = null;
+    toggleNFC(false, runNextTest);
+  }
+
+  request.onerror = function () {
+    ok(false, "checkP2PRegistration failed.");
+
+    nfc.onpeerready = null;
+    toggleNFC(false, runNextTest);
+  }
+}
+
 function activateRE(re) {
   let deferred = Promise.defer();
   let cmd = "nfc ntf rf_intf_activated " + re;
 
   emulator.run(cmd, function(result) {
     is(result.pop(), "OK", "check activation of RE" + re);
     deferred.resolve();
   });
@@ -50,15 +75,25 @@ function testPeerReady() {
   window.navigator.mozSetMessageHandler(
     "nfc-manager-tech-discovered", handleTechnologyDiscoveredRE0);
 
   toggleNFC(true, function() {
     activateRE(0);
   });
 }
 
+function testCheckP2PRegFailure() {
+  window.navigator.mozSetMessageHandler(
+    "nfc-manager-tech-discovered", handleTechnologyDiscoveredRE0ForP2PRegFailure);
+
+  toggleNFC(true, function() {
+    activateRE(0);
+  });
+}
+
 let tests = [
-  testPeerReady
+  testPeerReady,
+  testCheckP2PRegFailure
 ];
 
 SpecialPowers.pushPermissions(
   [{"type": "nfc-manager", "allow": true, context: document},
    {"type": "nfc-write", "allow": true, context: document}], runTests);
--- a/dom/system/gonk/Nfc.js
+++ b/dom/system/gonk/Nfc.js
@@ -302,16 +302,34 @@ XPCOMUtils.defineLazyGetter(this, "gMess
 
     isValidPeerEvent: function isValidPeerEvent(event) {
       // Valid values : 0x01, 0x02 Or 0x03
       return ((event === NFC.NFC_PEER_EVENT_READY) ||
               (event === NFC.NFC_PEER_EVENT_LOST)  ||
               (event === (NFC.NFC_PEER_EVENT_READY | NFC.NFC_PEER_EVENT_LOST)));
     },
 
+    checkP2PRegistration: function checkP2PRegistration(msg) {
+      // Check if the session and application id yeild a valid registered
+      // target.  It should have registered for NFC_PEER_EVENT_READY
+      let isValid = !!this.nfc.sessionTokenMap[this.nfc._currentSessionId] &&
+                    this.isRegisteredP2PTarget(msg.json.appId,
+                                               NFC.NFC_PEER_EVENT_READY);
+      // Remember the current AppId if registered.
+      this.currentPeerAppId = (isValid) ? msg.json.appId : null;
+      let status = (isValid) ? NFC.GECKO_NFC_ERROR_SUCCESS :
+                               NFC.GECKO_NFC_ERROR_GENERIC_FAILURE;
+
+      // Notify the content process immediately of the status
+      msg.target.sendAsyncMessage(msg.name + "Response", {
+        status: status,
+        requestId: msg.json.requestId
+      });
+    },
+
     /**
      * nsIMessageListener interface methods.
      */
 
     receiveMessage: function receiveMessage(msg) {
       debug("Received '" + msg.name + "' message from content process");
       if (msg.name == "child-process-shutdown") {
         // By the time we receive child-process-shutdown, the child process has
@@ -355,29 +373,17 @@ XPCOMUtils.defineLazyGetter(this, "gMess
           return null;
         case "NFC:RegisterPeerTarget":
           this.registerPeerTarget(msg);
           return null;
         case "NFC:UnregisterPeerTarget":
           this.unregisterPeerTarget(msg);
           return null;
         case "NFC:CheckP2PRegistration":
-          // Check if the application id is a valid registered target.
-          // (It should have registered for NFC_PEER_EVENT_READY).
-          let isRegistered = this.isRegisteredP2PTarget(msg.json.appId,
-                                                        NFC.NFC_PEER_EVENT_READY);
-          // Remember the current AppId if registered.
-          this.currentPeerAppId = (isRegistered) ? msg.json.appId : null;
-          let status = (isRegistered) ? NFC.GECKO_NFC_ERROR_SUCCESS :
-                                        NFC.GECKO_NFC_ERROR_GENERIC_FAILURE;
-          // Notify the content process immediately of the status
-          msg.target.sendAsyncMessage(msg.name + "Response", {
-            status: status,
-            requestId: msg.json.requestId
-          });
+          this.checkP2PRegistration(msg);
           return null;
         case "NFC:NotifyUserAcceptedP2P":
           // Notify the 'NFC_PEER_EVENT_READY' since user has acknowledged
           this.notifyPeerEvent(msg.json.appId, NFC.NFC_PEER_EVENT_READY);
           return null;
         case "NFC:NotifySendFileStatus":
           // Upon receiving the status of sendFile operation, send the response
           // to appropriate content process.
--- a/dom/system/gonk/NfcContentHelper.js
+++ b/dom/system/gonk/NfcContentHelper.js
@@ -402,21 +402,23 @@ NfcContentHelper.prototype = {
 
     switch (message.name) {
       case "NFC:ReadNDEFResponse":
         this.handleReadNDEFResponse(result);
         break;
       case "NFC:GetDetailsNDEFResponse":
         this.handleGetDetailsNDEFResponse(result);
         break;
+      case "NFC:CheckP2PRegistrationResponse":
+        this.handleCheckP2PRegistrationResponse(result);
+        break;
       case "NFC:ConnectResponse": // Fall through.
       case "NFC:CloseResponse":
       case "NFC:WriteNDEFResponse":
       case "NFC:MakeReadOnlyNDEFResponse":
-      case "NFC:CheckP2PRegistrationResponse":
       case "NFC:NotifySendFileStatusResponse":
       case "NFC:ConfigResponse":
         if (result.status !== NFC.GECKO_NFC_ERROR_SUCCESS) {
           this.fireRequestError(atob(result.requestId), result.status);
         } else {
           this.fireRequestSuccess(atob(result.requestId), result);
         }
         break;
@@ -463,13 +465,20 @@ NfcContentHelper.prototype = {
       this.fireRequestError(atob(result.requestId), result.status);
       return;
     }
 
     let requestId = atob(result.requestId);
     let result = new GetDetailsNDEFResponse(result);
     this.fireRequestSuccess(requestId, result);
   },
+
+  handleCheckP2PRegistrationResponse: function handleCheckP2PRegistrationResponse(result) {
+    // Privilaged status API. Always fire success to avoid using exposed props.
+    // The receiver must check the boolean mapped status code to handle.
+    let requestId = atob(result.requestId);
+    this.fireRequestSuccess(requestId, result.status == NFC.GECKO_NFC_ERROR_SUCCESS);
+  },
 };
 
 if (NFC_ENABLED) {
   this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NfcContentHelper]);
 }
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -13,16 +13,18 @@ import java.util.Vector;
 
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.mozilla.gecko.AndroidGamepadManager;
 import org.mozilla.gecko.DynamicToolbar.PinReason;
 import org.mozilla.gecko.DynamicToolbar.VisibilityTransition;
 import org.mozilla.gecko.GeckoProfileDirectories.NoMozillaDirectoryException;
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.animation.PropertyAnimator;
 import org.mozilla.gecko.animation.ViewHelper;
 import org.mozilla.gecko.db.BrowserContract.Combined;
 import org.mozilla.gecko.db.BrowserContract.ReadingListItems;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.favicons.Favicons;
 import org.mozilla.gecko.favicons.LoadFaviconTask;
 import org.mozilla.gecko.favicons.OnFaviconLoadedListener;
@@ -480,16 +482,18 @@ abstract public class BrowserApp extends
         mActionBar = (ActionModeCompatView) findViewById(R.id.actionbar);
 
         mBrowserToolbar = (BrowserToolbar) findViewById(R.id.browser_toolbar);
         mProgressView = (ToolbarProgressView) findViewById(R.id.progress);
         mBrowserToolbar.setProgressBar(mProgressView);
         if (Intent.ACTION_VIEW.equals(intent.getAction())) {
             // Show the target URL immediately in the toolbar.
             mBrowserToolbar.setTitle(intent.getDataString());
+
+            Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT);
         }
 
         ((GeckoApp.MainLayout) mMainLayout).setTouchEventInterceptor(new HideTabsTouchListener());
         ((GeckoApp.MainLayout) mMainLayout).setMotionEventInterceptor(new MotionEventInterceptor() {
             @Override
             public boolean onInterceptMotionEvent(View view, MotionEvent event) {
                 // If we get a gamepad panning MotionEvent while the focus is not on the layerview,
                 // put the focus on the layerview and carry on
@@ -638,16 +642,22 @@ abstract public class BrowserApp extends
         });
 
         mBrowserToolbar.setOnFocusChangeListener(new View.OnFocusChangeListener() {
             @Override
             public void onFocusChange(View v, boolean hasFocus) {
                 if (isHomePagerVisible()) {
                     mHomePager.onToolbarFocusChange(hasFocus);
                 }
+
+                if (hasFocus) {
+                    Telemetry.startUISession(TelemetryContract.Session.URLBAR_FOCUSED);
+                } else {
+                    Telemetry.stopUISession(TelemetryContract.Session.URLBAR_FOCUSED);
+                }
             }
         });
 
         mBrowserToolbar.setOnStartEditingListener(new BrowserToolbar.OnStartEditingListener() {
             public void onStartEditing() {
                 // Temporarily disable doorhanger notifications.
                 mDoorHangerPopup.disable();
             }
@@ -746,16 +756,17 @@ abstract public class BrowserApp extends
 
     @Override
     public boolean onContextItemSelected(MenuItem item) {
         final int itemId = item.getItemId();
         if (itemId == R.id.pasteandgo) {
             String text = Clipboard.getText();
             if (!TextUtils.isEmpty(text)) {
                 Tabs.getInstance().loadUrl(text);
+                Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.CONTEXT_MENU);
             }
             return true;
         }
 
         if (itemId == R.id.site_settings) {
             GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Permissions:Get", null));
             return true;
         }
@@ -937,16 +948,19 @@ abstract public class BrowserApp extends
         }
 
         if (AboutPages.isAboutReader(url)) {
             url = ReaderModeUtils.getUrlFromAboutReader(url);
         }
 
         GeckoAppShell.openUriExternal(url, "text/plain", "", "",
                                       Intent.ACTION_SEND, tab.getDisplayTitle());
+
+        // Context: Sharing via chrome list (no explicit session is active)
+        Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.LIST);
     }
 
     @Override
     protected void loadStartupTab(String url) {
         // We aren't showing about:home, so cancel the telemetry timer
         if (url != null || mShouldRestore) {
             mAboutHomeStartupTimer.cancel();
         }
@@ -1608,16 +1622,18 @@ abstract public class BrowserApp extends
         // Don't do anything if the user entered an empty URL.
         if (TextUtils.isEmpty(url)) {
             return;
         }
 
         // If the URL doesn't look like a search query, just load it.
         if (!StringUtils.isSearchQuery(url, true)) {
             Tabs.getInstance().loadUrl(url, Tabs.LOADURL_USER_ENTERED);
+
+            Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL);
             return;
         }
 
         // Otherwise, check for a bookmark keyword.
         ThreadUtils.postToBackgroundThread(new Runnable() {
             @Override
             public void run() {
                 final String keyword;
@@ -1633,24 +1649,26 @@ abstract public class BrowserApp extends
                 }
 
                 final String keywordUrl = BrowserDB.getUrlForKeyword(getContentResolver(), keyword);
 
                 // If there isn't a bookmark keyword, load the url. This may result in a query
                 // using the default search engine.
                 if (TextUtils.isEmpty(keywordUrl)) {
                     Tabs.getInstance().loadUrl(url, Tabs.LOADURL_USER_ENTERED);
+                    Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL);
                     return;
                 }
 
                 recordSearch(null, "barkeyword");
 
                 // Otherwise, construct a search query from the bookmark keyword.
                 final String searchUrl = keywordUrl.replace("%s", URLEncoder.encode(keywordSearch));
                 Tabs.getInstance().loadUrl(searchUrl, Tabs.LOADURL_USER_ENTERED);
+                Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, "", "keyword");
             }
         });
     }
 
     /**
      * Record in Health Report that a search has occurred.
      *
      * @param engine
@@ -2533,19 +2551,21 @@ abstract public class BrowserApp extends
             String uri = intent.getDataString();
             GeckoAppShell.sendEventToGecko(GeckoEvent.createURILoadEvent(uri));
         }
 
         if (!mInitialized) {
             return;
         }
 
-        // Dismiss editing mode if the user is loading a URL from an external app.
         if (Intent.ACTION_VIEW.equals(action)) {
+            // Dismiss editing mode if the user is loading a URL from an external app.
             mBrowserToolbar.cancelEdit();
+
+            Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT);
             return;
         }
 
         // Only solicit feedback when the app has been launched from the icon shortcut.
         if (!Intent.ACTION_MAIN.equals(action)) {
             return;
         }
 
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -602,16 +602,19 @@ public abstract class GeckoApp
                 final String url = message.getString("url");
                 final String origin = message.getString("origin");
                 final String title = message.getString("title");
                 final String type = message.getString("shortcutType");
                 GeckoAppShell.removeShortcut(title, url, origin, type);
             } else if (event.equals("Share:Text")) {
                 String text = message.getString("text");
                 GeckoAppShell.openUriExternal(text, "text/plain", "", "", Intent.ACTION_SEND, "");
+
+                // Context: Sharing via chrome list (no explicit session is active)
+                Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.LIST);
             } else if (event.equals("Image:SetAs")) {
                 String src = message.getString("url");
                 setImageAs(src);
             } else if (event.equals("Sanitize:ClearHistory")) {
                 handleClearHistory();
             } else if (event.equals("Update:Check")) {
                 startService(new Intent(UpdateServiceHelper.ACTION_CHECK_FOR_UPDATE, null, this, UpdateService.class));
             } else if (event.equals("Update:Download")) {
--- a/mobile/android/base/TelemetryContract.java
+++ b/mobile/android/base/TelemetryContract.java
@@ -25,40 +25,85 @@ public interface TelemetryContract {
         public static final String TOP_SITES_UNPIN = "unpin.1";
 
         // Top site edited.
         public static final String TOP_SITES_EDIT = "edit.1";
 
         // Set default panel.
         public static final String PANEL_SET_DEFAULT = "setdefault.1";
 
+        // Sharing content.
+        public static final String SHARE = "share.1";
+
         // Sanitizing private data.
         public static final String SANITIZE = "sanitize.1";
+
+        // Saving a resource (reader, bookmark, etc) for viewing later.
+        // Note: Only used in JavaScript for now, but here for completeness.
+        public static final String SAVE = "save.1";
+
+        // Stop holding a resource (reader, bookmark, etc) for viewing later.
+        // Note: Only used in JavaScript for now, but here for completeness.
+        public static final String UNSAVE = "unsave.1";
+
+        // Loading a URL.
+        public static final String LOAD_URL = "loadurl.1";
     }
 
     /**
      * Holds event methods. Intended for use in
      * Telemetry.sendUIEvent() as the "method" parameter.
      */
     public interface Method {
+        // Action triggered from a list.
+        public static final String LIST = "list";
+
+        // Action triggered from a button.
+        public static final String BUTTON = "button";
+
         // Action triggered from a dialog.
         public static final String DIALOG = "dialog";
+
+        // Action occurred via an intent.
+        public static final String INTENT = "intent";
+
+        // Action occurred via a context menu.
+        public static final String CONTEXT_MENU = "contextmenu";
+
+        // Action triggered from a view grid item, like a thumbnail.
+        public static final String GRID_ITEM = "griditem";
+
+        // Action triggered from a view list item, like a row of a list.
+        public static final String LIST_ITEM = "listitem";
+
+        // Action triggered from a suggestion provided to the user.
+        public static final String SUGGESTION = "suggestion";
     }
 
     /**
      * Holds session names. Intended for use with
      * Telemetry.startUISession() as the "sessionName" parameter.
      */
     public interface Session {
         // Started when a user enters about:home.
         public static final String HOME = "home.1";
 
         // Started when a user enters a given home panel.
         // Session name is dynamic, encoded as "homepanel.1:<panel_id>"
         public static final String HOME_PANEL = "homepanel.1:";
+
+        // Started when a Reader viewer becomes active in the foreground.
+        // Note: Only used in JavaScript for now, but here for completeness.
+        public static final String READER = "reader.1";
+
+        // URL bar focused.
+        public static final String URLBAR_FOCUSED = "urlbar.1:";
+
+        // Awesomescreen frecency search is active.
+        public static final String FRECENCY = "frecency.1:";
     }
 
     /**
      * Holds reasons for stopping a session. Intended for use in
      * Telemetry.stopUISession() as the "reason" parameter.
      */
     public interface Reason {}
 }
--- a/mobile/android/base/home/BookmarksListView.java
+++ b/mobile/android/base/home/BookmarksListView.java
@@ -3,16 +3,18 @@
  * 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/. */
 
 package org.mozilla.gecko.home;
 
 import java.util.EnumSet;
 
 import org.mozilla.gecko.R;
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.db.BrowserContract.Bookmarks;
 import org.mozilla.gecko.db.BrowserDB.URLColumns;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
 
 import android.content.Context;
 import android.database.Cursor;
 import android.util.AttributeSet;
 import android.view.KeyEvent;
@@ -86,16 +88,18 @@ public class BookmarksListView extends H
             // If we're clicking on a folder, update adapter to move to that folder
             final int folderId = cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks._ID));
             final String folderTitle = adapter.getFolderTitle(parent.getContext(), cursor);
             adapter.moveToChildFolder(folderId, folderTitle);
         } else {
             // Otherwise, just open the URL
             final String url = cursor.getString(cursor.getColumnIndexOrThrow(URLColumns.URL));
 
+            Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL);
+
             // This item is a TwoLinePageRow, so we allow switch-to-tab.
             getOnUrlOpenListener().onUrlOpen(url, EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));
         }
     }
 
     @Override
     public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
         // Adjust the item position to account for the parent folder row that is inserted
--- a/mobile/android/base/home/BrowserSearch.java
+++ b/mobile/android/base/home/BrowserSearch.java
@@ -6,16 +6,18 @@
 package org.mozilla.gecko.home;
 
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoEvent;
 import org.mozilla.gecko.PrefsHelper;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.Tab;
 import org.mozilla.gecko.Tabs;
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.db.BrowserDB.URLColumns;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
 import org.mozilla.gecko.home.SearchEngine;
 import org.mozilla.gecko.home.SearchLoader.SearchCursorLoader;
 import org.mozilla.gecko.mozglue.RobocopTarget;
 import org.mozilla.gecko.toolbar.AutocompleteHandler;
 import org.mozilla.gecko.util.GeckoEventListener;
 import org.mozilla.gecko.util.StringUtils;
@@ -216,16 +218,30 @@ public class BrowserSearch extends HomeF
     @Override
     public void onStop() {
         super.onStop();
 
         getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
     }
 
     @Override
+    public void onResume() {
+        super.onResume();
+
+        Telemetry.startUISession(TelemetryContract.Session.FRECENCY);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+
+        Telemetry.stopUISession(TelemetryContract.Session.FRECENCY);
+    }
+
+    @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
         // All list views are styled to look the same with a global activity theme.
         // If the style of the list changes, inflate it from an XML.
         mView = (LinearLayout) inflater.inflate(R.layout.browser_search, container, false);
         mList = (HomeListView) mView.findViewById(R.id.home_list_view);
 
         return mView;
     }
@@ -259,16 +275,21 @@ public class BrowserSearch extends HomeF
                     return;
                 }
 
                 // Account for the search engine rows.
                 position -= getSuggestEngineCount();
                 final Cursor c = mAdapter.getCursor(position);
                 final String url = c.getString(c.getColumnIndexOrThrow(URLColumns.URL));
 
+                // The "urlbar" and "frecency" sessions can be open at the same time. Use the LIST_ITEM
+                // method to set this LOAD_URL event apart from the case where the user commits what's in
+                // the url bar.
+                Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.LIST_ITEM);
+
                 // This item is a TwoLinePageRow, so we allow switch-to-tab.
                 mUrlOpenListener.onUrlOpen(url, EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));
             }
         });
 
         mList.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
             @Override
             public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
--- a/mobile/android/base/home/HomeFragment.java
+++ b/mobile/android/base/home/HomeFragment.java
@@ -7,16 +7,18 @@ package org.mozilla.gecko.home;
 
 import org.mozilla.gecko.EditBookmarkDialog;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoEvent;
 import org.mozilla.gecko.GeckoProfile;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.ReaderModeUtils;
 import org.mozilla.gecko.Tabs;
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.db.BrowserContract.Combined;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.favicons.Favicons;
 import org.mozilla.gecko.util.ThreadUtils;
 import org.mozilla.gecko.util.UiAsyncTask;
 
 import android.content.ContentResolver;
 import android.content.Context;
@@ -127,16 +129,19 @@ abstract class HomeFragment extends Frag
         final int itemId = item.getItemId();
         if (itemId == R.id.home_share) {
             if (info.url == null) {
                 Log.e(LOGTAG, "Can't share because URL is null");
                 return false;
             } else {
                 GeckoAppShell.openUriExternal(info.url, SHARE_MIME_TYPE, "", "",
                                               Intent.ACTION_SEND, info.getDisplayTitle());
+
+                // Context: Sharing via chrome homepage contextmenu list (home session should be active)
+                Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.LIST);
                 return true;
             }
         }
 
         if (itemId == R.id.home_add_to_launcher) {
             if (info.url == null) {
                 Log.e(LOGTAG, "Can't add to home screen because URL is null");
                 return false;
@@ -152,16 +157,18 @@ abstract class HomeFragment extends Frag
                 Log.e(LOGTAG, "Can't open in new tab because URL is null");
                 return false;
             }
 
             int flags = Tabs.LOADURL_NEW_TAB | Tabs.LOADURL_BACKGROUND;
             if (item.getItemId() == R.id.home_open_private_tab)
                 flags |= Tabs.LOADURL_PRIVATE;
 
+            Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.CONTEXT_MENU);
+
             final String url = (info.isInReadingList() ? ReaderModeUtils.getAboutReaderForUrl(info.url) : info.url);
 
             // Some pinned site items have "user-entered" urls. URLs entered in the PinSiteDialog are wrapped in
             // a special URI until we can get a valid URL. If the url is a user-entered url, decode the URL before loading it.
             Tabs.getInstance().loadUrl(decodeUserEnteredUrl(url), flags);
             Toast.makeText(context, R.string.new_tab_opened, Toast.LENGTH_SHORT).show();
             return true;
         }
--- a/mobile/android/base/home/LastTabsPanel.java
+++ b/mobile/android/base/home/LastTabsPanel.java
@@ -4,16 +4,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.home;
 
 import org.mozilla.gecko.AboutPages;
 import org.mozilla.gecko.GeckoProfile;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.SessionParser;
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.db.BrowserContract.Combined;
 import org.mozilla.gecko.home.HomePager.OnNewTabsListener;
 
 import android.app.Activity;
 import android.content.Context;
 import android.database.Cursor;
 import android.database.MatrixCursor;
 import android.database.MatrixCursor.RowBuilder;
@@ -105,16 +107,18 @@ public class LastTabsPanel extends HomeF
         mList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
             @Override
             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                 final Cursor c = mAdapter.getCursor();
                 if (c == null || !c.moveToPosition(position)) {
                     return;
                 }
 
+                Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL);
+
                 final String url = c.getString(c.getColumnIndexOrThrow(Combined.URL));
                 mNewTabsListener.onNewTabs(new String[] { url });
             }
         });
 
         mList.setContextMenuInfoFactory(new HomeContextMenuInfo.Factory() {
             @Override
             public HomeContextMenuInfo makeInfoForCursor(View view, int position, long id, Cursor cursor) {
@@ -201,16 +205,18 @@ public class LastTabsPanel extends HomeF
         }
 
         final String[] urls = new String[c.getCount()];
 
         do {
             urls[c.getPosition()] = c.getString(c.getColumnIndexOrThrow(Combined.URL));
         } while (c.moveToNext());
 
+        Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.BUTTON);
+
         mNewTabsListener.onNewTabs(urls);
     }
 
     private static class LastTabsCursorLoader extends SimpleCursorLoader {
         public LastTabsCursorLoader(Context context) {
             super(context);
         }
 
--- a/mobile/android/base/home/MostRecentPanel.java
+++ b/mobile/android/base/home/MostRecentPanel.java
@@ -4,16 +4,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.home;
 
 import java.util.Date;
 import java.util.EnumSet;
 
 import org.mozilla.gecko.R;
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.db.BrowserContract.Combined;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.db.BrowserDB.URLColumns;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
 
 import android.app.Activity;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -93,16 +95,18 @@ public class MostRecentPanel extends Hom
 
         mList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
             @Override
             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                 position -= mAdapter.getMostRecentSectionsCountBefore(position);
                 final Cursor c = mAdapter.getCursor(position);
                 final String url = c.getString(c.getColumnIndexOrThrow(URLColumns.URL));
 
+                Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL);
+
                 // This item is a TwoLinePageRow, so we allow switch-to-tab.
                 mUrlOpenListener.onUrlOpen(url, EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));
             }
         });
 
         mList.setContextMenuInfoFactory(new HomeContextMenuInfo.Factory() {
             @Override
             public HomeContextMenuInfo makeInfoForCursor(View view, int position, long id, Cursor cursor) {
--- a/mobile/android/base/home/ReadingListPanel.java
+++ b/mobile/android/base/home/ReadingListPanel.java
@@ -4,16 +4,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.home;
 
 import java.util.EnumSet;
 
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.ReaderModeUtils;
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.db.BrowserContract.ReadingListItems;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.db.BrowserDB.URLColumns;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
 
 import android.app.Activity;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -100,16 +102,18 @@ public class ReadingListPanel extends Ho
                 final Cursor c = mAdapter.getCursor();
                 if (c == null || !c.moveToPosition(position)) {
                     return;
                 }
 
                 String url = c.getString(c.getColumnIndexOrThrow(URLColumns.URL));
                 url = ReaderModeUtils.getAboutReaderForUrl(url);
 
+                Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL);
+
                 // This item is a TwoLinePageRow, so we allow switch-to-tab.
                 mUrlOpenListener.onUrlOpen(url, EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));
             }
         });
 
         mList.setContextMenuInfoFactory(new HomeContextMenuInfo.Factory() {
             @Override
             public HomeContextMenuInfo makeInfoForCursor(View view, int position, long id, Cursor cursor) {
--- a/mobile/android/base/home/SearchEngineRow.java
+++ b/mobile/android/base/home/SearchEngineRow.java
@@ -1,16 +1,18 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * 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/. */
 
 package org.mozilla.gecko.home;
 
 import org.mozilla.gecko.R;
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.home.BrowserSearch.OnEditSuggestionListener;
 import org.mozilla.gecko.home.BrowserSearch.OnSearchListener;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
 import org.mozilla.gecko.util.StringUtils;
 import org.mozilla.gecko.widget.AnimatedHeightLayout;
 import org.mozilla.gecko.widget.FaviconView;
 import org.mozilla.gecko.widget.FlowLayout;
 
@@ -74,19 +76,26 @@ class SearchEngineRow extends AnimatedHe
             public void onClick(View v) {
                 final String suggestion = getSuggestionTextFromView(v);
 
                 // If we're not clicking the user-entered view (the first suggestion item)
                 // and the search matches a URL pattern, go to that URL. Otherwise, do a
                 // search for the term.
                 if (v != mUserEnteredView && !StringUtils.isSearchQuery(suggestion, false)) {
                     if (mUrlOpenListener != null) {
+                        Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.SUGGESTION, "url");
+
                         mUrlOpenListener.onUrlOpen(suggestion, EnumSet.noneOf(OnUrlOpenListener.Flags.class));
                     }
                 } else if (mSearchListener != null) {
+                    if (v == mUserEnteredView) {
+                        Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.SUGGESTION, "user");
+                    } else {
+                        Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.SUGGESTION, "engine");
+                    }
                     mSearchListener.onSearch(mSearchEngine, suggestion);
                 }
             }
         };
 
         mLongClickListener = new OnLongClickListener() {
             @Override
             public boolean onLongClick(View v) {
@@ -130,16 +139,17 @@ class SearchEngineRow extends AnimatedHe
     }
 
     /**
      * Perform a search for the user-entered term.
      */
     public void performUserEnteredSearch() {
         String searchTerm = getSuggestionTextFromView(mUserEnteredView);
         if (mSearchListener != null) {
+            Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.SUGGESTION, "user");
             mSearchListener.onSearch(mSearchEngine, searchTerm);
         }
     }
 
     public void setSearchTerm(String searchTerm) {
         mUserEnteredTextView.setText(searchTerm);
 
         // mSearchEngine is not set in the first call to this method; the content description
--- a/mobile/android/base/home/TopSitesGridView.java
+++ b/mobile/android/base/home/TopSitesGridView.java
@@ -3,16 +3,18 @@
  * 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/. */
 
 package org.mozilla.gecko.home;
 
 import java.util.EnumSet;
 
 import org.mozilla.gecko.R;
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.ThumbnailHelper;
 import org.mozilla.gecko.db.BrowserDB.URLColumns;
 import org.mozilla.gecko.db.TopSitesCursorWrapper;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
 
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.database.Cursor;
@@ -105,16 +107,18 @@ public class TopSitesGridView extends Gr
 
                 // Decode "user-entered" URLs before loading them.
                 String url = HomeFragment.decodeUserEnteredUrl(row.getUrl());
 
                 // If the url is empty, the user can pin a site.
                 // If not, navigate to the page given by the url.
                 if (!TextUtils.isEmpty(url)) {
                     if (mUrlOpenListener != null) {
+                        Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.GRID_ITEM);
+
                         mUrlOpenListener.onUrlOpen(url, EnumSet.noneOf(OnUrlOpenListener.Flags.class));
                     }
                 } else {
                     if (mEditPinnedSiteListener != null) {
                         mEditPinnedSiteListener.onEditPinnedSite(position, "");
                     }
                 }
             }
--- a/mobile/android/base/home/TopSitesPanel.java
+++ b/mobile/android/base/home/TopSitesPanel.java
@@ -176,16 +176,18 @@ public class TopSitesPanel extends HomeF
 
                 final Cursor c = mListAdapter.getCursor();
                 if (c == null || !c.moveToPosition(position)) {
                     return;
                 }
 
                 final String url = c.getString(c.getColumnIndexOrThrow(URLColumns.URL));
 
+                Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.LIST_ITEM);
+
                 // This item is a TwoLinePageRow, so we allow switch-to-tab.
                 mUrlOpenListener.onUrlOpen(url, EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));
             }
         });
 
         mList.setContextMenuInfoFactory(new HomeContextMenuInfo.Factory() {
             @Override
             public HomeContextMenuInfo makeInfoForCursor(View view, int position, long id, Cursor cursor) {
--- a/mobile/android/base/prompts/PromptListAdapter.java
+++ b/mobile/android/base/prompts/PromptListAdapter.java
@@ -1,14 +1,16 @@
 package org.mozilla.gecko.prompts;
 
-import org.mozilla.gecko.menu.MenuItemActionView;
+import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.R;
-import org.mozilla.gecko.GeckoAppShell;
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.gfx.BitmapUtils;
+import org.mozilla.gecko.menu.MenuItemActionView;
 import org.mozilla.gecko.widget.GeckoActionProvider;
 
 import org.json.JSONArray;
 import org.json.JSONObject;
 import org.json.JSONException;
 
 import android.content.Context;
 import android.content.Intent;
@@ -192,16 +194,19 @@ public class PromptListAdapter extends A
                 }
 
                 final GeckoActionProvider provider = GeckoActionProvider.getForType(item.getIntent().getType(), getContext());
                 IntentChooserPrompt prompt = new IntentChooserPrompt(getContext(), provider);
                 prompt.show(item.label, getContext(), new IntentHandler() {
                     @Override
                     public void onIntentSelected(final Intent intent, final int p) {
                         provider.chooseActivity(p);
+
+                        // Context: Sharing via content contextmenu list (no explicit session is active)
+                        Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.LIST);
                     }
 
                     @Override
                     public void onCancelled() {
                         // do nothing
                     }
                 });
             }
--- a/mobile/android/base/tabspanel/RemoteTabsList.java
+++ b/mobile/android/base/tabspanel/RemoteTabsList.java
@@ -14,16 +14,18 @@ import android.widget.SimpleExpandableLi
 
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.Tabs;
 import org.mozilla.gecko.TabsAccessor;
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
 
 /**
  * The actual list of synced tabs. This serves as the only child view of {@link RemoteTabsContainer}
  * so it can be refreshed using a swipe-to-refresh gesture.
  */
 class RemoteTabsList extends ExpandableListView
                      implements ExpandableListView.OnGroupClickListener,
                                 ExpandableListView.OnChildClickListener,
@@ -63,16 +65,18 @@ class RemoteTabsList extends ExpandableL
     @Override
     public boolean onChildClick(ExpandableListView parent, View view, int groupPosition, int childPosition, long id) {
         HashMap <String, String> tab = tabsList.get(groupPosition).get(childPosition);
         if (tab == null) {
             autoHidePanel();
             return true;
         }
 
+        Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, "", "remote");
+
         Tabs.getInstance().loadUrl(tab.get("url"), Tabs.LOADURL_NEW_TAB);
         autoHidePanel();
         return true;
     }
 
     @Override
     public void onQueryTabsComplete(List<TabsAccessor.RemoteTab> remoteTabsList) {
         ArrayList<TabsAccessor.RemoteTab> remoteTabs = new ArrayList<TabsAccessor.RemoteTab> (remoteTabsList);
--- a/mobile/android/base/widget/GeckoActionProvider.java
+++ b/mobile/android/base/widget/GeckoActionProvider.java
@@ -1,15 +1,17 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * 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/. */
 
 package org.mozilla.gecko.widget;
 
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.menu.MenuItemActionView;
 
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.graphics.drawable.Drawable;
 import android.view.MenuItem;
@@ -189,18 +191,24 @@ public class GeckoActionProvider {
             if (mOnTargetListener != null) {
                 mOnTargetListener.onTargetSelected();
             }
         }
 
         @Override
         public boolean onMenuItemClick(MenuItem item) {
             chooseActivity(item.getItemId());
+
+            // Context: Sharing via chrome mainmenu list (no explicit session is active)
+            Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.LIST);
             return true;
         }
 
         @Override
         public void onClick(View view) {
             Integer index = (Integer) view.getTag();
             chooseActivity(index);
+
+            // Context: Sharing via chrome mainmenu and content contextmenu quickshare (no explicit session is active)
+            Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.BUTTON);
         }
     }
 }
--- a/mobile/android/chrome/content/aboutReader.js
+++ b/mobile/android/chrome/content/aboutReader.js
@@ -2,16 +2,19 @@
  * 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/. */
 
 let Ci = Components.interfaces, Cc = Components.classes, Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Services.jsm")
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "UITelemetry",
+                                  "resource://gre/modules/UITelemetry.jsm");
+
 XPCOMUtils.defineLazyGetter(window, "gChromeWin", function ()
   window.QueryInterface(Ci.nsIInterfaceRequestor)
     .getInterface(Ci.nsIWebNavigation)
     .QueryInterface(Ci.nsIDocShellTreeItem)
     .rootTreeItem
     .QueryInterface(Ci.nsIInterfaceRequestor)
     .getInterface(Ci.nsIDOMWindow)
     .QueryInterface(Ci.nsIDOMChromeWindow));
@@ -281,22 +284,28 @@ AboutReader.prototype = {
 
   _onReaderToggle: function Reader_onToggle() {
     if (!this._article)
       return;
 
     this._isReadingListItem = (this._isReadingListItem == 1) ? 0 : 1;
     this._updateToggleButton();
 
+    // Create a relative timestamp for telemetry
+    let uptime = Date.now() - Services.startup.getStartupInfo().linkerInitialized;
+
     if (this._isReadingListItem == 1) {
       gChromeWin.Reader.storeArticleInCache(this._article, function(success) {
         dump("Reader:Add (in reader) success=" + success);
 
-        let result = (success ? gChromeWin.Reader.READER_ADD_SUCCESS :
-            gChromeWin.Reader.READER_ADD_FAILED);
+        let result = gChromeWin.Reader.READER_ADD_FAILED;
+        if (success) {
+          result = gChromeWin.Reader.READER_ADD_SUCCESS;
+          UITelemetry.addEvent("save.1", "button", uptime, "reader");
+        }
 
         let json = JSON.stringify({ fromAboutReader: true, url: this._article.url });
         Services.obs.notifyObservers(null, "Reader:Add", json);
 
         gChromeWin.sendMessageToJava({
           type: "Reader:Added",
           result: result,
           title: this._article.title,
@@ -305,28 +314,34 @@ AboutReader.prototype = {
           excerpt: this._article.excerpt
         });
       }.bind(this));
     } else {
       // In addition to removing the article from the cache (handled in
       // browser.js), sending this message will cause the toggle button to be
       // updated (handled in this file).
       Services.obs.notifyObservers(null, "Reader:Remove", this._article.url);
+
+      UITelemetry.addEvent("unsave.1", "button", uptime, "reader");
     }
   },
 
   _onShare: function Reader_onShare() {
     if (!this._article)
       return;
 
     gChromeWin.sendMessageToJava({
       type: "Reader:Share",
       url: this._article.url,
       title: this._article.title
     });
+
+    // Create a relative timestamp for telemetry
+    let uptime = Date.now() - Services.startup.getStartupInfo().linkerInitialized;
+    UITelemetry.addEvent("share.1", "list", uptime);
   },
 
   _setFontSize: function Reader_setFontSize(newFontSize) {
     let bodyClasses = this._doc.body.classList;
 
     if (this._fontSize > 0)
       bodyClasses.remove("font-size" + this._fontSize);
 
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -7478,33 +7478,50 @@ let Reader = {
         type: "Reader:Click",
       });
     },
 
     readerModeActiveCallback: function(){
       sendMessageToJava({
         type: "Reader:LongClick",
       });
+
+      // Create a relative timestamp for telemetry
+      let uptime = Date.now() - Services.startup.getStartupInfo().linkerInitialized;
+      UITelemetry.addEvent("save.1", "pageaction", uptime, "reader");
     },
   },
 
   updatePageAction: function(tab) {
-    if(this.pageAction.id) {
+    if (this.pageAction.id) {
       NativeWindow.pageactions.remove(this.pageAction.id);
       delete this.pageAction.id;
     }
 
+    // Create a relative timestamp for telemetry
+    let uptime = Date.now() - Services.startup.getStartupInfo().linkerInitialized;
+
     if (tab.readerActive) {
       this.pageAction.id = NativeWindow.pageactions.add({
         title: Strings.browser.GetStringFromName("readerMode.exit"),
         icon: "drawable://reader_active",
         clickCallback: this.pageAction.readerModeCallback,
         important: true
       });
-    } else if (tab.readerEnabled) {
+
+      // Only start a reader session if the viewer is in the foreground. We do
+      // not track background reader viewers.
+      UITelemetry.startSession("reader.1", uptime);
+      return;
+    }
+
+    // Only stop a reader session if the foreground viewer is not visible.
+    UITelemetry.stopSession("reader.1", "", uptime);
+
+    if (tab.readerEnabled) {
       this.pageAction.id = NativeWindow.pageactions.add({
         title: Strings.browser.GetStringFromName("readerMode.enter"),
         icon: "drawable://reader",
         clickCallback:this.pageAction.readerModeCallback,
         longClickCallback: this.pageAction.readerModeActiveCallback,
         important: true
       });
     }
--- a/testing/xpcshell/xpcshell_android.ini
+++ b/testing/xpcshell/xpcshell_android.ini
@@ -34,8 +34,9 @@
 [include:intl/unicharutil/tests/unit/xpcshell.ini]
 [include:intl/uconv/tests/unit/xpcshell.ini]
 [include:uriloader/exthandler/tests/unit/xpcshell.ini]
 [include:storage/test/unit/xpcshell.ini]
 [include:docshell/test/unit/xpcshell.ini]
 [include:js/xpconnect/tests/unit/xpcshell.ini]
 [include:js/jsd/test/xpcshell.ini]
 [include:security/manager/ssl/tests/unit/xpcshell.ini]
+[include:toolkit/devtools/qrcode/tests/unit/xpcshell.ini]
--- a/testing/xpcshell/xpcshell_b2g.ini
+++ b/testing/xpcshell/xpcshell_b2g.ini
@@ -4,16 +4,17 @@
 
 [include:dom/apps/tests/unit/xpcshell.ini]
 [include:dom/mobilemessage/tests/xpcshell.ini]
 [include:dom/network/tests/unit_stats/xpcshell.ini]
 [include:dom/system/gonk/tests/xpcshell.ini]
 [include:dom/wappush/tests/xpcshell.ini]
 [include:toolkit/devtools/apps/tests/unit/xpcshell.ini]
 [include:toolkit/devtools/debugger/tests/unit/xpcshell.ini]
+[include:toolkit/devtools/qrcode/tests/unit/xpcshell.ini]
 [include:toolkit/devtools/sourcemap/tests/unit/xpcshell.ini]
 [include:toolkit/mozapps/downloads/tests/unit/xpcshell.ini]
 [include:toolkit/mozapps/update/tests/unit_aus_update/xpcshell.ini]
 [include:toolkit/mozapps/update/tests/unit_base_updater/xpcshell.ini]
 [include:toolkit/mozapps/update/tests/unit_timermanager/xpcshell.ini]
 [include:ipc/testshell/tests/xpcshell.ini]
 [include:b2g/components/test/unit/xpcshell.ini]
 [include:security/manager/ssl/tests/unit/xpcshell.ini]
--- a/toolkit/components/telemetry/UITelemetry.jsm
+++ b/toolkit/components/telemetry/UITelemetry.jsm
@@ -136,17 +136,16 @@ this.UITelemetry = {
     if (!this.enabled) {
       return;
     }
 
     let sessionStart = this._activeSessions[aName];
     delete this._activeSessions[aName];
 
     if (!sessionStart) {
-      Services.console.logStringMessage("UITelemetry error: no session [" + aName + "] to stop!");
       return;
     }
 
     let aEvent = {
       type: "session",
       name: aName,
       reason: aReason,
       start: sessionStart,
--- a/toolkit/content/license.html
+++ b/toolkit/content/license.html
@@ -108,16 +108,17 @@
       <li><a href="about:license#msinttypes">msinttypes License</a></li>
       <li><a href="about:license#myspell">MySpell License</a></li>
       <li><a href="about:license#nicer">nICEr License</a></li>
       <li><a href="about:license#nrappkit">nrappkit License</a></li>
       <li><a href="about:license#openvision">OpenVision License</a></li>
       <li><a href="about:license#pbkdf2-sha256">pbkdf2_sha256 License</a></li>
       <li><a href="about:license#praton">praton License</a></li>
       <li><a href="about:license#qcms">qcms License</a></li>
+      <li><a href="about:license#qrcode-generator">QR Code Generator License</a></li>
       <li><a href="about:license#xdg">Red Hat xdg_user_dir_lookup License</a></li>
       <li><a href="about:license#hunspell-ru">Russian Spellchecking Dictionary License</a></li>
       <li><a href="about:license#sctp">SCTP Licenses</a></li>
       <li><a href="about:license#skia">Skia License</a></li>
       <li><a href="about:license#snappy">Snappy License</a></li>
       <li><a href="about:license#sparkle">Sparkle License</a></li>
 #ifdef USE_STLPORT
       <li><a href="about:license#stlport">STLport License</a></li>
@@ -3571,16 +3572,45 @@ FITNESS FOR A PARTICULAR PURPOSE AND NON
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 </pre>
 
 
     <hr>
 
+    <h1><a id="qrcode-generator"></a>QR Code Generator License</h1>
+
+    <p>This license applies to certain files in the directory
+      <span class="path">toolkit/devtools/qrcode/encoder/</span>.</p>
+<pre>
+Copyright (c) 2009 Kazuhiko Arase
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+</pre>
+
+
+    <hr>
+
     <h1><a id="xdg"></a>Red Hat xdg_user_dir_lookup License</h1>
 
     <p>This license applies to the
     <span class="path">xdg_user_dir_lookup</span> function in
     <span class="path">xpcom/io/SpecialSystemDirectory.cpp</span>.</p>
 
 <pre>
 Copyright (c) 2007 Red Hat, Inc.
--- a/toolkit/content/tests/chrome/chrome.ini
+++ b/toolkit/content/tests/chrome/chrome.ini
@@ -125,16 +125,18 @@ skip-if = os == "win" # Intermittent fai
 [test_popuphidden.xul]
 [test_popupincontent.xul]
 [test_popupremoving.xul]
 [test_popupremoving_frame.xul]
 [test_position.xul]
 [test_preferences.xul]
 [test_preferences_beforeaccept.xul]
 support-files = window_preferences_beforeaccept.xul
+[test_preferences_onsyncfrompreference.xul]
+support-files = window_preferences_onsyncfrompreference.xul
 [test_progressmeter.xul]
 [test_props.xul]
 [test_radio.xul]
 [test_richlist_direction.xul]
 [test_righttoleft.xul]
 [test_scale.xul]
 [test_scaledrag.xul]
 [test_screenPersistence.xul]
new file mode 100644
--- /dev/null
+++ b/toolkit/content/tests/chrome/test_preferences_onsyncfrompreference.xul
@@ -0,0 +1,62 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+<!-- 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/.  -->
+<window title="Preferences Window beforeaccept Tests"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"/>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <script type="application/javascript">
+  <![CDATA[
+    const PREFS = ['tests.onsyncfrompreference.pref1',
+                   'tests.onsyncfrompreference.pref2',
+                   'tests.onsyncfrompreference.pref3'];
+
+    SimpleTest.waitForExplicitFinish();
+
+    for (let pref of PREFS) {
+      SpecialPowers.setIntPref(pref, 1);
+    }
+
+    let counter = 0;
+    let prefWindow = openDialog("window_preferences_onsyncfrompreference.xul", "", "", onSync);
+
+    SimpleTest.registerCleanupFunction(() => {
+      for (let pref of PREFS) {
+        SpecialPowers.clearUserPref(pref);
+      }
+      prefWindow.close();
+    });
+
+    // Onsyncfrompreference handler for the prefs
+    function onSync() {
+      for (let pref of PREFS) {
+        // The `value` field of each <preference> element should be initialized by now.
+
+        is(SpecialPowers.getIntPref(pref), prefWindow.document.getElementById(pref).value,
+           "Pref constructor was called correctly")
+      }
+
+      counter++;
+
+      if (counter == PREFS.length) {
+        SimpleTest.finish();
+      }
+      return true;
+    }
+  ]]>
+  </script>
+
+  <body xmlns="http://www.w3.org/1999/xhtml">
+    <p id="display"></p>
+    <div id="content" style="display: none"></div>
+    <pre id="test"></pre>
+  </body>
+
+</window>
new file mode 100644
--- /dev/null
+++ b/toolkit/content/tests/chrome/window_preferences_onsyncfrompreference.xul
@@ -0,0 +1,42 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<!-- 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/.  -->
+<!--
+  XUL Widget Test for preferences window with onsyncfrompreference
+  This test ensures that onsyncfrompreference handlers are called after all the
+  values of the corresponding preference element have been set correctly
+-->
+<prefwindow xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+            title="preferences window"
+            width="300" height="300"
+            windowtype="test:preferences">
+
+  <prefpane id="sample_pane" label="Sample Prefpane">
+    <preferences id="sample_preferences">
+      <preference id="tests.onsyncfrompreference.pref1"
+                  name="tests.onsyncfrompreference.pref1"
+                  type="int"/>
+      <preference id="tests.onsyncfrompreference.pref2"
+                  name="tests.onsyncfrompreference.pref2"
+                  type="int"/>
+      <preference id="tests.onsyncfrompreference.pref3"
+                  name="tests.onsyncfrompreference.pref3"
+                  type="int"/>
+    </preferences>
+  </prefpane>
+  <label>Test Prefpane</label>
+        <checkbox id="check1" label="Label1"
+                preference="tests.onsyncfrompreference.pref1"
+                onsyncfrompreference="return window.arguments[0]();"
+                onsynctopreference="return 1;"/>
+        <checkbox id="check2" label="Label2"
+                preference="tests.onsyncfrompreference.pref2"
+                onsyncfrompreference="return window.arguments[0]();"
+                onsynctopreference="return 1;"/>
+        <checkbox id="check3" label="Label3"
+                preference="tests.onsyncfrompreference.pref3"
+                onsyncfrompreference="return window.arguments[0]();"
+                onsynctopreference="return 1;"/>
+</prefwindow>
--- a/toolkit/content/widgets/preferences.xml
+++ b/toolkit/content/widgets/preferences.xml
@@ -25,16 +25,35 @@
 #       </preferences>
 #       <checkbox label="Preference" preference="preference1"/>
 #     </prefpane>
 #   </prefwindow>
 #
 
   <binding id="preferences">
     <implementation implements="nsIObserver">
+      <method name="_constructAfterChildren">
+      <body>
+      <![CDATA[
+      // This method will be called after each one of the child
+      // <preference> elements is constructed. Its purpose is to propagate
+      // the values to the associated form elements
+
+      var elements = this.getElementsByTagName("preference");
+      for (let element of elements) {
+        if (!element._constructed) {
+          return;
+        }
+      }
+      for (let element of elements) {
+        element.updateElements();
+      }
+      ]]>
+      </body>
+      </method>
       <method name="observe">
         <parameter name="aSubject"/>
         <parameter name="aTopic"/>
         <parameter name="aData"/>
         <body>
         <![CDATA[
           for (var i = 0; i < this.childNodes.length; ++i) {
             var preference = this.childNodes[i];
@@ -96,16 +115,18 @@
       </property>
     </implementation>
   </binding>
 
   <binding id="preference">
     <implementation>
       <constructor>
       <![CDATA[
+        this._constructed = true;
+
         // if the element has been inserted without the name attribute set,
         // we have nothing to do here
         if (!this.name)
           return;
 
         this.preferences.rootBranchInternal
             .addObserver(this.name, this.preferences, false);
         // In non-instant apply mode, we must try and use the last saved state
@@ -121,28 +142,30 @@
           for (var k = 0; (k < parentPreferences.length && !preference); ++k) {
             var parentPrefs = parentPreferences[k]
                                     .getElementsByAttribute("name", this.name);
             for (var l = 0; (l < parentPrefs.length && !preference); ++l) {
               if (parentPrefs[l].localName == "preference")
                 preference = parentPrefs[l];
             }
           }
-          this._setValue(preference ? preference.value 
-                                    : this.valueFromPreferences, false);
+
+          // Don't use the value setter here, we don't want updateElements to be prematurely fired.
+          this._value = preference ? preference.value : this.valueFromPreferences;
         }
         else
-          this._setValue(this.valueFromPreferences, false);
+          this._value = this.valueFromPreferences;
+        this.preferences._constructAfterChildren();
       ]]>
       </constructor>
       <destructor>
         this.preferences.rootBranchInternal
             .removeObserver(this.name, this.preferences);
       </destructor>
-      
+      <field name="_constructed">false</field>
       <property name="instantApply">
         <getter>
           return this.getAttribute("instantApply") == "true" || this.preferences.instantApply;
         </getter>
       </property>
 
       <property name="preferences" onget="return this.parentNode"/>
       <property name="name" onget="return this.getAttribute('name');">
@@ -164,34 +187,29 @@
       <property name="inverted" onget="return this.getAttribute('inverted') == 'true';"
                                 onset="this.setAttribute('inverted', val); return val;"/>
       <property name="readonly" onget="return this.getAttribute('readonly') == 'true';"
                                 onset="this.setAttribute('readonly', val); return val;"/>
 
       <field name="_value">null</field>
       <method name="_setValue">
         <parameter name="aValue"/>
-        <parameter name="aUpdate"/>
         <body>
         <![CDATA[
-          if (aUpdate && this.value !== aValue) {
+          if (this.value !== aValue) {
             this._value = aValue;
             if (this.instantApply)
               this.valueFromPreferences = aValue;
             this.preferences.fireChangedEvent(this);
           }
-          else if (!aUpdate) {
-            this._value = aValue;
-            this.updateElements();
-          }
           return aValue;
         ]]>
         </body>
       </method>
-      <property name="value" onget="return this._value" onset="return this._setValue(val, true);"/>
+      <property name="value" onget="return this._value" onset="return this._setValue(val);"/>
       
       <property name="locked">
         <getter>
           return this.preferences.rootBranch.prefIsLocked(this.name);
         </getter>
       </property>
       
       <property name="disabled">
--- a/toolkit/devtools/moz.build
+++ b/toolkit/devtools/moz.build
@@ -8,13 +8,14 @@ PARALLEL_DIRS += [
     'server',
     'client',
     'gcli',
     'sourcemap',
     'webconsole',
     'apps',
     'styleinspector',
     'acorn',
-    'pretty-fast'
+    'pretty-fast',
+    'qrcode'
 ]
 
 MOCHITEST_CHROME_MANIFESTS += ['tests/mochitest/chrome.ini']
 XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/qrcode/encoder/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2009 Kazuhiko Arase
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/qrcode/encoder/index.js
@@ -0,0 +1,1673 @@
+//---------------------------------------------------------------------
+//
+// QR Code Generator for JavaScript
+//
+// Copyright (c) 2009 Kazuhiko Arase
+//
+// URL: http://www.d-project.com/
+//
+// Licensed under the MIT license:
+//	http://www.opensource.org/licenses/mit-license.php
+//
+// The word 'QR Code' is registered trademark of
+// DENSO WAVE INCORPORATED
+//	http://www.denso-wave.com/qrcode/faqpatent-e.html
+//
+//---------------------------------------------------------------------
+
+var qrcode = function() {
+
+	//---------------------------------------------------------------------
+	// qrcode
+	//---------------------------------------------------------------------
+
+	/**
+	 * qrcode
+	 * @param typeNumber 1 to 10
+	 * @param errorCorrectLevel 'L','M','Q','H'
+	 */
+	var qrcode = function(typeNumber, errorCorrectLevel) {
+
+		var PAD0 = 0xEC;
+		var PAD1 = 0x11;
+
+		var _typeNumber = typeNumber;
+		var _errorCorrectLevel = QRErrorCorrectLevel[errorCorrectLevel];
+		var _modules = null;
+		var _moduleCount = 0;
+		var _dataCache = null;
+		var _dataList = new Array();
+
+		var _this = {};
+
+		var makeImpl = function(test, maskPattern) {
+
+			_moduleCount = _typeNumber * 4 + 17;
+			_modules = function(moduleCount) {
+				var modules = new Array(moduleCount);
+				for (var row = 0; row < moduleCount; row += 1) {
+					modules[row] = new Array(moduleCount);
+					for (var col = 0; col < moduleCount; col += 1) {
+						modules[row][col] = null;
+					}
+				}
+				return modules;
+			}(_moduleCount);
+
+			setupPositionProbePattern(0, 0);
+			setupPositionProbePattern(_moduleCount - 7, 0);
+			setupPositionProbePattern(0, _moduleCount - 7);
+			setupPositionAdjustPattern();
+			setupTimingPattern();
+			setupTypeInfo(test, maskPattern);
+
+			if (_typeNumber >= 7) {
+				setupTypeNumber(test);
+			}
+
+			if (_dataCache == null) {
+				_dataCache = createData(_typeNumber, _errorCorrectLevel, _dataList);
+			}
+
+			mapData(_dataCache, maskPattern);
+		};
+
+		var setupPositionProbePattern = function(row, col) {
+
+			for (var r = -1; r <= 7; r += 1) {
+
+				if (row + r <= -1 || _moduleCount <= row + r) continue;
+
+				for (var c = -1; c <= 7; c += 1) {
+
+					if (col + c <= -1 || _moduleCount <= col + c) continue;
+
+					if ( (0 <= r && r <= 6 && (c == 0 || c == 6) )
+							|| (0 <= c && c <= 6 && (r == 0 || r == 6) )
+							|| (2 <= r && r <= 4 && 2 <= c && c <= 4) ) {
+						_modules[row + r][col + c] = true;
+					} else {
+						_modules[row + r][col + c] = false;
+					}
+				}
+			}
+		};
+
+		var getBestMaskPattern = function() {
+
+			var minLostPoint = 0;
+			var pattern = 0;
+
+			for (var i = 0; i < 8; i += 1) {
+
+				makeImpl(true, i);
+
+				var lostPoint = QRUtil.getLostPoint(_this);
+
+				if (i == 0 || minLostPoint > lostPoint) {
+					minLostPoint = lostPoint;
+					pattern = i;
+				}
+			}
+
+			return pattern;
+		};
+
+		var setupTimingPattern = function() {
+
+			for (var r = 8; r < _moduleCount - 8; r += 1) {
+				if (_modules[r][6] != null) {
+					continue;
+				}
+				_modules[r][6] = (r % 2 == 0);
+			}
+
+			for (var c = 8; c < _moduleCount - 8; c += 1) {
+				if (_modules[6][c] != null) {
+					continue;
+				}
+				_modules[6][c] = (c % 2 == 0);
+			}
+		};
+
+		var setupPositionAdjustPattern = function() {
+
+			var pos = QRUtil.getPatternPosition(_typeNumber);
+
+			for (var i = 0; i < pos.length; i += 1) {
+
+				for (var j = 0; j < pos.length; j += 1) {
+
+					var row = pos[i];
+					var col = pos[j];
+
+					if (_modules[row][col] != null) {
+						continue;
+					}
+
+					for (var r = -2; r <= 2; r += 1) {
+
+						for (var c = -2; c <= 2; c += 1) {
+
+							if (r == -2 || r == 2 || c == -2 || c == 2
+									|| (r == 0 && c == 0) ) {
+								_modules[row + r][col + c] = true;
+							} else {
+								_modules[row + r][col + c] = false;
+							}
+						}
+					}
+				}
+			}
+		};
+
+		var setupTypeNumber = function(test) {
+
+			var bits = QRUtil.getBCHTypeNumber(_typeNumber);
+
+			for (var i = 0; i < 18; i += 1) {
+				var mod = (!test && ( (bits >> i) & 1) == 1);
+				_modules[Math.floor(i / 3)][i % 3 + _moduleCount - 8 - 3] = mod;
+			}
+
+			for (var i = 0; i < 18; i += 1) {
+				var mod = (!test && ( (bits >> i) & 1) == 1);
+				_modules[i % 3 + _moduleCount - 8 - 3][Math.floor(i / 3)] = mod;
+			}
+		};
+
+		var setupTypeInfo = function(test, maskPattern) {
+
+			var data = (_errorCorrectLevel << 3) | maskPattern;
+			var bits = QRUtil.getBCHTypeInfo(data);
+
+			// vertical
+			for (var i = 0; i < 15; i += 1) {
+
+				var mod = (!test && ( (bits >> i) & 1) == 1);
+
+				if (i < 6) {
+					_modules[i][8] = mod;
+				} else if (i < 8) {
+					_modules[i + 1][8] = mod;
+				} else {
+					_modules[_moduleCount - 15 + i][8] = mod;
+				}
+			}
+
+			// horizontal
+			for (var i = 0; i < 15; i += 1) {
+
+				var mod = (!test && ( (bits >> i) & 1) == 1);
+
+				if (i < 8) {
+					_modules[8][_moduleCount - i - 1] = mod;
+				} else if (i < 9) {
+					_modules[8][15 - i - 1 + 1] = mod;
+				} else {
+					_modules[8][15 - i - 1] = mod;
+				}
+			}
+
+			// fixed module
+			_modules[_moduleCount - 8][8] = (!test);
+		};
+
+		var mapData = function(data, maskPattern) {
+
+			var inc = -1;
+			var row = _moduleCount - 1;
+			var bitIndex = 7;
+			var byteIndex = 0;
+			var maskFunc = QRUtil.getMaskFunction(maskPattern);
+
+			for (var col = _moduleCount - 1; col > 0; col -= 2) {
+
+				if (col == 6) col -= 1;
+
+				while (true) {
+
+					for (var c = 0; c < 2; c += 1) {
+
+						if (_modules[row][col - c] == null) {
+
+							var dark = false;
+
+							if (byteIndex < data.length) {
+								dark = ( ( (data[byteIndex] >>> bitIndex) & 1) == 1);
+							}
+
+							var mask = maskFunc(row, col - c);
+
+							if (mask) {
+								dark = !dark;
+							}
+
+							_modules[row][col - c] = dark;
+							bitIndex -= 1;
+
+							if (bitIndex == -1) {
+								byteIndex += 1;
+								bitIndex = 7;
+							}
+						}
+					}
+
+					row += inc;
+
+					if (row < 0 || _moduleCount <= row) {
+						row -= inc;
+						inc = -inc;
+						break;
+					}
+				}
+			}
+		};
+
+		var createBytes = function(buffer, rsBlocks) {
+
+			var offset = 0;
+
+			var maxDcCount = 0;
+			var maxEcCount = 0;
+
+			var dcdata = new Array(rsBlocks.length);
+			var ecdata = new Array(rsBlocks.length);
+
+			for (var r = 0; r < rsBlocks.length; r += 1) {
+
+				var dcCount = rsBlocks[r].dataCount;
+				var ecCount = rsBlocks[r].totalCount - dcCount;
+
+				maxDcCount = Math.max(maxDcCount, dcCount);
+				maxEcCount = Math.max(maxEcCount, ecCount);
+
+				dcdata[r] = new Array(dcCount);
+
+				for (var i = 0; i < dcdata[r].length; i += 1) {
+					dcdata[r][i] = 0xff & buffer.getBuffer()[i + offset];
+				}
+				offset += dcCount;
+
+				var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount);
+				var rawPoly = qrPolynomial(dcdata[r], rsPoly.getLength() - 1);
+
+				var modPoly = rawPoly.mod(rsPoly);
+				ecdata[r] = new Array(rsPoly.getLength() - 1);
+				for (var i = 0; i < ecdata[r].length; i += 1) {
+					var modIndex = i + modPoly.getLength() - ecdata[r].length;
+					ecdata[r][i] = (modIndex >= 0)? modPoly.getAt(modIndex) : 0;
+				}
+			}
+
+			var totalCodeCount = 0;
+			for (var i = 0; i < rsBlocks.length; i += 1) {
+				totalCodeCount += rsBlocks[i].totalCount;
+			}
+
+			var data = new Array(totalCodeCount);
+			var index = 0;
+
+			for (var i = 0; i < maxDcCount; i += 1) {
+				for (var r = 0; r < rsBlocks.length; r += 1) {
+					if (i < dcdata[r].length) {
+						data[index] = dcdata[r][i];
+						index += 1;
+					}
+				}
+			}
+
+			for (var i = 0; i < maxEcCount; i += 1) {
+				for (var r = 0; r < rsBlocks.length; r += 1) {
+					if (i < ecdata[r].length) {
+						data[index] = ecdata[r][i];
+						index += 1;
+					}
+				}
+			}
+
+			return data;
+		};
+
+		var createData = function(typeNumber, errorCorrectLevel, dataList) {
+
+			var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectLevel);
+
+			var buffer = qrBitBuffer();
+
+			for (var i = 0; i < dataList.length; i += 1) {
+				var data = dataList[i];
+				buffer.put(data.getMode(), 4);
+				buffer.put(data.getLength(), QRUtil.getLengthInBits(data.getMode(), typeNumber) );
+				data.write(buffer);
+			}
+
+			// calc num max data.
+			var totalDataCount = 0;
+			for (var i = 0; i < rsBlocks.length; i += 1) {
+				totalDataCount += rsBlocks[i].dataCount;
+			}
+
+			if (buffer.getLengthInBits() > totalDataCount * 8) {
+				throw new Error('code length overflow. ('
+					+ buffer.getLengthInBits()
+					+ '>'
+					+ totalDataCount * 8
+					+ ')');
+			}
+
+			// end code
+			if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) {
+				buffer.put(0, 4);
+			}
+
+			// padding
+			while (buffer.getLengthInBits() % 8 != 0) {
+				buffer.putBit(false);
+			}
+
+			// padding
+			while (true) {
+
+				if (buffer.getLengthInBits() >= totalDataCount * 8) {
+					break;
+				}
+				buffer.put(PAD0, 8);
+
+				if (buffer.getLengthInBits() >= totalDataCount * 8) {
+					break;
+				}
+				buffer.put(PAD1, 8);
+			}
+
+			return createBytes(buffer, rsBlocks);
+		};
+
+		_this.addData = function(data) {
+			var newData = qr8BitByte(data);
+			_dataList.push(newData);
+			_dataCache = null;
+		};
+
+		_this.isDark = function(row, col) {
+			if (row < 0 || _moduleCount <= row || col < 0 || _moduleCount <= col) {
+				throw new Error(row + ',' + col);
+			}
+			return _modules[row][col];
+		};
+
+		_this.getModuleCount = function() {
+			return _moduleCount;
+		};
+
+		_this.make = function() {
+			makeImpl(false, getBestMaskPattern() );
+		};
+
+		_this.createTableTag = function(cellSize, margin) {
+
+			cellSize = cellSize || 2;
+			margin = (typeof margin == 'undefined')? cellSize * 4 : margin;
+
+			var qrHtml = '';
+
+			qrHtml += '<table style="';
+			qrHtml += ' border-width: 0px; border-style: none;';
+			qrHtml += ' border-collapse: collapse;';
+			qrHtml += ' padding: 0px; margin: ' + margin + 'px;';
+			qrHtml += '">';
+			qrHtml += '<tbody>';
+
+			for (var r = 0; r < _this.getModuleCount(); r += 1) {
+
+				qrHtml += '<tr>';
+
+				for (var c = 0; c < _this.getModuleCount(); c += 1) {
+					qrHtml += '<td style="';
+					qrHtml += ' border-width: 0px; border-style: none;';
+					qrHtml += ' border-collapse: collapse;';
+					qrHtml += ' padding: 0px; margin: 0px;';
+					qrHtml += ' width: ' + cellSize + 'px;';
+					qrHtml += ' height: ' + cellSize + 'px;';
+					qrHtml += ' background-color: ';
+					qrHtml += _this.isDark(r, c)? '#000000' : '#ffffff';
+					qrHtml += ';';
+					qrHtml += '"/>';
+				}
+
+				qrHtml += '</tr>';
+			}
+
+			qrHtml += '</tbody>';
+			qrHtml += '</table>';
+
+			return qrHtml;
+		};
+
+		_this.createImgTag = function(cellSize, margin) {
+
+			cellSize = cellSize || 2;
+			margin = (typeof margin == 'undefined')? cellSize * 4 : margin;
+
+			var size = _this.getModuleCount() * cellSize + margin * 2;
+			var min = margin;
+			var max = size - margin;
+
+			return createImgTag(size, size, function(x, y) {
+				if (min <= x && x < max && min <= y && y < max) {
+					var c = Math.floor( (x - min) / cellSize);
+					var r = Math.floor( (y - min) / cellSize);
+					return _this.isDark(r, c)? 0 : 1;
+				} else {
+					return 1;
+				}
+			} );
+		};
+
+		_this.createImgData = function(cellSize, margin) {
+
+			cellSize = cellSize || 2;
+			margin = (typeof margin == 'undefined')? cellSize * 4 : margin;
+
+			var size = _this.getModuleCount() * cellSize + margin * 2;
+			var min = margin;
+			var max = size - margin;
+
+			var base64 = createGifData(size, size, function(x, y) {
+				if (min <= x && x < max && min <= y && y < max) {
+					var c = Math.floor( (x - min) / cellSize);
+					var r = Math.floor( (y - min) / cellSize);
+					return _this.isDark(r, c)? 0 : 1;
+				} else {
+					return 1;
+				}
+			} );
+
+                        return {
+                                src: 'data:image/gif;base64,' + base64,
+                                width: size,
+                                height: size
+                        };
+		};
+
+		return _this;
+	};
+
+	//---------------------------------------------------------------------
+	// qrcode.stringToBytes
+	//---------------------------------------------------------------------
+
+	qrcode.stringToBytes = function(s) {
+		var bytes = new Array();
+		for (var i = 0; i < s.length; i += 1) {
+			var c = s.charCodeAt(i);
+			bytes.push(c & 0xff);
+		}
+		return bytes;
+	};
+
+	//---------------------------------------------------------------------
+	// qrcode.createStringToBytes
+	//---------------------------------------------------------------------
+
+	/**
+	 * @param unicodeData base64 string of byte array.
+	 * [16bit Unicode],[16bit Bytes], ...
+	 * @param numChars
+	 */
+	qrcode.createStringToBytes = function(unicodeData, numChars) {
+
+		// create conversion map.
+
+		var unicodeMap = function() {
+
+			var bin = base64DecodeInputStream(unicodeData);
+			var read = function() {
+				var b = bin.read();
+				if (b == -1) throw new Error();
+				return b;
+			};
+
+			var count = 0;
+			var unicodeMap = {};
+			while (true) {
+				var b0 = bin.read();
+				if (b0 == -1) break;
+				var b1 = read();
+				var b2 = read();
+				var b3 = read();
+				var k = String.fromCharCode( (b0 << 8) | b1);
+				var v = (b2 << 8) | b3;
+				unicodeMap[k] = v;
+				count += 1;
+			}
+			if (count != numChars) {
+				throw new Error(count + ' != ' + numChars);
+			}
+
+			return unicodeMap;
+		}();
+
+		var unknownChar = '?'.charCodeAt(0);
+
+		return function(s) {
+			var bytes = new Array();
+			for (var i = 0; i < s.length; i += 1) {
+				var c = s.charCodeAt(i);
+				if (c < 128) {
+					bytes.push(c);
+				} else {
+					var b = unicodeMap[s.charAt(i)];
+					if (typeof b == 'number') {
+						if ( (b & 0xff) == b) {
+							// 1byte
+							bytes.push(b);
+						} else {
+							// 2bytes
+							bytes.push(b >>> 8);
+							bytes.push(b & 0xff);
+						}
+					} else {
+						bytes.push(unknownChar);
+					}
+				}
+			}
+			return bytes;
+		};
+	};
+
+	//---------------------------------------------------------------------
+	// QRMode
+	//---------------------------------------------------------------------
+
+	var QRMode = {
+		MODE_NUMBER :		1 << 0,
+		MODE_ALPHA_NUM : 	1 << 1,
+		MODE_8BIT_BYTE : 	1 << 2,
+		MODE_KANJI :		1 << 3
+	};
+
+	//---------------------------------------------------------------------
+	// QRErrorCorrectLevel
+	//---------------------------------------------------------------------
+
+	var QRErrorCorrectLevel = {
+		L : 1,
+		M : 0,
+		Q : 3,
+		H : 2
+	};
+        exports.QRErrorCorrectLevel = QRErrorCorrectLevel;
+
+	//---------------------------------------------------------------------
+	// QRMaskPattern
+	//---------------------------------------------------------------------
+
+	var QRMaskPattern = {
+		PATTERN000 : 0,
+		PATTERN001 : 1,
+		PATTERN010 : 2,
+		PATTERN011 : 3,
+		PATTERN100 : 4,
+		PATTERN101 : 5,
+		PATTERN110 : 6,
+		PATTERN111 : 7
+	};
+
+	//---------------------------------------------------------------------
+	// QRUtil
+	//---------------------------------------------------------------------
+
+	var QRUtil = function() {
+
+		var PATTERN_POSITION_TABLE = [
+			[],
+			[6, 18],
+			[6, 22],
+			[6, 26],
+			[6, 30],
+			[6, 34],
+			[6, 22, 38],
+			[6, 24, 42],
+			[6, 26, 46],
+			[6, 28, 50],
+			[6, 30, 54],
+			[6, 32, 58],
+			[6, 34, 62],
+			[6, 26, 46, 66],
+			[6, 26, 48, 70],
+			[6, 26, 50, 74],
+			[6, 30, 54, 78],
+			[6, 30, 56, 82],
+			[6, 30, 58, 86],
+			[6, 34, 62, 90],
+			[6, 28, 50, 72, 94],
+			[6, 26, 50, 74, 98],
+			[6, 30, 54, 78, 102],
+			[6, 28, 54, 80, 106],
+			[6, 32, 58, 84, 110],
+			[6, 30, 58, 86, 114],
+			[6, 34, 62, 90, 118],
+			[6, 26, 50, 74, 98, 122],
+			[6, 30, 54, 78, 102, 126],
+			[6, 26, 52, 78, 104, 130],
+			[6, 30, 56, 82, 108, 134],
+			[6, 34, 60, 86, 112, 138],
+			[6, 30, 58, 86, 114, 142],
+			[6, 34, 62, 90, 118, 146],
+			[6, 30, 54, 78, 102, 126, 150],
+			[6, 24, 50, 76, 102, 128, 154],
+			[6, 28, 54, 80, 106, 132, 158],
+			[6, 32, 58, 84, 110, 136, 162],
+			[6, 26, 54, 82, 110, 138, 166],
+			[6, 30, 58, 86, 114, 142, 170]
+		];
+		var G15 = (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0);
+		var G18 = (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0);
+		var G15_MASK = (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1);
+
+		var _this = {};
+
+		var getBCHDigit = function(data) {
+			var digit = 0;
+			while (data != 0) {
+				digit += 1;
+				data >>>= 1;
+			}
+			return digit;
+		};
+
+		_this.getBCHTypeInfo = function(data) {
+			var d = data << 10;
+			while (getBCHDigit(d) - getBCHDigit(G15) >= 0) {
+				d ^= (G15 << (getBCHDigit(d) - getBCHDigit(G15) ) );
+			}
+			return ( (data << 10) | d) ^ G15_MASK;
+		};
+
+		_this.getBCHTypeNumber = function(data) {
+			var d = data << 12;
+			while (getBCHDigit(d) - getBCHDigit(G18) >= 0) {
+				d ^= (G18 << (getBCHDigit(d) - getBCHDigit(G18) ) );
+			}
+			return (data << 12) | d;
+		};
+
+		_this.getPatternPosition = function(typeNumber) {
+			return PATTERN_POSITION_TABLE[typeNumber - 1];
+		};
+
+		_this.getMaskFunction = function(maskPattern) {
+
+			switch (maskPattern) {
+
+			case QRMaskPattern.PATTERN000 :
+				return function(i, j) { return (i + j) % 2 == 0; };
+			case QRMaskPattern.PATTERN001 :
+				return function(i, j) { return i % 2 == 0; };
+			case QRMaskPattern.PATTERN010 :
+				return function(i, j) { return j % 3 == 0; };
+			case QRMaskPattern.PATTERN011 :
+				return function(i, j) { return (i + j) % 3 == 0; };
+			case QRMaskPattern.PATTERN100 :
+				return function(i, j) { return (Math.floor(i / 2) + Math.floor(j / 3) ) % 2 == 0; };
+			case QRMaskPattern.PATTERN101 :
+				return function(i, j) { return (i * j) % 2 + (i * j) % 3 == 0; };
+			case QRMaskPattern.PATTERN110 :
+				return function(i, j) { return ( (i * j) % 2 + (i * j) % 3) % 2 == 0; };
+			case QRMaskPattern.PATTERN111 :
+				return function(i, j) { return ( (i * j) % 3 + (i + j) % 2) % 2 == 0; };
+
+			default :
+				throw new Error('bad maskPattern:' + maskPattern);
+			}
+		};
+
+		_this.getErrorCorrectPolynomial = function(errorCorrectLength) {
+			var a = qrPolynomial([1], 0);
+			for (var i = 0; i < errorCorrectLength; i += 1) {
+				a = a.multiply(qrPolynomial([1, QRMath.gexp(i)], 0) );
+			}
+			return a;
+		};
+
+		_this.getLengthInBits = function(mode, type) {
+
+			if (1 <= type && type < 10) {
+
+				// 1 - 9
+
+				switch(mode) {
+				case QRMode.MODE_NUMBER 	: return 10;
+				case QRMode.MODE_ALPHA_NUM 	: return 9;
+				case QRMode.MODE_8BIT_BYTE	: return 8;
+				case QRMode.MODE_KANJI		: return 8;
+				default :
+					throw new Error('mode:' + mode);
+				}
+
+			} else if (type < 27) {
+
+				// 10 - 26
+
+				switch(mode) {
+				case QRMode.MODE_NUMBER 	: return 12;
+				case QRMode.MODE_ALPHA_NUM 	: return 11;
+				case QRMode.MODE_8BIT_BYTE	: return 16;
+				case QRMode.MODE_KANJI		: return 10;
+				default :
+					throw new Error('mode:' + mode);
+				}
+
+			} else if (type < 41) {
+
+				// 27 - 40
+
+				switch(mode) {
+				case QRMode.MODE_NUMBER 	: return 14;
+				case QRMode.MODE_ALPHA_NUM	: return 13;
+				case QRMode.MODE_8BIT_BYTE	: return 16;
+				case QRMode.MODE_KANJI		: return 12;
+				default :
+					throw new Error('mode:' + mode);
+				}
+
+			} else {
+				throw new Error('type:' + type);
+			}
+		};
+
+		_this.getLostPoint = function(qrcode) {
+
+			var moduleCount = qrcode.getModuleCount();
+
+			var lostPoint = 0;
+
+			// LEVEL1
+
+			for (var row = 0; row < moduleCount; row += 1) {
+				for (var col = 0; col < moduleCount; col += 1) {
+
+					var sameCount = 0;
+					var dark = qrcode.isDark(row, col);
+
+					for (var r = -1; r <= 1; r += 1) {
+
+						if (row + r < 0 || moduleCount <= row + r) {
+							continue;
+						}
+
+						for (var c = -1; c <= 1; c += 1) {
+
+							if (col + c < 0 || moduleCount <= col + c) {
+								continue;
+							}
+
+							if (r == 0 && c == 0) {
+								continue;
+							}
+
+							if (dark == qrcode.isDark(row + r, col + c) ) {
+								sameCount += 1;
+							}
+						}
+					}
+
+					if (sameCount > 5) {
+						lostPoint += (3 + sameCount - 5);
+					}
+				}
+			};
+
+			// LEVEL2
+
+			for (var row = 0; row < moduleCount - 1; row += 1) {
+				for (var col = 0; col < moduleCount - 1; col += 1) {
+					var count = 0;
+					if (qrcode.isDark(row, col) ) count += 1;
+					if (qrcode.isDark(row + 1, col) ) count += 1;
+					if (qrcode.isDark(row, col + 1) ) count += 1;
+					if (qrcode.isDark(row + 1, col + 1) ) count += 1;
+					if (count == 0 || count == 4) {
+						lostPoint += 3;
+					}
+				}
+			}
+
+			// LEVEL3
+
+			for (var row = 0; row < moduleCount; row += 1) {
+				for (var col = 0; col < moduleCount - 6; col += 1) {
+					if (qrcode.isDark(row, col)
+							&& !qrcode.isDark(row, col + 1)
+							&&  qrcode.isDark(row, col + 2)
+							&&  qrcode.isDark(row, col + 3)
+							&&  qrcode.isDark(row, col + 4)
+							&& !qrcode.isDark(row, col + 5)
+							&&  qrcode.isDark(row, col + 6) ) {
+						lostPoint += 40;
+					}
+				}
+			}
+
+			for (var col = 0; col < moduleCount; col += 1) {
+				for (var row = 0; row < moduleCount - 6; row += 1) {
+					if (qrcode.isDark(row, col)
+							&& !qrcode.isDark(row + 1, col)
+							&&  qrcode.isDark(row + 2, col)
+							&&  qrcode.isDark(row + 3, col)
+							&&  qrcode.isDark(row + 4, col)
+							&& !qrcode.isDark(row + 5, col)
+							&&  qrcode.isDark(row + 6, col) ) {
+						lostPoint += 40;
+					}
+				}
+			}
+
+			// LEVEL4
+
+			var darkCount = 0;
+
+			for (var col = 0; col < moduleCount; col += 1) {
+				for (var row = 0; row < moduleCount; row += 1) {
+					if (qrcode.isDark(row, col) ) {
+						darkCount += 1;
+					}
+				}
+			}
+
+			var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5;
+			lostPoint += ratio * 10;
+
+			return lostPoint;
+		};
+
+		return _this;
+	}();
+
+	//---------------------------------------------------------------------
+	// QRMath
+	//---------------------------------------------------------------------
+
+	var QRMath = function() {
+
+		var EXP_TABLE = new Array(256);
+		var LOG_TABLE = new Array(256);
+
+		// initialize tables
+		for (var i = 0; i < 8; i += 1) {
+			EXP_TABLE[i] = 1 << i;
+		}
+		for (var i = 8; i < 256; i += 1) {
+			EXP_TABLE[i] = EXP_TABLE[i - 4]
+				^ EXP_TABLE[i - 5]
+				^ EXP_TABLE[i - 6]
+				^ EXP_TABLE[i - 8];
+		}
+		for (var i = 0; i < 255; i += 1) {
+			LOG_TABLE[EXP_TABLE[i] ] = i;
+		}
+
+		var _this = {};
+
+		_this.glog = function(n) {
+
+			if (n < 1) {
+				throw new Error('glog(' + n + ')');
+			}
+
+			return LOG_TABLE[n];
+		};
+
+		_this.gexp = function(n) {
+
+			while (n < 0) {
+				n += 255;
+			}
+
+			while (n >= 256) {
+				n -= 255;
+			}
+
+			return EXP_TABLE[n];
+		};
+
+		return _this;
+	}();
+
+	//---------------------------------------------------------------------
+	// qrPolynomial
+	//---------------------------------------------------------------------
+
+	function qrPolynomial(num, shift) {
+
+		if (typeof num.length == 'undefined') {
+			throw new Error(num.length + '/' + shift);
+		}
+
+		var _num = function() {
+			var offset = 0;
+			while (offset < num.length && num[offset] == 0) {
+				offset += 1;
+			}
+			var _num = new Array(num.length - offset + shift);
+			for (var i = 0; i < num.length - offset; i += 1) {
+				_num[i] = num[i + offset];
+			}
+			return _num;
+		}();
+
+		var _this = {};
+
+		_this.getAt = function(index) {
+			return _num[index];
+		};
+
+		_this.getLength = function() {
+			return _num.length;
+		};
+
+		_this.multiply = function(e) {
+
+			var num = new Array(_this.getLength() + e.getLength() - 1);
+
+			for (var i = 0; i < _this.getLength(); i += 1) {
+				for (var j = 0; j < e.getLength(); j += 1) {
+					num[i + j] ^= QRMath.gexp(QRMath.glog(_this.getAt(i) ) + QRMath.glog(e.getAt(j) ) );
+				}
+			}
+
+			return qrPolynomial(num, 0);
+		};
+
+		_this.mod = function(e) {
+
+			if (_this.getLength() - e.getLength() < 0) {
+				return _this;
+			}
+
+			var ratio = QRMath.glog(_this.getAt(0) ) - QRMath.glog(e.getAt(0) );
+
+			var num = new Array(_this.getLength() );
+			for (var i = 0; i < _this.getLength(); i += 1) {
+				num[i] = _this.getAt(i);
+			}
+
+			for (var i = 0; i < e.getLength(); i += 1) {
+				num[i] ^= QRMath.gexp(QRMath.glog(e.getAt(i) ) + ratio);
+			}
+
+			// recursive call
+			return qrPolynomial(num, 0).mod(e);
+		};
+
+		return _this;
+	};
+
+	//---------------------------------------------------------------------
+	// QRRSBlock
+	//---------------------------------------------------------------------
+
+	var QRRSBlock = function() {
+
+		var RS_BLOCK_TABLE = [
+
+			// L
+			// M
+			// Q
+			// H
+
+			// 1
+			[1, 26, 19],
+			[1, 26, 16],
+			[1, 26, 13],
+			[1, 26, 9],
+
+			// 2
+			[1, 44, 34],
+			[1, 44, 28],
+			[1, 44, 22],
+			[1, 44, 16],
+
+			// 3
+			[1, 70, 55],
+			[1, 70, 44],
+			[2, 35, 17],
+			[2, 35, 13],
+
+			// 4
+			[1, 100, 80],
+			[2, 50, 32],
+			[2, 50, 24],
+			[4, 25, 9],
+
+			// 5
+			[1, 134, 108],
+			[2, 67, 43],
+			[2, 33, 15, 2, 34, 16],
+			[2, 33, 11, 2, 34, 12],
+
+			// 6
+			[2, 86, 68],
+			[4, 43, 27],
+			[4, 43, 19],
+			[4, 43, 15],
+
+			// 7
+			[2, 98, 78],
+			[4, 49, 31],
+			[2, 32, 14, 4, 33, 15],
+			[4, 39, 13, 1, 40, 14],
+
+			// 8
+			[2, 121, 97],
+			[2, 60, 38, 2, 61, 39],
+			[4, 40, 18, 2, 41, 19],
+			[4, 40, 14, 2, 41, 15],
+
+			// 9
+			[2, 146, 116],
+			[3, 58, 36, 2, 59, 37],
+			[4, 36, 16, 4, 37, 17],
+			[4, 36, 12, 4, 37, 13],
+
+			// 10
+			[2, 86, 68, 2, 87, 69],
+			[4, 69, 43, 1, 70, 44],
+			[6, 43, 19, 2, 44, 20],
+			[6, 43, 15, 2, 44, 16]
+		];
+
+		var qrRSBlock = function(totalCount, dataCount) {
+			var _this = {};
+			_this.totalCount = totalCount;
+			_this.dataCount = dataCount;
+			return _this;
+		};
+
+		var _this = {};
+
+		var getRsBlockTable = function(typeNumber, errorCorrectLevel) {
+
+			switch(errorCorrectLevel) {
+			case QRErrorCorrectLevel.L :
+				return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0];
+			case QRErrorCorrectLevel.M :
+				return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1];
+			case QRErrorCorrectLevel.Q :
+				return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2];
+			case QRErrorCorrectLevel.H :
+				return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3];
+			default :
+				return undefined;
+			}
+		};
+
+		_this.getRSBlocks = function(typeNumber, errorCorrectLevel) {
+
+			var rsBlock = getRsBlockTable(typeNumber, errorCorrectLevel);
+
+			if (typeof rsBlock == 'undefined') {
+				throw new Error('bad rs block @ typeNumber:' + typeNumber +
+						'/errorCorrectLevel:' + errorCorrectLevel);
+			}
+
+			var length = rsBlock.length / 3;
+
+			var list = new Array();
+
+			for (var i = 0; i < length; i += 1) {
+
+				var count = rsBlock[i * 3 + 0];
+				var totalCount = rsBlock[i * 3 + 1];
+				var dataCount = rsBlock[i * 3 + 2];
+
+				for (var j = 0; j < count; j += 1) {
+					list.push(qrRSBlock(totalCount, dataCount) );
+				}
+			}
+
+			return list;
+		};
+
+		return _this;
+	}();
+
+        // jryans: Add module support
+        exports.QRRSBlock = QRRSBlock;
+
+	//---------------------------------------------------------------------
+	// qrBitBuffer
+	//---------------------------------------------------------------------
+
+	var qrBitBuffer = function() {
+
+		var _buffer = new Array();
+		var _length = 0;
+
+		var _this = {};
+
+		_this.getBuffer = function() {
+			return _buffer;
+		};
+
+		_this.getAt = function(index) {
+			var bufIndex = Math.floor(index / 8);
+			return ( (_buffer[bufIndex] >>> (7 - index % 8) ) & 1) == 1;
+		};
+
+		_this.put = function(num, length) {
+			for (var i = 0; i < length; i += 1) {
+				_this.putBit( ( (num >>> (length - i - 1) ) & 1) == 1);
+			}
+		};
+
+		_this.getLengthInBits = function() {
+			return _length;
+		};
+
+		_this.putBit = function(bit) {
+
+			var bufIndex = Math.floor(_length / 8);
+			if (_buffer.length <= bufIndex) {
+				_buffer.push(0);
+			}
+
+			if (bit) {
+				_buffer[bufIndex] |= (0x80 >>> (_length % 8) );
+			}
+
+			_length += 1;
+		};
+
+		return _this;
+	};
+
+	//---------------------------------------------------------------------
+	// qr8BitByte
+	//---------------------------------------------------------------------
+
+	var qr8BitByte = function(data) {
+
+		var _mode = QRMode.MODE_8BIT_BYTE;
+		var _data = data;
+		var _bytes = qrcode.stringToBytes(data);
+
+		var _this = {};
+
+		_this.getMode = function() {
+			return _mode;
+		};
+
+		_this.getLength = function(buffer) {
+			return _bytes.length;
+		};
+
+		_this.write = function(buffer) {
+			for (var i = 0; i < _bytes.length; i += 1) {
+				buffer.put(_bytes[i], 8);
+			}
+		};
+
+		return _this;
+	};
+
+	//=====================================================================
+	// GIF Support etc.
+	//
+
+	//---------------------------------------------------------------------
+	// byteArrayOutputStream
+	//---------------------------------------------------------------------
+
+	var byteArrayOutputStream = function() {
+
+		var _bytes = new Array();
+
+		var _this = {};
+
+		_this.writeByte = function(b) {
+			_bytes.push(b & 0xff);
+		};
+
+		_this.writeShort = function(i) {
+			_this.writeByte(i);
+			_this.writeByte(i >>> 8);
+		};
+
+		_this.writeBytes = function(b, off, len) {
+			off = off || 0;
+			len = len || b.length;
+			for (var i = 0; i < len; i += 1) {
+				_this.writeByte(b[i + off]);
+			}
+		};
+
+		_this.writeString = function(s) {
+			for (var i = 0; i < s.length; i += 1) {
+				_this.writeByte(s.charCodeAt(i) );
+			}
+		};
+
+		_this.toByteArray = function() {
+			return _bytes;
+		};
+
+		_this.toString = function() {
+			var s = '';
+			s += '[';
+			for (var i = 0; i < _bytes.length; i += 1) {
+				if (i > 0) {
+					s += ',';
+				}
+				s += _bytes[i];
+			}
+			s += ']';
+			return s;
+		};
+
+		return _this;
+	};
+
+	//---------------------------------------------------------------------
+	// base64EncodeOutputStream
+	//---------------------------------------------------------------------
+
+	var base64EncodeOutputStream = function() {
+
+		var _buffer = 0;
+		var _buflen = 0;
+		var _length = 0;
+		var _base64 = '';
+
+		var _this = {};
+
+		var writeEncoded = function(b) {
+			_base64 += String.fromCharCode(encode(b & 0x3f) );
+		};
+
+		var encode = function(n) {
+			if (n < 0) {
+				// error.
+			} else if (n < 26) {
+				return 0x41 + n;
+			} else if (n < 52) {
+				return 0x61 + (n - 26);
+			} else if (n < 62) {
+				return 0x30 + (n - 52);
+			} else if (n == 62) {
+				return 0x2b;
+			} else if (n == 63) {
+				return 0x2f;
+			}
+			throw new Error('n:' + n);
+		};
+
+		_this.writeByte = function(n) {
+
+			_buffer = (_buffer << 8) | (n & 0xff);
+			_buflen += 8;
+			_length += 1;
+
+			while (_buflen >= 6) {
+				writeEncoded(_buffer >>> (_buflen - 6) );
+				_buflen -= 6;
+			}
+		};
+
+		_this.flush = function() {
+
+			if (_buflen > 0) {
+				writeEncoded(_buffer << (6 - _buflen) );
+				_buffer = 0;
+				_buflen = 0;
+			}
+
+			if (_length % 3 != 0) {
+				// padding
+				var padlen = 3 - _length % 3;
+				for (var i = 0; i < padlen; i += 1) {
+					_base64 += '=';
+				}
+			}
+		};
+
+		_this.toString = function() {
+			return _base64;
+		};
+
+		return _this;
+	};
+
+	//---------------------------------------------------------------------
+	// base64DecodeInputStream
+	//---------------------------------------------------------------------
+
+	var base64DecodeInputStream = function(str) {
+
+		var _str = str;
+		var _pos = 0;
+		var _buffer = 0;
+		var _buflen = 0;
+
+		var _this = {};
+
+		_this.read = function() {
+
+			while (_buflen < 8) {
+
+				if (_pos >= _str.length) {
+					if (_buflen == 0) {
+						return -1;
+					}
+					throw new Error('unexpected end of file./' + _buflen);
+				}
+
+				var c = _str.charAt(_pos);
+				_pos += 1;
+
+				if (c == '=') {
+					_buflen = 0;
+					return -1;
+				} else if (c.match(/^\s$/) ) {
+					// ignore if whitespace.
+					continue;
+				}
+
+				_buffer = (_buffer << 6) | decode(c.charCodeAt(0) );
+				_buflen += 6;
+			}
+
+			var n = (_buffer >>> (_buflen - 8) ) & 0xff;
+			_buflen -= 8;
+			return n;
+		};
+
+		var decode = function(c) {
+			if (0x41 <= c && c <= 0x5a) {
+				return c - 0x41;
+			} else if (0x61 <= c && c <= 0x7a) {
+				return c - 0x61 + 26;
+			} else if (0x30 <= c && c <= 0x39) {
+				return c - 0x30 + 52;
+			} else if (c == 0x2b) {
+				return 62;
+			} else if (c == 0x2f) {
+				return 63;
+			} else {
+				throw new Error('c:' + c);
+			}
+		};
+
+		return _this;
+	};
+
+	//---------------------------------------------------------------------
+	// gifImage (B/W)
+	//---------------------------------------------------------------------
+
+	var gifImage = function(width, height) {
+
+		var _width = width;
+		var _height = height;
+		var _data = new Array(width * height);
+
+		var _this = {};
+
+		_this.setPixel = function(x, y, pixel) {
+			_data[y * _width + x] = pixel;
+		};
+
+		_this.write = function(out) {
+
+			//---------------------------------
+			// GIF Signature
+
+			out.writeString('GIF87a');
+
+			//---------------------------------
+			// Screen Descriptor
+
+			out.writeShort(_width);
+			out.writeShort(_height);
+
+			out.writeByte(0x80); // 2bit
+			out.writeByte(0);
+			out.writeByte(0);
+
+			//---------------------------------
+			// Global Color Map
+
+			// black
+			out.writeByte(0x00);
+			out.writeByte(0x00);
+			out.writeByte(0x00);
+
+			// white
+			out.writeByte(0xff);
+			out.writeByte(0xff);
+			out.writeByte(0xff);
+
+			//---------------------------------
+			// Image Descriptor
+
+			out.writeString(',');
+			out.writeShort(0);
+			out.writeShort(0);
+			out.writeShort(_width);
+			out.writeShort(_height);
+			out.writeByte(0);
+
+			//---------------------------------
+			// Local Color Map
+
+			//---------------------------------
+			// Raster Data
+
+			var lzwMinCodeSize = 2;
+			var raster = getLZWRaster(lzwMinCodeSize);
+
+			out.writeByte(lzwMinCodeSize);
+
+			var offset = 0;
+
+			while (raster.length - offset > 255) {
+				out.writeByte(255);
+				out.writeBytes(raster, offset, 255);
+				offset += 255;
+			}
+
+			out.writeByte(raster.length - offset);
+			out.writeBytes(raster, offset, raster.length - offset);
+			out.writeByte(0x00);
+
+			//---------------------------------
+			// GIF Terminator
+			out.writeString(';');
+		};
+
+		var bitOutputStream = function(out) {
+
+			var _out = out;
+			var _bitLength = 0;
+			var _bitBuffer = 0;
+
+			var _this = {};
+
+			_this.write = function(data, length) {
+
+				if ( (data >>> length) != 0) {
+					throw new Error('length over');
+				}
+
+				while (_bitLength + length >= 8) {
+					_out.writeByte(0xff & ( (data << _bitLength) | _bitBuffer) );
+					length -= (8 - _bitLength);
+					data >>>= (8 - _bitLength);
+					_bitBuffer = 0;
+					_bitLength = 0;
+				}
+
+				_bitBuffer = (data << _bitLength) | _bitBuffer;
+				_bitLength = _bitLength + length;
+			};
+
+			_this.flush = function() {
+				if (_bitLength > 0) {
+					_out.writeByte(_bitBuffer);
+				}
+			};
+
+			return _this;
+		};
+
+		var getLZWRaster = function(lzwMinCodeSize) {
+
+			var clearCode = 1 << lzwMinCodeSize;
+			var endCode = (1 << lzwMinCodeSize) + 1;
+			var bitLength = lzwMinCodeSize + 1;
+
+			// Setup LZWTable
+			var table = lzwTable();
+
+			for (var i = 0; i < clearCode; i += 1) {
+				table.add(String.fromCharCode(i) );
+			}
+			table.add(String.fromCharCode(clearCode) );
+			table.add(String.fromCharCode(endCode) );
+
+			var byteOut = byteArrayOutputStream();
+			var bitOut = bitOutputStream(byteOut);
+
+			// clear code
+			bitOut.write(clearCode, bitLength);
+
+			var dataIndex = 0;
+
+			var s = String.fromCharCode(_data[dataIndex]);
+			dataIndex += 1;
+
+			while (dataIndex < _data.length) {
+
+				var c = String.fromCharCode(_data[dataIndex]);
+				dataIndex += 1;
+
+				if (table.contains(s + c) ) {
+
+					s = s + c;
+
+				} else {
+
+					bitOut.write(table.indexOf(s), bitLength);
+
+					if (table.size() < 0xfff) {
+
+						if (table.size() == (1 << bitLength) ) {
+							bitLength += 1;
+						}
+
+						table.add(s + c);
+					}
+
+					s = c;
+				}
+			}
+
+			bitOut.write(table.indexOf(s), bitLength);
+
+			// end code
+			bitOut.write(endCode, bitLength);
+
+			bitOut.flush();
+
+			return byteOut.toByteArray();
+		};
+
+		var lzwTable = function() {
+
+			var _map = {};
+			var _size = 0;
+
+			var _this = {};
+
+			_this.add = function(key) {
+				if (_this.contains(key) ) {
+					throw new Error('dup key:' + key);
+				}
+				_map[key] = _size;
+				_size += 1;
+			};
+
+			_this.size = function() {
+				return _size;
+			};
+
+			_this.indexOf = function(key) {
+				return _map[key];
+			};
+
+			_this.contains = function(key) {
+				return typeof _map[key] != 'undefined';
+			};
+
+			return _this;
+		};
+
+		return _this;
+	};
+
+        var createGifData = function(width, height, getPixel) {
+
+        	var gif = gifImage(width, height);
+		for (var y = 0; y < height; y += 1) {
+			for (var x = 0; x < width; x += 1) {
+				gif.setPixel(x, y, getPixel(x, y) );
+			}
+		}
+
+		var b = byteArrayOutputStream();
+		gif.write(b);
+
+		var base64 = base64EncodeOutputStream();
+		var bytes = b.toByteArray();
+		for (var i = 0; i < bytes.length; i += 1) {
+			base64.writeByte(bytes[i]);
+		}
+		base64.flush();
+                return base64;
+
+        };
+
+	var createImgTag = function(width, height, getPixel, alt) {
+
+                var base64 = createGifData(width, height, getPixel);
+		var img = '';
+		img += '<img';
+		img += '\u0020src="';
+		img += 'data:image/gif;base64,';
+		img += base64;
+		img += '"';
+		img += '\u0020width="';
+		img += width;
+		img += '"';
+		img += '\u0020height="';
+		img += height;
+		img += '"';
+		if (alt) {
+			img += '\u0020alt="';
+			img += alt;
+			img += '"';
+		}
+		img += '/>';
+
+		return img;
+	};
+
+	//---------------------------------------------------------------------
+	// returns qrcode function.
+
+	return qrcode;
+}();
+
+// jryans: Add module support
+exports.Encoder = qrcode;
copy from toolkit/devtools/moz.build
copy to toolkit/devtools/qrcode/encoder/moz.build
--- a/toolkit/devtools/moz.build
+++ b/toolkit/devtools/qrcode/encoder/moz.build
@@ -1,20 +1,11 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-PARALLEL_DIRS += [
-    'server',
-    'client',
-    'gcli',
-    'sourcemap',
-    'webconsole',
-    'apps',
-    'styleinspector',
-    'acorn',
-    'pretty-fast'
-]
-
-MOCHITEST_CHROME_MANIFESTS += ['tests/mochitest/chrome.ini']
-XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+JS_MODULES_PATH = 'modules/devtools/qrcode/encoder'
+
+EXTRA_JS_MODULES += [
+    'index.js',
+]
copy from toolkit/devtools/moz.build
copy to toolkit/devtools/qrcode/moz.build
--- a/toolkit/devtools/moz.build
+++ b/toolkit/devtools/qrcode/moz.build
@@ -1,20 +1,17 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 PARALLEL_DIRS += [
-    'server',
-    'client',
-    'gcli',
-    'sourcemap',
-    'webconsole',
-    'apps',
-    'styleinspector',
-    'acorn',
-    'pretty-fast'
+    'encoder'
 ]
 
-MOCHITEST_CHROME_MANIFESTS += ['tests/mochitest/chrome.ini']
+JS_MODULES_PATH = 'modules/devtools/qrcode'
+
+EXTRA_JS_MODULES += [
+    'qrcode.js',
+]
+
 XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/qrcode/qrcode.js
@@ -0,0 +1,62 @@
+/* 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";
+
+let { Encoder, QRRSBlock, QRErrorCorrectLevel } = require("./encoder/index");
+
+/**
+ * There are many "versions" of QR codes, which describes how many dots appear
+ * in the resulting image, thus limiting the amount of data that can be
+ * represented.
+ *
+ * The encoder used here allows for versions 1 - 10 (more dots for larger
+ * versions).
+ *
+ * It expects you to pick a version large enough to contain your message.  Here
+ * we search for the mimimum version based on the message length.
+ * @param string message
+ *        Text to encode
+ * @param string quality
+ *        Quality level: L, M, Q, H
+ * @return integer
+ */
+exports.findMinimumVersion = function(message, quality) {
+  let msgLength = message.length;
+  let qualityLevel = QRErrorCorrectLevel[quality];
+  for (let version = 1; version <= 10; version++) {
+    let rsBlocks = QRRSBlock.getRSBlocks(version, qualityLevel);
+    let maxLength = rsBlocks.reduce((prev, block) => {
+      return prev + block.dataCount;
+    }, 0);
+    // Remove two bytes to fit header info
+    maxLength -= 2;
+    if (msgLength <= maxLength) {
+      return version;
+    }
+  }
+  throw new Error("Message too large");
+};
+
+/**
+ * Simple wrapper around the underlying encoder's API.
+ * @param string  message
+ *        Text to encode
+ * @param string  quality (optional)
+          Quality level: L, M, Q, H
+ * @param integer version (optional)
+ *        QR code "version" large enough to contain the message
+ * @return object with the following fields:
+ *   * src:    an image encoded a data URI
+ *   * height: image height
+ *   * width:  image width
+ */
+exports.encodeToDataURI = function(message, quality, version) {
+  quality = quality || "H";
+  version = version || exports.findMinimumVersion(message, quality);
+  let encoder = new Encoder(version, quality);
+  encoder.addData(message);
+  encoder.make();
+  return encoder.createImgData();
+};
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/qrcode/tests/unit/test_encode.js
@@ -0,0 +1,28 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+/**
+ * Test encoding a simple message.
+ */
+
+const { utils: Cu } = Components;
+const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+const { require } = devtools;
+
+const QR = require("devtools/toolkit/qrcode/qrcode");
+
+function run_test() {
+  let imgData = QR.encodeToDataURI("HELLO", "L");
+  do_check_eq(imgData.src,
+              "data:image/gif;base64,R0lGODdhOgA6AIAAAAAAAP///ywAAAAAOgA6AAAC" +
+              "/4yPqcvtD6OctNqLs968+w+G4gKU5nkaKKquLuW+QVy2tAkDTj3rfQts8CRDko" +
+              "+HPPoYRUgy9YsyldDm44mLWhHYZM6W7WaDqyCRGkZDySxpRGw2sqvLt1q5w/fo" +
+              "XyE6vnUQOJUHBlinMGh046V1F5PDqNcoqcgBOWKBKbK2N+aY+Ih49VkmqMcl2l" +
+              "dkhZUK1umE6jZXJ2ZJaujZaRqH4bpb2uZrJxvIt4Ebe9qoYYrJOsw8apz2bCut" +
+              "m9kqDcw52uuImyr5Oh1KXH1jrn2anuunywtODU/o2c6teceW39ZcLFg/fNMo1b" +
+              "t3jVw2dwTPwJq1KYG3gAklCgu37yGxeScYKyiCc+7DR34hPVQiuQ7UhJMagyEb" +
+              "lymmzJk0a9q8iTOnzp0NCgAAOw==");
+  do_check_eq(imgData.width, 58);
+  do_check_eq(imgData.height, 58);
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/qrcode/tests/unit/xpcshell.ini
@@ -0,0 +1,5 @@
+[DEFAULT]
+head =
+tail =
+
+[test_encode.js]
--- a/toolkit/mozapps/extensions/test/browser/head.js
+++ b/toolkit/mozapps/extensions/test/browser/head.js
@@ -142,16 +142,22 @@ function log_exceptions(aCallback, ...aA
     return aCallback.apply(null, aArgs);
   }
   catch (e) {
     info("Exception thrown: " + e);
     throw e;
   }
 }
 
+function log_callback(aPromise, aCallback) {
+  aPromise.then(aCallback)
+    .then(null, e => info("Exception thrown: " + e));
+  return aPromise;
+}
+
 function add_test(test) {
   gPendingTests.push(test);
 }
 
 function run_next_test() {
   if (gTestsRun > 0)
     info("Test " + gTestsRun + " took " + (Date.now() - gTestStart) + "ms");
 
@@ -278,97 +284,92 @@ function wait_for_manager_load(aManagerW
   info("Waiting for initialization");
   aManagerWindow.document.addEventListener("Initialized", function() {
     aManagerWindow.document.removeEventListener("Initialized", arguments.callee, false);
     log_exceptions(aCallback, aManagerWindow);
   }, false);
 }
 
 function open_manager(aView, aCallback, aLoadCallback, aLongerTimeout) {
-  let deferred = Promise.defer();
+  let p = new Promise((resolve, reject) => {
 
-  function setup_manager(aManagerWindow) {
-    if (aLoadCallback)
-      log_exceptions(aLoadCallback, aManagerWindow);
+    function setup_manager(aManagerWindow) {
+      if (aLoadCallback)
+        log_exceptions(aLoadCallback, aManagerWindow);
+
+      if (aView)
+        aManagerWindow.loadView(aView);
 
-    if (aView)
-      aManagerWindow.loadView(aView);
+      ok(aManagerWindow != null, "Should have an add-ons manager window");
+      is(aManagerWindow.location, MANAGER_URI, "Should be displaying the correct UI");
 
-    ok(aManagerWindow != null, "Should have an add-ons manager window");
-    is(aManagerWindow.location, MANAGER_URI, "Should be displaying the correct UI");
+      waitForFocus(function() {
+        info("window has focus, waiting for manager load");
+        wait_for_manager_load(aManagerWindow, function() {
+          info("Manager waiting for view load");
+          wait_for_view_load(aManagerWindow, function() {
+            resolve(aManagerWindow);
+          }, null, aLongerTimeout);
+        });
+      }, aManagerWindow);
+    }
 
-    waitForFocus(function() {
-      wait_for_manager_load(aManagerWindow, function() {
-        wait_for_view_load(aManagerWindow, function() {
-          // Some functions like synthesizeMouse don't like to be called during
-          // the load event so ensure that has completed
-          executeSoon(function() {
-            if (aCallback) {
-              log_exceptions(aCallback, aManagerWindow);
-            }
-            deferred.resolve(aManagerWindow);
-          });
-        }, null, aLongerTimeout);
-      });
-    }, aManagerWindow);
-  }
+    if (gUseInContentUI) {
+      info("Loading manager window in tab");
+      Services.obs.addObserver(function (aSubject, aTopic, aData) {
+        Services.obs.removeObserver(arguments.callee, aTopic);
+        if (aSubject.location.href != MANAGER_URI) {
+          info("Ignoring load event for " + aSubject.location.href);
+          return;
+        }
+        setup_manager(aSubject);
+      }, "EM-loaded", false);
 
-  if (gUseInContentUI) {
-    gBrowser.selectedTab = gBrowser.addTab();
-    switchToTabHavingURI(MANAGER_URI, true);
+      gBrowser.selectedTab = gBrowser.addTab();
+      switchToTabHavingURI(MANAGER_URI, true);
+    } else {
+      info("Loading manager window in dialog");
+      Services.obs.addObserver(function (aSubject, aTopic, aData) {
+        Services.obs.removeObserver(arguments.callee, aTopic);
+        setup_manager(aSubject);
+      }, "EM-loaded", false);
 
-    // This must be a new load, else the ping/pong would have
-    // found the window above.
-    Services.obs.addObserver(function (aSubject, aTopic, aData) {
-      Services.obs.removeObserver(arguments.callee, aTopic);
-      if (aSubject.location.href != MANAGER_URI)
-        return;
-      setup_manager(aSubject);
-    }, "EM-loaded", false);
-    return deferred.promise;
-  }
+      openDialog(MANAGER_URI);
+    }
+  });
 
-  openDialog(MANAGER_URI);
-  Services.obs.addObserver(function (aSubject, aTopic, aData) {
-    Services.obs.removeObserver(arguments.callee, aTopic);
-    setup_manager(aSubject);
-  }, "EM-loaded", false);
-
-  return deferred.promise;
+  return log_callback(p, aCallback);
 }
 
 function close_manager(aManagerWindow, aCallback, aLongerTimeout) {
-  let deferred = Promise.defer();
-  requestLongerTimeout(aLongerTimeout ? aLongerTimeout : 2);
+  let p = new Promise((resolve, reject) => {
+    requestLongerTimeout(aLongerTimeout ? aLongerTimeout : 2);
 
-  ok(aManagerWindow != null, "Should have an add-ons manager window to close");
-  is(aManagerWindow.location, MANAGER_URI, "Should be closing window with correct URI");
+    ok(aManagerWindow != null, "Should have an add-ons manager window to close");
+    is(aManagerWindow.location, MANAGER_URI, "Should be closing window with correct URI");
 
-  aManagerWindow.addEventListener("unload", function() {
-    this.removeEventListener("unload", arguments.callee, false);
-    if (aCallback) {
-      log_exceptions(aCallback);
-    }
-    deferred.resolve();
-  }, false);
+    aManagerWindow.addEventListener("unload", function() {
+      info("Manager window unloaded");
+      this.removeEventListener("unload", arguments.callee, false);
+      resolve();
+    }, false);
+  });
 
   aManagerWindow.close();
 
-  return deferred.promise;
+  return log_callback(p, aCallback);
 }
 
 function restart_manager(aManagerWindow, aView, aCallback, aLoadCallback) {
   if (!aManagerWindow) {
-    open_manager(aView, aCallback, aLoadCallback);
-    return;
+    return open_manager(aView, aCallback, aLoadCallback);
   }
 
-  close_manager(aManagerWindow, function() {
-    open_manager(aView, aCallback, aLoadCallback);
-  });
+  return close_manager(aManagerWindow)
+    .then(() => open_manager(aView, aCallback, aLoadCallback));
 }
 
 function wait_for_window_open(aCallback) {
   Services.wm.addListener({
     onOpenWindow: function(aWindow) {
       Services.wm.removeListener(this);
 
       let domwindow = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
@@ -432,35 +433,27 @@ function is_element_hidden(aElement, aMs
 }
 
 /**
  * Install an add-on and call a callback when complete.
  *
  * The callback will receive the Addon for the installed add-on.
  */
 function install_addon(path, cb, pathPrefix=TESTROOT) {
-  let deferred = Promise.defer();
-
-  AddonManager.getInstallForURL(pathPrefix + path, (install) => {
-    install.addListener({
-      onInstallEnded: () => {
-        executeSoon(() => {
-          if (cb) {
-            cb(install.addon);
-          }
+  let p = new Promise((resolve, reject) => {
+    AddonManager.getInstallForURL(pathPrefix + path, (install) => {
+      install.addListener({
+        onInstallEnded: () => resolve(install.addon),
+      });
 
-          deferred.resolve(install.addon);
-        });
-      },
-    });
+      install.install();
+    }, "application/x-xpinstall");
+  });
 
-    install.install();
-  }, "application/x-xpinstall");
-
-  return deferred.promise;
+  return log_callback(p, cb);
 }
 
 function CategoryUtilities(aManagerWindow) {
   this.window = aManagerWindow;
 
   var self = this;
   this.window.addEventListener("unload", function() {
     self.window.removeEventListener("unload", arguments.callee, false);
@@ -512,32 +505,24 @@ CategoryUtilities.prototype = {
     return !is_hidden(aCategory);
   },
 
   isTypeVisible: function(aCategoryType) {
     return this.isVisible(this.get(aCategoryType));
   },
 
   open: function(aCategory, aCallback) {
-    let deferred = Promise.defer();
 
     isnot(this.window, null, "Should not open category when manager window is not loaded");
     ok(this.isVisible(aCategory), "Category should be visible if attempting to open it");
 
     EventUtils.synthesizeMouse(aCategory, 2, 2, { }, this.window);
+    let p = new Promise((resolve, reject) => wait_for_view_load(this.window, resolve));
 
-    wait_for_view_load(this.window, (win) => {
-      if (aCallback) {
-        log_exceptions(aCallback, win);
-      }
-
-      deferred.resolve(win);
-    });
-
-    return deferred.promise;
+    return log_callback(p, aCallback);
   },
 
   openType: function(aCategoryType, aCallback) {
     return this.open(this.get(aCategoryType), aCallback);
   }
 }
 
 function CertOverrideListener(host, bits) {