Merge m-c to inbound a=merge CLOSED TREE

This commit is contained in:
Wes Kocher 2015-02-26 18:52:43 -08:00
commit 81c5733880
84 changed files with 1946 additions and 369 deletions

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db"> <project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
<copyfile dest="Makefile" src="core/root.mk"/> <copyfile dest="Makefile" src="core/root.mk"/>
</project> </project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="22230c9a340a0a2c3af73320cf47e1cb544517ad"/> <project name="gaia" path="gaia" remote="mozillaorg" revision="7512026a377271a0cade12d70846557f0bc7781c"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/> <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/> <copyfile dest="Makefile" src="core/root.mk"/>
</project> </project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/> <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="22230c9a340a0a2c3af73320cf47e1cb544517ad"/> <project name="gaia.git" path="gaia" remote="mozillaorg" revision="7512026a377271a0cade12d70846557f0bc7781c"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/> <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/> <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>

View File

@ -17,7 +17,7 @@
</project> </project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/> <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="22230c9a340a0a2c3af73320cf47e1cb544517ad"/> <project name="gaia" path="gaia" remote="mozillaorg" revision="7512026a377271a0cade12d70846557f0bc7781c"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/> <project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a1ddea3133e0807350326cee5dcf0d06fad00c08"/> <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a1ddea3133e0807350326cee5dcf0d06fad00c08"/>
@ -135,7 +135,7 @@
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c5f8d282efe4a4e8b1e31a37300944e338e60e4f"/> <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c5f8d282efe4a4e8b1e31a37300944e338e60e4f"/>
<project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/> <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="9f28c4faea3b2f01db227b2467b08aeba96d9bec"/> <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="9f28c4faea3b2f01db227b2467b08aeba96d9bec"/>
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="d19dad5844e8803d5e88d1577a2742b4f5cbc467"/> <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="84395037a7a04546e8ef7cb81572eb516b85562b"/>
<project name="android-sdk" path="sdk" remote="b2g" revision="8b1365af38c9a653df97349ee53a3f5d64fd590a"/> <project name="android-sdk" path="sdk" remote="b2g" revision="8b1365af38c9a653df97349ee53a3f5d64fd590a"/>
<project name="darwinstreamingserver" path="system/darwinstreamingserver" remote="b2g" revision="cf85968c7f85e0ec36e72c87ceb4837a943b8af6"/> <project name="darwinstreamingserver" path="system/darwinstreamingserver" remote="b2g" revision="cf85968c7f85e0ec36e72c87ceb4837a943b8af6"/>
</manifest> </manifest>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db"> <project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
<copyfile dest="Makefile" src="core/root.mk"/> <copyfile dest="Makefile" src="core/root.mk"/>
</project> </project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="22230c9a340a0a2c3af73320cf47e1cb544517ad"/> <project name="gaia" path="gaia" remote="mozillaorg" revision="7512026a377271a0cade12d70846557f0bc7781c"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/> <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="52775e03a2d8532429dff579cb2cd56718e488c3"> <project name="platform_build" path="build" remote="b2g" revision="52775e03a2d8532429dff579cb2cd56718e488c3">
<copyfile dest="Makefile" src="core/root.mk"/> <copyfile dest="Makefile" src="core/root.mk"/>
</project> </project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="22230c9a340a0a2c3af73320cf47e1cb544517ad"/> <project name="gaia" path="gaia" remote="mozillaorg" revision="7512026a377271a0cade12d70846557f0bc7781c"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/> <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/> <copyfile dest="Makefile" src="core/root.mk"/>
</project> </project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/> <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="22230c9a340a0a2c3af73320cf47e1cb544517ad"/> <project name="gaia.git" path="gaia" remote="mozillaorg" revision="7512026a377271a0cade12d70846557f0bc7781c"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/> <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/> <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db"> <project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
<copyfile dest="Makefile" src="core/root.mk"/> <copyfile dest="Makefile" src="core/root.mk"/>
</project> </project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="22230c9a340a0a2c3af73320cf47e1cb544517ad"/> <project name="gaia" path="gaia" remote="mozillaorg" revision="7512026a377271a0cade12d70846557f0bc7781c"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/> <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
@ -146,7 +146,7 @@
<project name="platform/hardware/ril" path="hardware/ril" revision="12b1977cc704b35f2e9db2bb423fa405348bc2f3"/> <project name="platform/hardware/ril" path="hardware/ril" revision="12b1977cc704b35f2e9db2bb423fa405348bc2f3"/>
<project name="platform/system/bluetooth" path="system/bluetooth" revision="985bf15264d865fe7b9c5b45f61c451cbaafa43d"/> <project name="platform/system/bluetooth" path="system/bluetooth" revision="985bf15264d865fe7b9c5b45f61c451cbaafa43d"/>
<project name="platform/system/core" path="system/core" revision="42839aedcf70bf6bc92a3b7ea4a5cc9bf9aef3f9"/> <project name="platform/system/core" path="system/core" revision="42839aedcf70bf6bc92a3b7ea4a5cc9bf9aef3f9"/>
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="d19dad5844e8803d5e88d1577a2742b4f5cbc467"/> <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="84395037a7a04546e8ef7cb81572eb516b85562b"/>
<project name="platform/system/qcom" path="system/qcom" revision="63e3f6f176caad587d42bba4c16b66d953fb23c2"/> <project name="platform/system/qcom" path="system/qcom" revision="63e3f6f176caad587d42bba4c16b66d953fb23c2"/>
<project name="platform/vendor/qcom-opensource/wlan/prima" path="vendor/qcom/opensource/wlan/prima" revision="d8952a42771045fca73ec600e2b42a4c7129d723"/> <project name="platform/vendor/qcom-opensource/wlan/prima" path="vendor/qcom/opensource/wlan/prima" revision="d8952a42771045fca73ec600e2b42a4c7129d723"/>
<project name="platform/vendor/qcom/msm8610" path="device/qcom/msm8610" revision="4c187c1f3a0dffd8e51a961735474ea703535b99"/> <project name="platform/vendor/qcom/msm8610" path="device/qcom/msm8610" revision="4c187c1f3a0dffd8e51a961735474ea703535b99"/>

View File

@ -17,7 +17,7 @@
</project> </project>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/> <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="22230c9a340a0a2c3af73320cf47e1cb544517ad"/> <project name="gaia" path="gaia" remote="mozillaorg" revision="7512026a377271a0cade12d70846557f0bc7781c"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/> <project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a1ddea3133e0807350326cee5dcf0d06fad00c08"/> <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a1ddea3133e0807350326cee5dcf0d06fad00c08"/>
@ -145,7 +145,7 @@
<project name="platform/hardware/ril" path="hardware/ril" revision="c4e2ac95907a5519a0e09f01a0d8e27fec101af0"/> <project name="platform/hardware/ril" path="hardware/ril" revision="c4e2ac95907a5519a0e09f01a0d8e27fec101af0"/>
<project name="platform/system/bluetooth" path="system/bluetooth" revision="e1eb226fa3ad3874ea7b63c56a9dc7012d7ff3c2"/> <project name="platform/system/bluetooth" path="system/bluetooth" revision="e1eb226fa3ad3874ea7b63c56a9dc7012d7ff3c2"/>
<project name="platform/system/core" path="system/core" revision="adc485d8755af6a61641d197de7cfef667722580"/> <project name="platform/system/core" path="system/core" revision="adc485d8755af6a61641d197de7cfef667722580"/>
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="d19dad5844e8803d5e88d1577a2742b4f5cbc467"/> <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="84395037a7a04546e8ef7cb81572eb516b85562b"/>
<project name="platform/system/qcom" path="system/qcom" revision="1cdab258b15258b7f9657da70e6f06ebd5a2fc25"/> <project name="platform/system/qcom" path="system/qcom" revision="1cdab258b15258b7f9657da70e6f06ebd5a2fc25"/>
<project name="platform/vendor/qcom/msm8610" path="device/qcom/msm8610" revision="4ae5df252123591d5b941191790e7abed1bce5a4"/> <project name="platform/vendor/qcom/msm8610" path="device/qcom/msm8610" revision="4ae5df252123591d5b941191790e7abed1bce5a4"/>
<project name="platform/vendor/qcom-opensource/wlan/prima" path="vendor/qcom/opensource/wlan/prima" revision="ce18b47b4a4f93a581d672bbd5cb6d12fe796ca9"/> <project name="platform/vendor/qcom-opensource/wlan/prima" path="vendor/qcom/opensource/wlan/prima" revision="ce18b47b4a4f93a581d672bbd5cb6d12fe796ca9"/>

View File

@ -1,9 +1,9 @@
{ {
"git": { "git": {
"git_revision": "22230c9a340a0a2c3af73320cf47e1cb544517ad", "git_revision": "7512026a377271a0cade12d70846557f0bc7781c",
"remote": "https://git.mozilla.org/releases/gaia.git", "remote": "https://git.mozilla.org/releases/gaia.git",
"branch": "" "branch": ""
}, },
"revision": "298fece36f2564e042e347a48fcc104aca78e3b5", "revision": "53713e514b92cccfd55404cebd2b627cb9804bc2",
"repo_path": "integration/gaia-central" "repo_path": "integration/gaia-central"
} }

View File

@ -17,7 +17,7 @@
</project> </project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/> <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="22230c9a340a0a2c3af73320cf47e1cb544517ad"/> <project name="gaia" path="gaia" remote="mozillaorg" revision="7512026a377271a0cade12d70846557f0bc7781c"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/> <project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a1ddea3133e0807350326cee5dcf0d06fad00c08"/> <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a1ddea3133e0807350326cee5dcf0d06fad00c08"/>
@ -130,7 +130,7 @@
<project name="device-mako" path="device/lge/mako" remote="b2g" revision="78d17f0c117f0c66dd55ee8d5c5dde8ccc93ecba"/> <project name="device-mako" path="device/lge/mako" remote="b2g" revision="78d17f0c117f0c66dd55ee8d5c5dde8ccc93ecba"/>
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="3a9a17613cc685aa232432566ad6cc607eab4ec1"/> <project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="3a9a17613cc685aa232432566ad6cc607eab4ec1"/>
<project name="device/lge/mako-kernel" path="device/lge/mako-kernel" revision="d1729e53d71d711c8fde25eab8728ff2b9b4df0e"/> <project name="device/lge/mako-kernel" path="device/lge/mako-kernel" revision="d1729e53d71d711c8fde25eab8728ff2b9b4df0e"/>
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="d19dad5844e8803d5e88d1577a2742b4f5cbc467"/> <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="84395037a7a04546e8ef7cb81572eb516b85562b"/>
<project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="7d33aaf740bbf6c7c6e9c34a92b371eda311b66b"/> <project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="7d33aaf740bbf6c7c6e9c34a92b371eda311b66b"/>
<project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/> <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/>
<project name="platform/hardware/broadcom/wlan" path="hardware/broadcom/wlan" revision="0e1929fa3aa38bf9d40e9e953d619fab8164c82e"/> <project name="platform/hardware/broadcom/wlan" path="hardware/broadcom/wlan" revision="0e1929fa3aa38bf9d40e9e953d619fab8164c82e"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="52775e03a2d8532429dff579cb2cd56718e488c3"> <project name="platform_build" path="build" remote="b2g" revision="52775e03a2d8532429dff579cb2cd56718e488c3">
<copyfile dest="Makefile" src="core/root.mk"/> <copyfile dest="Makefile" src="core/root.mk"/>
</project> </project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="22230c9a340a0a2c3af73320cf47e1cb544517ad"/> <project name="gaia" path="gaia" remote="mozillaorg" revision="7512026a377271a0cade12d70846557f0bc7781c"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/> <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
@ -141,7 +141,7 @@
<default remote="caf" revision="refs/tags/android-5.0.0_r6" sync-j="4"/> <default remote="caf" revision="refs/tags/android-5.0.0_r6" sync-j="4"/>
<!-- Nexus 5 specific things --> <!-- Nexus 5 specific things -->
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="ba62cc8b78c30d36181b8060a2016cc8da166236"/> <project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="ba62cc8b78c30d36181b8060a2016cc8da166236"/>
<project name="device-hammerhead" path="device/lge/hammerhead" remote="b2g" revision="b765862a60e0063fc2451f5e3e137247dd9838c3"/> <project name="device-hammerhead" path="device/lge/hammerhead" remote="b2g" revision="dc525eeb67301a44f514a7ac40a59ec592b34e31"/>
<project name="device/qcom/common" path="device/qcom/common" remote="caf" revision="3697e5acf25629b82658334e3f8ee3b6df5becab"/> <project name="device/qcom/common" path="device/qcom/common" remote="caf" revision="3697e5acf25629b82658334e3f8ee3b6df5becab"/>
<project name="device_lge_hammerhead-kernel" path="device/lge/hammerhead-kernel" remote="b2g" revision="1268f640184df5ef759ada669f101a613451673a"/> <project name="device_lge_hammerhead-kernel" path="device/lge/hammerhead-kernel" remote="b2g" revision="1268f640184df5ef759ada669f101a613451673a"/>
<project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="0cb8574d338bf9f15b45ace7c08ad6deae9673ee"/> <project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="0cb8574d338bf9f15b45ace7c08ad6deae9673ee"/>
@ -157,5 +157,5 @@
<project name="platform/hardware/qcom/sensors" path="hardware/qcom/sensors" revision="3724fd91ef5183684d97e2bf1d7ff948faabe090"/> <project name="platform/hardware/qcom/sensors" path="hardware/qcom/sensors" revision="3724fd91ef5183684d97e2bf1d7ff948faabe090"/>
<project name="platform/hardware/qcom/wlan" path="hardware/qcom/wlan" revision="2e54754cc0529d26ccac37ed291600048adbf6c0"/> <project name="platform/hardware/qcom/wlan" path="hardware/qcom/wlan" revision="2e54754cc0529d26ccac37ed291600048adbf6c0"/>
<project name="platform/hardware/ril" path="hardware/ril" revision="71dfa8228ad0d6cdf6bac0426ac59404ab74b7f3"/> <project name="platform/hardware/ril" path="hardware/ril" revision="71dfa8228ad0d6cdf6bac0426ac59404ab74b7f3"/>
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="d19dad5844e8803d5e88d1577a2742b4f5cbc467"/> <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="84395037a7a04546e8ef7cb81572eb516b85562b"/>
</manifest> </manifest>

View File

@ -1577,6 +1577,9 @@ pref("devtools.browserconsole.filter.secwarn", true);
// Text size in the Web Console. Use 0 for the system default size. // Text size in the Web Console. Use 0 for the system default size.
pref("devtools.webconsole.fontSize", 0); pref("devtools.webconsole.fontSize", 0);
// Max number of inputs to store in web console history.
pref("devtools.webconsole.inputHistoryCount", 50);
// Persistent logging: |true| if you want the Web Console to keep all of the // Persistent logging: |true| if you want the Web Console to keep all of the
// logged messages after reloading the page, |false| if you want the output to // logged messages after reloading the page, |false| if you want the output to
// be cleared each time page navigation happens. // be cleared each time page navigation happens.

View File

@ -11,13 +11,19 @@ XPCOMUtils.defineLazyModuleGetter(this, "CloudSync",
let CloudSync = null; let CloudSync = null;
#endif #endif
XPCOMUtils.defineLazyModuleGetter(this, "ReadingListScheduler",
"resource:///modules/readinglist/Scheduler.jsm");
// gSyncUI handles updating the tools menu and displaying notifications. // gSyncUI handles updating the tools menu and displaying notifications.
let gSyncUI = { let gSyncUI = {
_obs: ["weave:service:sync:start", _obs: ["weave:service:sync:start",
"weave:service:sync:finish",
"weave:service:sync:error",
"weave:service:quota:remaining", "weave:service:quota:remaining",
"weave:service:setup-complete", "weave:service:setup-complete",
"weave:service:login:start", "weave:service:login:start",
"weave:service:login:finish", "weave:service:login:finish",
"weave:service:login:error",
"weave:service:logout:finish", "weave:service:logout:finish",
"weave:service:start-over", "weave:service:start-over",
"weave:service:start-over:finish", "weave:service:start-over:finish",
@ -25,9 +31,15 @@ let gSyncUI = {
"weave:ui:sync:error", "weave:ui:sync:error",
"weave:ui:sync:finish", "weave:ui:sync:finish",
"weave:ui:clear-error", "weave:ui:clear-error",
"readinglist:sync:start",
"readinglist:sync:finish",
"readinglist:sync:error",
], ],
_unloaded: false, _unloaded: false,
// The number of "active" syncs - while this is non-zero, our button will spin
_numActiveSyncTasks: 0,
init: function () { init: function () {
Cu.import("resource://services-common/stringbundle.js"); Cu.import("resource://services-common/stringbundle.js");
@ -95,21 +107,25 @@ let gSyncUI = {
} }
}, },
_needsSetup: function SUI__needsSetup() { _needsSetup() {
// We want to treat "account needs verification" as "needs setup". So // We want to treat "account needs verification" as "needs setup". So
// "reach in" to Weave.Status._authManager to check whether we the signed-in // "reach in" to Weave.Status._authManager to check whether we the signed-in
// user is verified. // user is verified.
// Referencing Weave.Status spins a nested event loop to initialize the // Referencing Weave.Status spins a nested event loop to initialize the
// authManager, so this should always return a value directly. // authManager, so this should always return a value directly.
// This only applies to fxAccounts-based Sync. // This only applies to fxAccounts-based Sync.
if (Weave.Status._authManager._signedInUser) { if (Weave.Status._authManager._signedInUser !== undefined) {
// If we have a signed in user already, and that user is not verified, // So we are using Firefox accounts - in this world, checking Sync isn't
// revert to the "needs setup" state. // enough as reading list may be configured but not Sync.
if (!Weave.Status._authManager._signedInUser.verified) { // We consider ourselves setup if we have a verified user.
return true; // XXX - later we should consider checking preferences to ensure at least
} // one engine is enabled?
return !Weave.Status._authManager._signedInUser ||
!Weave.Status._authManager._signedInUser.verified;
} }
// So we are using legacy sync, and reading-list isn't supported for such
// users, so check sync itself.
let firstSync = ""; let firstSync = "";
try { try {
firstSync = Services.prefs.getCharPref("services.sync.firstSync"); firstSync = Services.prefs.getCharPref("services.sync.firstSync");
@ -120,7 +136,8 @@ let gSyncUI = {
}, },
_loginFailed: function () { _loginFailed: function () {
return Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED; return Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED ||
ReadingListScheduler.state == ReadingListScheduler.STATE_ERROR_AUTHENTICATION;
}, },
updateUI: function SUI_updateUI() { updateUI: function SUI_updateUI() {
@ -136,6 +153,7 @@ let gSyncUI = {
document.getElementById("sync-syncnow-state").hidden = false; document.getElementById("sync-syncnow-state").hidden = false;
} else if (loginFailed) { } else if (loginFailed) {
document.getElementById("sync-reauth-state").hidden = false; document.getElementById("sync-reauth-state").hidden = false;
this.showLoginError();
} else if (needsSetup) { } else if (needsSetup) {
document.getElementById("sync-setup-state").hidden = false; document.getElementById("sync-setup-state").hidden = false;
} else { } else {
@ -146,14 +164,6 @@ let gSyncUI = {
return; return;
let syncButton = document.getElementById("sync-button"); let syncButton = document.getElementById("sync-button");
if (syncButton) {
syncButton.removeAttribute("status");
}
let panelHorizontalButton = document.getElementById("PanelUI-fxa-status");
if (panelHorizontalButton) {
panelHorizontalButton.removeAttribute("syncstatus");
}
if (needsSetup && syncButton) if (needsSetup && syncButton)
syncButton.removeAttribute("tooltiptext"); syncButton.removeAttribute("tooltiptext");
@ -162,17 +172,45 @@ let gSyncUI = {
// Functions called by observers // Functions called by observers
onActivityStart: function SUI_onActivityStart() { onActivityStart() {
if (!gBrowser) if (!gBrowser)
return; return;
let button = document.getElementById("sync-button"); this.log.debug("onActivityStart with numActive", this._numActiveSyncTasks);
if (button) { if (++this._numActiveSyncTasks == 1) {
button.setAttribute("status", "active"); let button = document.getElementById("sync-button");
if (button) {
button.setAttribute("status", "active");
}
button = document.getElementById("PanelUI-fxa-status");
if (button) {
button.setAttribute("syncstatus", "active");
}
} }
button = document.getElementById("PanelUI-fxa-status"); },
if (button) {
button.setAttribute("syncstatus", "active"); onActivityStop() {
if (!gBrowser)
return;
this.log.debug("onActivityStop with numActive", this._numActiveSyncTasks);
if (--this._numActiveSyncTasks) {
if (this._numActiveSyncTasks < 0) {
// This isn't particularly useful (it seems more likely we'll set a
// "start" without a "stop" meaning it forever remains > 0) but it
// might offer some value...
this.log.error("mismatched onActivityStart/Stop calls",
new Error("active=" + this._numActiveSyncTasks));
}
return; // active tasks are still ongoing...
}
let syncButton = document.getElementById("sync-button");
if (syncButton) {
syncButton.removeAttribute("status");
}
let panelHorizontalButton = document.getElementById("PanelUI-fxa-status");
if (panelHorizontalButton) {
panelHorizontalButton.removeAttribute("syncstatus");
} }
}, },
@ -187,6 +225,7 @@ let gSyncUI = {
}, },
onLoginError: function SUI_onLoginError() { onLoginError: function SUI_onLoginError() {
// Note: This is used for *both* Sync and ReadingList login errors.
// if login fails, any other notifications are essentially moot // if login fails, any other notifications are essentially moot
Weave.Notifications.removeAll(); Weave.Notifications.removeAll();
@ -200,11 +239,18 @@ let gSyncUI = {
this.updateUI(); this.updateUI();
return; return;
} }
this.showLoginError();
this.updateUI();
},
showLoginError() {
// Note: This is used for *both* Sync and ReadingList login errors.
let title = this._stringBundle.GetStringFromName("error.login.title"); let title = this._stringBundle.GetStringFromName("error.login.title");
let description; let description;
if (Weave.Status.sync == Weave.PROLONGED_SYNC_FAILURE) { if (Weave.Status.sync == Weave.PROLONGED_SYNC_FAILURE ||
this.isProlongedReadingListError()) {
this.log.debug("showLoginError has a prolonged login error");
// Convert to days // Convert to days
let lastSync = let lastSync =
Services.prefs.getIntPref("services.sync.errorhandler.networkFailureReportTimeout") / 86400; Services.prefs.getIntPref("services.sync.errorhandler.networkFailureReportTimeout") / 86400;
@ -214,6 +260,7 @@ let gSyncUI = {
let reason = Weave.Utils.getErrorString(Weave.Status.login); let reason = Weave.Utils.getErrorString(Weave.Status.login);
description = description =
this._stringBundle.formatStringFromName("error.sync.description", [reason], 1); this._stringBundle.formatStringFromName("error.sync.description", [reason], 1);
this.log.debug("showLoginError has a non-prolonged error", reason);
} }
let buttons = []; let buttons = [];
@ -226,7 +273,6 @@ let gSyncUI = {
let notification = new Weave.Notification(title, description, null, let notification = new Weave.Notification(title, description, null,
Weave.Notifications.PRIORITY_WARNING, buttons); Weave.Notifications.PRIORITY_WARNING, buttons);
Weave.Notifications.replaceTitle(notification); Weave.Notifications.replaceTitle(notification);
this.updateUI();
}, },
onLogout: function SUI_onLogout() { onLogout: function SUI_onLogout() {
@ -271,6 +317,7 @@ let gSyncUI = {
} }
Services.obs.notifyObservers(null, "cloudsync:user-sync", null); Services.obs.notifyObservers(null, "cloudsync:user-sync", null);
Services.obs.notifyObservers(null, "readinglist:user-sync", null);
}, },
handleToolbarButton: function SUI_handleStatusbarButton() { handleToolbarButton: function SUI_handleStatusbarButton() {
@ -367,7 +414,15 @@ let gSyncUI = {
let lastSync; let lastSync;
try { try {
lastSync = Services.prefs.getCharPref("services.sync.lastSync"); lastSync = new Date(Services.prefs.getCharPref("services.sync.lastSync"));
}
catch (e) { };
// and reading-list time - we want whatever one is the most recent.
try {
let lastRLSync = new Date(Services.prefs.getCharPref("readinglist.scheduler.lastSync"));
if (!lastSync || lastRLSync > lastSync) {
lastSync = lastRLSync;
}
} }
catch (e) { }; catch (e) { };
if (!lastSync || this._needsSetup()) { if (!lastSync || this._needsSetup()) {
@ -376,9 +431,9 @@ let gSyncUI = {
} }
// Show the day-of-week and time (HH:MM) of last sync // Show the day-of-week and time (HH:MM) of last sync
let lastSyncDate = new Date(lastSync).toLocaleFormat("%a %H:%M"); let lastSyncDateString = lastSync.toLocaleFormat("%a %H:%M");
let lastSyncLabel = let lastSyncLabel =
this._stringBundle.formatStringFromName("lastSync2.label", [lastSyncDate], 1); this._stringBundle.formatStringFromName("lastSync2.label", [lastSyncDateString], 1);
syncButton.setAttribute("tooltiptext", lastSyncLabel); syncButton.setAttribute("tooltiptext", lastSyncLabel);
}, },
@ -395,7 +450,69 @@ let gSyncUI = {
this.clearError(title); this.clearError(title);
}, },
// Return true if the reading-list is in a "prolonged" error state. That
// engine doesn't impose what that means, so calculate it here. For
// consistency, we just use the sync prefs.
isProlongedReadingListError() {
let lastSync, threshold, prolonged;
try {
lastSync = new Date(Services.prefs.getCharPref("readinglist.scheduler.lastSync"));
threshold = new Date(Date.now() - Services.prefs.getIntPref("services.sync.errorhandler.networkFailureReportTimeout"));
prolonged = lastSync <= threshold;
} catch (ex) {
// no pref, assume not prolonged.
prolonged = false;
}
this.log.debug("isProlongedReadingListError has last successful sync at ${lastSync}, threshold is ${threshold}, prolonged=${prolonged}",
{lastSync, threshold, prolonged});
return prolonged;
},
onRLSyncError() {
// Like onSyncError, but from the reading-list engine.
// However, the current UX around Sync is that error notifications should
// generally *not* be seen as they typically aren't actionable - so only
// authentication errors (which require user action) and "prolonged" errors
// (which technically aren't actionable, but user really should know anyway)
// are shown.
this.log.debug("onRLSyncError with readingList state", ReadingListScheduler.state);
if (ReadingListScheduler.state == ReadingListScheduler.STATE_ERROR_AUTHENTICATION) {
this.onLoginError();
return;
}
// If it's not prolonged there's nothing to do.
if (!this.isProlongedReadingListError()) {
this.log.debug("onRLSyncError has a non-authentication, non-prolonged error, so not showing any error UI");
return;
}
// So it's a prolonged error.
// Unfortunate duplication from below...
this.log.debug("onRLSyncError has a prolonged error");
let title = this._stringBundle.GetStringFromName("error.sync.title");
// XXX - this is somewhat wrong - we are reporting the threshold we consider
// to be prolonged, not how long it actually has been. (ie, lastSync below
// is effectively constant) - bit it too is copied from below.
let lastSync =
Services.prefs.getIntPref("services.sync.errorhandler.networkFailureReportTimeout") / 86400;
let description =
this._stringBundle.formatStringFromName("error.sync.prolonged_failure", [lastSync], 1);
let priority = Weave.Notifications.PRIORITY_INFO;
let buttons = [
new Weave.NotificationButton(
this._stringBundle.GetStringFromName("error.sync.tryAgainButton.label"),
this._stringBundle.GetStringFromName("error.sync.tryAgainButton.accesskey"),
function() { gSyncUI.doSync(); return true; }
),
];
let notification =
new Weave.Notification(title, description, null, priority, buttons);
Weave.Notifications.replaceTitle(notification);
this.updateUI();
},
onSyncError: function SUI_onSyncError() { onSyncError: function SUI_onSyncError() {
this.log.debug("onSyncError");
let title = this._stringBundle.GetStringFromName("error.sync.title"); let title = this._stringBundle.GetStringFromName("error.sync.title");
if (Weave.Status.login != Weave.LOGIN_SUCCEEDED) { if (Weave.Status.login != Weave.LOGIN_SUCCEEDED) {
@ -418,7 +535,9 @@ let gSyncUI = {
let priority = Weave.Notifications.PRIORITY_WARNING; let priority = Weave.Notifications.PRIORITY_WARNING;
let buttons = []; let buttons = [];
// Check if the client is outdated in some way // Check if the client is outdated in some way (but note: we've never in the
// past, and probably never will, bump the relevent version numbers, so
// this is effectively dead code!)
let outdated = Weave.Status.sync == Weave.VERSION_OUT_OF_DATE; let outdated = Weave.Status.sync == Weave.VERSION_OUT_OF_DATE;
for (let [engine, reason] in Iterator(Weave.Status.engines)) for (let [engine, reason] in Iterator(Weave.Status.engines))
outdated = outdated || reason == Weave.VERSION_OUT_OF_DATE; outdated = outdated || reason == Weave.VERSION_OUT_OF_DATE;
@ -468,6 +587,7 @@ let gSyncUI = {
}, },
observe: function SUI_observe(subject, topic, data) { observe: function SUI_observe(subject, topic, data) {
this.log.debug("observed", topic);
if (this._unloaded) { if (this._unloaded) {
Cu.reportError("SyncUI observer called after unload: " + topic); Cu.reportError("SyncUI observer called after unload: " + topic);
return; return;
@ -480,10 +600,26 @@ let gSyncUI = {
subject = subject.wrappedJSObject.object; subject = subject.wrappedJSObject.object;
} }
// First handle "activity" only.
switch (topic) { switch (topic) {
case "weave:service:sync:start": case "weave:service:sync:start":
case "weave:service:login:start":
case "readinglist:sync:start":
this.onActivityStart(); this.onActivityStart();
break; break;
case "weave:service:sync:finish":
case "weave:service:sync:error":
case "weave:service:login:finish":
case "weave:service:login:error":
case "readinglist:sync:finish":
case "readinglist:sync:error":
this.onActivityStop();
break;
}
// Now non-activity state (eg, enabled, errors, etc)
// Note that sync uses the ":ui:" notifications for errors because sync.
// ReadingList has no such concept (yet?; hopefully the :error is enough!)
switch (topic) {
case "weave:ui:sync:finish": case "weave:ui:sync:finish":
this.onSyncFinish(); this.onSyncFinish();
break; break;
@ -496,9 +632,6 @@ let gSyncUI = {
case "weave:service:setup-complete": case "weave:service:setup-complete":
this.onSetupComplete(); this.onSetupComplete();
break; break;
case "weave:service:login:start":
this.onActivityStart();
break;
case "weave:service:login:finish": case "weave:service:login:finish":
this.onLoginFinish(); this.onLoginFinish();
break; break;
@ -523,6 +656,13 @@ let gSyncUI = {
case "weave:ui:clear-error": case "weave:ui:clear-error":
this.clearError(); this.clearError();
break; break;
case "readinglist:sync:error":
this.onRLSyncError();
break;
case "readinglist:sync:finish":
this.clearError();
break;
} }
}, },
@ -540,3 +680,6 @@ XPCOMUtils.defineLazyGetter(gSyncUI, "_stringBundle", function() {
createBundle("chrome://weave/locale/services/sync.properties"); createBundle("chrome://weave/locale/services/sync.properties");
}); });
XPCOMUtils.defineLazyGetter(gSyncUI, "log", function() {
return Log.repository.getLogger("browserwindow.syncui");
});

View File

@ -37,6 +37,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "ContentSearch",
"resource:///modules/ContentSearch.jsm"); "resource:///modules/ContentSearch.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AboutHome", XPCOMUtils.defineLazyModuleGetter(this, "AboutHome",
"resource:///modules/AboutHome.jsm"); "resource:///modules/AboutHome.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Log",
"resource://gre/modules/Log.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "Favicons", XPCOMUtils.defineLazyServiceGetter(this, "Favicons",
"@mozilla.org/browser/favicon-service;1", "@mozilla.org/browser/favicon-service;1",
"mozIAsyncFavicons"); "mozIAsyncFavicons");

View File

@ -403,6 +403,8 @@ support-files =
[browser_ssl_error_reports.js] [browser_ssl_error_reports.js]
[browser_star_hsts.js] [browser_star_hsts.js]
[browser_subframe_favicons_not_used.js] [browser_subframe_favicons_not_used.js]
[browser_syncui.js]
skip-if = e10s # Bug 1137087 - browser_tabopen_reflows.js fails if this was previously run with e10s
[browser_tabDrop.js] [browser_tabDrop.js]
skip-if = buildapp == 'mulet' || e10s skip-if = buildapp == 'mulet' || e10s
[browser_tabMatchesInAwesomebar.js] [browser_tabMatchesInAwesomebar.js]

View File

@ -0,0 +1,244 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
let {Log} = Cu.import("resource://gre/modules/Log.jsm", {});
let {Weave} = Cu.import("resource://services-sync/main.js", {});
let {Notifications} = Cu.import("resource://services-sync/notifications.js", {});
// The BackStagePass allows us to get this test-only non-exported function.
let {getInternalScheduler} = Cu.import("resource:///modules/readinglist/Scheduler.jsm", {});
let stringBundle = Cc["@mozilla.org/intl/stringbundle;1"]
.getService(Ci.nsIStringBundleService)
.createBundle("chrome://weave/locale/services/sync.properties");
// ensure test output sees log messages.
Log.repository.getLogger("browserwindow.syncui").addAppender(new Log.DumpAppender());
function promiseObserver(topic) {
return new Promise(resolve => {
let obs = (subject, topic, data) => {
Services.obs.removeObserver(obs, topic);
resolve(subject);
}
Services.obs.addObserver(obs, topic, false);
});
}
add_task(function* prepare() {
let xps = Components.classes["@mozilla.org/weave/service;1"]
.getService(Components.interfaces.nsISupports)
.wrappedJSObject;
yield xps.whenLoaded();
// mock out the "_needsSetup()" function so we don't short-circuit.
let oldNeedsSetup = window.gSyncUI._needsSetup;
window.gSyncUI._needsSetup = () => false;
registerCleanupFunction(() => {
window.gSyncUI._needsSetup = oldNeedsSetup;
});
});
add_task(function* testProlongedSyncError() {
let promiseNotificationAdded = promiseObserver("weave:notification:added");
Assert.equal(Notifications.notifications.length, 0, "start with no notifications");
// Pretend we are in the "prolonged error" state.
Weave.Status.sync = Weave.PROLONGED_SYNC_FAILURE;
Weave.Status.login = Weave.LOGIN_SUCCEEDED;
Services.obs.notifyObservers(null, "weave:ui:sync:error", null);
let subject = yield promiseNotificationAdded;
let notification = subject.wrappedJSObject.object; // sync's observer abstraction is abstract!
Assert.equal(notification.title, stringBundle.GetStringFromName("error.sync.title"));
Assert.equal(Notifications.notifications.length, 1, "exactly 1 notification");
// Now pretend we just had a successful sync - the error notification should go away.
let promiseNotificationRemoved = promiseObserver("weave:notification:removed");
Weave.Status.sync = Weave.STATUS_OK;
Services.obs.notifyObservers(null, "weave:ui:sync:finish", null);
yield promiseNotificationRemoved;
Assert.equal(Notifications.notifications.length, 0, "no notifications left");
});
add_task(function* testProlongedRLError() {
let promiseNotificationAdded = promiseObserver("weave:notification:added");
Assert.equal(Notifications.notifications.length, 0, "start with no notifications");
// Pretend the reading-list is in the "prolonged error" state.
let longAgo = new Date(Date.now() - 100 * 24 * 60 * 60 * 1000); // 100 days ago.
Services.prefs.setCharPref("readinglist.scheduler.lastSync", longAgo.toString());
getInternalScheduler().state = ReadingListScheduler.STATE_ERROR_OTHER;
Services.obs.notifyObservers(null, "readinglist:sync:start", null);
Services.obs.notifyObservers(null, "readinglist:sync:error", null);
let subject = yield promiseNotificationAdded;
let notification = subject.wrappedJSObject.object; // sync's observer abstraction is abstract!
Assert.equal(notification.title, stringBundle.GetStringFromName("error.sync.title"));
Assert.equal(Notifications.notifications.length, 1, "exactly 1 notification");
// Now pretend we just had a successful sync - the error notification should go away.
let promiseNotificationRemoved = promiseObserver("weave:notification:removed");
Services.prefs.setCharPref("readinglist.scheduler.lastSync", Date.now().toString());
Services.obs.notifyObservers(null, "readinglist:sync:start", null);
Services.obs.notifyObservers(null, "readinglist:sync:finish", null);
yield promiseNotificationRemoved;
Assert.equal(Notifications.notifications.length, 0, "no notifications left");
});
add_task(function* testSyncLoginError() {
let promiseNotificationAdded = promiseObserver("weave:notification:added");
Assert.equal(Notifications.notifications.length, 0, "start with no notifications");
// Pretend we are in the "prolonged error" state.
Weave.Status.sync = Weave.LOGIN_FAILED;
Weave.Status.login = Weave.LOGIN_FAILED_LOGIN_REJECTED;
Services.obs.notifyObservers(null, "weave:ui:sync:error", null);
let subject = yield promiseNotificationAdded;
let notification = subject.wrappedJSObject.object; // sync's observer abstraction is abstract!
Assert.equal(notification.title, stringBundle.GetStringFromName("error.login.title"));
Assert.equal(Notifications.notifications.length, 1, "exactly 1 notification");
// Now pretend we just had a successful login - the error notification should go away.
Weave.Status.sync = Weave.STATUS_OK;
Weave.Status.login = Weave.LOGIN_SUCCEEDED;
let promiseNotificationRemoved = promiseObserver("weave:notification:removed");
Services.obs.notifyObservers(null, "weave:service:login:start", null);
Services.obs.notifyObservers(null, "weave:service:login:finish", null);
yield promiseNotificationRemoved;
Assert.equal(Notifications.notifications.length, 0, "no notifications left");
});
add_task(function* testRLLoginError() {
let promiseNotificationAdded = promiseObserver("weave:notification:added");
Assert.equal(Notifications.notifications.length, 0, "start with no notifications");
// Pretend RL is in an auth error state
getInternalScheduler().state = ReadingListScheduler.STATE_ERROR_AUTHENTICATION;
Services.obs.notifyObservers(null, "readinglist:sync:start", null);
Services.obs.notifyObservers(null, "readinglist:sync:error", null);
let subject = yield promiseNotificationAdded;
let notification = subject.wrappedJSObject.object; // sync's observer abstraction is abstract!
Assert.equal(notification.title, stringBundle.GetStringFromName("error.login.title"));
Assert.equal(Notifications.notifications.length, 1, "exactly 1 notification");
// Now pretend we just had a successful sync - the error notification should go away.
getInternalScheduler().state = ReadingListScheduler.STATE_OK;
let promiseNotificationRemoved = promiseObserver("weave:notification:removed");
Services.obs.notifyObservers(null, "readinglist:sync:start", null);
Services.obs.notifyObservers(null, "readinglist:sync:finish", null);
yield promiseNotificationRemoved;
Assert.equal(Notifications.notifications.length, 0, "no notifications left");
});
// Here we put readinglist into an "authentication error" state (should see
// the error bar reflecting this), then report a prolonged error from Sync (an
// infobar to reflect the sync error should replace it), then resolve the sync
// error - the authentication error from readinglist should remain.
add_task(function* testRLLoginErrorRemains() {
let promiseNotificationAdded = promiseObserver("weave:notification:added");
Assert.equal(Notifications.notifications.length, 0, "start with no notifications");
// Pretend RL is in an auth error state
getInternalScheduler().state = ReadingListScheduler.STATE_ERROR_AUTHENTICATION;
Services.obs.notifyObservers(null, "readinglist:sync:start", null);
Services.obs.notifyObservers(null, "readinglist:sync:error", null);
let subject = yield promiseNotificationAdded;
let notification = subject.wrappedJSObject.object; // sync's observer abstraction is abstract!
Assert.equal(notification.title, stringBundle.GetStringFromName("error.login.title"));
Assert.equal(Notifications.notifications.length, 1, "exactly 1 notification");
// Now Sync into a prolonged auth error state.
promiseNotificationAdded = promiseObserver("weave:notification:added");
Weave.Status.sync = Weave.PROLONGED_SYNC_FAILURE;
Weave.Status.login = Weave.LOGIN_FAILED_LOGIN_REJECTED;
Services.obs.notifyObservers(null, "weave:ui:sync:error", null);
subject = yield promiseNotificationAdded;
// still exactly 1 notification with the "login" title.
notification = subject.wrappedJSObject.object;
Assert.equal(notification.title, stringBundle.GetStringFromName("error.login.title"));
Assert.equal(Notifications.notifications.length, 1, "exactly 1 notification");
// Resolve the sync problem.
promiseNotificationAdded = promiseObserver("weave:notification:added");
Weave.Status.sync = Weave.STATUS_OK;
Weave.Status.login = Weave.LOGIN_SUCCEEDED;
Services.obs.notifyObservers(null, "weave:ui:sync:finish", null);
// Expect one notification - the RL login problem.
subject = yield promiseNotificationAdded;
// still exactly 1 notification with the "login" title.
notification = subject.wrappedJSObject.object;
Assert.equal(notification.title, stringBundle.GetStringFromName("error.login.title"));
Assert.equal(Notifications.notifications.length, 1, "exactly 1 notification");
// and cleanup - resolve the readinglist error.
getInternalScheduler().state = ReadingListScheduler.STATE_OK;
let promiseNotificationRemoved = promiseObserver("weave:notification:removed");
Services.obs.notifyObservers(null, "readinglist:sync:start", null);
Services.obs.notifyObservers(null, "readinglist:sync:finish", null);
yield promiseNotificationRemoved;
Assert.equal(Notifications.notifications.length, 0, "no notifications left");
});
function checkButtonsStatus(shouldBeActive) {
let button = document.getElementById("sync-button");
let panelbutton = document.getElementById("PanelUI-fxa-status");
if (shouldBeActive) {
Assert.equal(button.getAttribute("status"), "active");
Assert.equal(panelbutton.getAttribute("syncstatus"), "active");
} else {
Assert.ok(!button.hasAttribute("status"));
Assert.ok(!panelbutton.hasAttribute("syncstatus"));
}
}
function testButtonActions(startNotification, endNotification) {
checkButtonsStatus(false);
// pretend a sync is starting.
Services.obs.notifyObservers(null, startNotification, null);
checkButtonsStatus(true);
// and has stopped
Services.obs.notifyObservers(null, endNotification, null);
checkButtonsStatus(false);
}
add_task(function* testButtonActivities() {
// add the Sync button to the panel so we can get it!
CustomizableUI.addWidgetToArea("sync-button", CustomizableUI.AREA_PANEL);
// check the button's functionality
yield PanelUI.show();
try {
testButtonActions("weave:service:login:start", "weave:service:login:finish");
testButtonActions("weave:service:login:start", "weave:service:login:error");
testButtonActions("weave:service:sync:start", "weave:service:sync:finish");
testButtonActions("weave:service:sync:start", "weave:service:sync:error");
testButtonActions("readinglist:sync:start", "readinglist:sync:finish");
testButtonActions("readinglist:sync:start", "readinglist:sync:error");
// and ensure the counters correctly handle multiple in-flight syncs
Services.obs.notifyObservers(null, "weave:service:sync:start", null);
checkButtonsStatus(true);
Services.obs.notifyObservers(null, "readinglist:sync:start", null);
checkButtonsStatus(true);
Services.obs.notifyObservers(null, "readinglist:sync:finish", null);
// sync is still going...
checkButtonsStatus(true);
// another reading list starts
Services.obs.notifyObservers(null, "readinglist:sync:start", null);
checkButtonsStatus(true);
// The initial sync stops.
Services.obs.notifyObservers(null, "weave:service:sync:finish", null);
// RL is still going...
checkButtonsStatus(true);
// RL finishes with an error, so no longer active.
Services.obs.notifyObservers(null, "readinglist:sync:error", null);
checkButtonsStatus(false);
} finally {
PanelUI.hide();
CustomizableUI.removeWidgetFromArea("sync-button");
}
});

View File

@ -4,6 +4,8 @@
let { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; let { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DownloadUtils", XPCOMUtils.defineLazyModuleGetter(this, "DownloadUtils",
"resource://gre/modules/DownloadUtils.jsm"); "resource://gre/modules/DownloadUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon", XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",

View File

@ -66,6 +66,8 @@
let { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; let { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon", XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
"resource:///modules/DownloadsCommon.jsm"); "resource:///modules/DownloadsCommon.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DownloadsViewUI", XPCOMUtils.defineLazyModuleGetter(this, "DownloadsViewUI",

View File

@ -0,0 +1,338 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict;"
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, 'LogManager',
'resource://services-common/logmanager.js');
XPCOMUtils.defineLazyModuleGetter(this, 'Log',
'resource://gre/modules/Log.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'Preferences',
'resource://gre/modules/Preferences.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'setTimeout',
'resource://gre/modules/Timer.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'clearTimeout',
'resource://gre/modules/Timer.jsm');
Cu.import('resource://gre/modules/Task.jsm');
this.EXPORTED_SYMBOLS = ["ReadingListScheduler"];
// A list of "external" observer topics that may cause us to change when we
// sync.
const OBSERVERS = [
// We don't sync when offline and restart when online.
"network:offline-status-changed",
// FxA notifications also cause us to check if we should sync.
"fxaccounts:onverified",
// When something notices a local change to an item.
"readinglist:item-changed",
// some notifications the engine might send if we have been requested to backoff.
"readinglist:backoff-requested",
// request to sync now
"readinglist:user-sync",
];
///////// A temp object until we get our "engine"
let engine = {
ERROR_AUTHENTICATION: "authentication error",
sync: Task.async(function* () {
}),
}
let prefs = new Preferences("readinglist.scheduler.");
// A helper to manage our interval values.
let intervals = {
// Getters for our intervals.
_fixupIntervalPref(prefName, def) {
// All pref values are seconds, but we return ms.
return prefs.get(prefName, def) * 1000;
},
// How long after startup do we do an initial sync?
get initial() this._fixupIntervalPref("initial", 20), // 20 seconds.
// Every interval after the first.
get schedule() this._fixupIntervalPref("schedule", 2 * 60 * 60), // 2 hours
// After we've been told an item has changed
get dirty() this._fixupIntervalPref("dirty", 2 * 60), // 2 mins
// After an error
get retry() this._fixupIntervalPref("retry", 2 * 60), // 2 mins
};
// This is the implementation, but it's not exposed directly.
function InternalScheduler() {
// oh, I don't know what logs yet - let's guess!
let logs = ["readinglist", "FirefoxAccounts", "browserwindow.syncui"];
this._logManager = new LogManager("readinglist.", logs, "readinglist");
this.log = Log.repository.getLogger("readinglist.scheduler");
this.log.info("readinglist scheduler created.")
this.state = this.STATE_OK;
// don't this.init() here, but instead at the module level - tests want to
// add hooks before it is called.
}
InternalScheduler.prototype = {
// When the next scheduled sync should happen. If we can sync, there will
// be a timer set to fire then. If we can't sync there will not be a timer,
// but it will be set to fire then as soon as we can.
_nextScheduledSync: null,
// The time when the most-recent "backoff request" expires - we will never
// schedule a new timer before this.
_backoffUntil: 0,
// Our current timer.
_timer: null,
// Our timer fires a promise - _timerRunning is true until it resolves or
// rejects.
_timerRunning: false,
// Our sync engine - XXX - maybe just a callback?
_engine: engine,
// Our state variable and constants.
state: null,
STATE_OK: "ok",
STATE_ERROR_AUTHENTICATION: "authentication error",
STATE_ERROR_OTHER: "other error",
init() {
this.log.info("scheduler initialzing");
this._observe = this.observe.bind(this);
for (let notification of OBSERVERS) {
Services.obs.addObserver(this._observe, notification, false);
}
this._nextScheduledSync = Date.now() + intervals.initial;
this._setupTimer();
},
// Note: only called by tests.
finalize() {
this.log.info("scheduler finalizing");
this._clearTimer();
for (let notification of OBSERVERS) {
Services.obs.removeObserver(this._observe, notification);
}
this._observe = null;
},
observe(subject, topic, data) {
this.log.debug("observed ${}", topic);
switch (topic) {
case "readinglist:backoff-requested": {
// The subject comes in as a string, a number of seconds.
let interval = parseInt(data, 10);
if (isNaN(interval)) {
this.log.warn("Backoff request had non-numeric value", data);
return;
}
this.log.info("Received a request to backoff for ${} seconds", interval);
this._backoffUntil = Date.now() + interval * 1000;
this._maybeReschedule(0);
break;
}
case "readinglist:local:dirty":
this._maybeReschedule(intervals.dirty);
break;
case "readinglist:user-sync":
this._syncNow();
break;
case "fxaccounts:onverified":
// If we were in an authentication error state, reset that now.
if (this.state == this.STATE_ERROR_AUTHENTICATION) {
this.state = this.STATE_OK;
}
break;
// The rest just indicate that now is probably a good time to check if
// we can sync as normal using whatever schedule was previously set.
default:
break;
}
// When observers fire we ignore the current sync error state as the
// notification may indicate it's been resolved.
this._setupTimer(true);
},
// Is the current error state such that we shouldn't schedule a new sync.
_isBlockedOnError() {
// this needs more thought...
return this.state == this.STATE_ERROR_AUTHENTICATION;
},
// canSync indicates if we can currently sync.
_canSync(ignoreBlockingErrors = false) {
if (Services.io.offline) {
this.log.info("canSync=false - we are offline");
return false;
}
if (!ignoreBlockingErrors && this._isBlockedOnError()) {
this.log.info("canSync=false - we are in a blocked error state", this.state);
return false;
}
this.log.info("canSync=true");
return true;
},
// _setupTimer checks the current state and the environment to see when
// we should next sync and creates the timer with the appropriate delay.
_setupTimer(ignoreBlockingErrors = false) {
if (!this._canSync(ignoreBlockingErrors)) {
this._clearTimer();
return;
}
if (this._timer) {
let when = new Date(this._nextScheduledSync);
let delay = this._nextScheduledSync - Date.now();
this.log.info("checkStatus - already have a timer - will fire in ${delay}ms at ${when}",
{delay, when});
return;
}
if (this._timerRunning) {
this.log.info("checkStatus - currently syncing");
return;
}
// no timer and we can sync, so start a new one.
let now = Date.now();
let delay = Math.max(0, this._nextScheduledSync - now);
let when = new Date(now + delay);
this.log.info("next scheduled sync is in ${delay}ms (at ${when})", {delay, when})
this._timer = this._setTimeout(delay);
},
// Something (possibly naively) thinks the next sync should happen in
// delay-ms. If there's a backoff in progress, ignore the requested delay
// and use the back-off. If there's already a timer scheduled for earlier
// than delay, let the earlier timer remain. Otherwise, use the requested
// delay.
_maybeReschedule(delay) {
// If there's no delay specified and there's nothing currently scheduled,
// it means a backoff request while the sync is actually running - there's
// no need to do anything here - the next reschedule after the sync
// completes will take the backoff into account.
if (!delay && !this._nextScheduledSync) {
this.log.debug("_maybeReschedule ignoring a backoff request while running");
return;
}
let now = Date.now();
if (!this._nextScheduledSync) {
this._nextScheduledSync = now + delay;
}
// If there is something currently scheduled before the requested delay,
// keep the existing value (eg, if we have a timer firing in 1 second, and
// get a "dirty" notification that says we should sync in 2 seconds, we
// keep the 1 second value)
this._nextScheduledSync = Math.min(this._nextScheduledSync, now + delay);
// But we still need to honor a backoff.
this._nextScheduledSync = Math.max(this._nextScheduledSync, this._backoffUntil);
// And always create a new timer next time _setupTimer is called.
this._clearTimer();
},
// callback for when the timer fires.
_doSync() {
this.log.debug("starting sync");
this._timer = null;
this._timerRunning = true;
// flag that there's no new schedule yet, so a request coming in while
// we are running does the right thing.
this._nextScheduledSync = 0;
Services.obs.notifyObservers(null, "readinglist:sync:start", null);
this._engine.sync().then(() => {
this.log.info("Sync completed successfully");
// Write a pref in the same format used to services/sync to indicate
// the last success.
prefs.set("lastSync", new Date().toString());
this.state = this.STATE_OK;
this._logManager.resetFileLog(this._logManager.REASON_SUCCESS);
Services.obs.notifyObservers(null, "readinglist:sync:finish", null);
return intervals.schedule;
}).catch(err => {
this.log.error("Sync failed", err);
// XXX - how to detect an auth error?
this.state = err == this._engine.ERROR_AUTHENTICATION ?
this.STATE_ERROR_AUTHENTICATION : this.STATE_ERROR_OTHER;
this._logManager.resetFileLog(this._logManager.REASON_ERROR);
Services.obs.notifyObservers(null, "readinglist:sync:error", null);
return intervals.retry;
}).then(nextDelay => {
this._timerRunning = false;
// ensure a new timer is setup for the appropriate next time.
this._maybeReschedule(nextDelay);
this._setupTimer();
this._onAutoReschedule(); // just for tests...
}).catch(err => {
// We should never get here, but better safe than sorry...
this.log.error("Failed to reschedule after sync completed", err);
});
},
_clearTimer() {
if (this._timer) {
clearTimeout(this._timer);
this._timer = null;
}
},
// A function to "sync now", but not allowing it to start if one is
// already running, and rescheduling the timer.
// To call this, just send a "readinglist:user-sync" notification.
_syncNow() {
if (this._timerRunning) {
this.log.info("syncNow() but a sync is already in progress - ignoring");
return;
}
this._clearTimer();
this._doSync();
},
// A couple of hook-points for testing.
// xpcshell tests hook this so (a) it can check the expected delay is set
// and (b) to ignore the delay and set a timeout of 0 so the test is fast.
_setTimeout(delay) {
return setTimeout(() => this._doSync(), delay);
},
// xpcshell tests hook this to make sure that the correct state etc exist
// after a sync has been completed and a new timer created (or not).
_onAutoReschedule() {},
};
let internalScheduler = new InternalScheduler();
internalScheduler.init();
// The public interface into this module is tiny, so a simple object that
// delegates to the implementation.
let ReadingListScheduler = {
get STATE_OK() internalScheduler.STATE_OK,
get STATE_ERROR_AUTHENTICATION() internalScheduler.STATE_ERROR_AUTHENTICATION,
get STATE_ERROR_OTHER() internalScheduler.STATE_ERROR_OTHER,
get state() internalScheduler.state,
};
// These functions are exposed purely for tests, which manage to grab them
// via a BackstagePass.
function createTestableScheduler() {
// kill the "real" scheduler as we don't want it listening to notifications etc.
if (internalScheduler) {
internalScheduler.finalize();
internalScheduler = null;
}
// No .init() call - that's up to the tests after hooking.
return new InternalScheduler();
}
// mochitests want the internal state of the real scheduler for various things.
function getInternalScheduler() {
return internalScheduler;
}

View File

@ -13,3 +13,9 @@ TESTING_JS_MODULES += [
] ]
BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini'] BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini']
XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell/xpcshell.ini']
EXTRA_JS_MODULES.readinglist += [
'Scheduler.jsm',
]

View File

@ -0,0 +1,7 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");

View File

@ -0,0 +1,146 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
XPCOMUtils.defineLazyModuleGetter(this, 'setTimeout',
'resource://gre/modules/Timer.jsm');
// Setup logging prefs before importing the scheduler module.
Services.prefs.setCharPref("readinglist.log.appender.dump", "Trace");
let {createTestableScheduler} = Cu.import("resource:///modules/readinglist/Scheduler.jsm", {});
Cu.import("resource://gre/modules/Preferences.jsm");
Cu.import("resource://gre/modules/Timer.jsm");
// Log rotation needs a profile dir.
do_get_profile();
let prefs = new Preferences("readinglist.scheduler.");
function promiseObserver(topic) {
return new Promise(resolve => {
let obs = (subject, topic, data) => {
Services.obs.removeObserver(obs, topic);
resolve(data);
}
Services.obs.addObserver(obs, topic, false);
});
}
function createScheduler(options) {
// avoid typos in the test and other footguns in the options.
let allowedOptions = ["expectedDelay", "expectNewTimer", "syncFunction"];
for (let key of Object.keys(options)) {
if (!allowedOptions.includes(key)) {
throw new Error("Invalid option " + key);
}
}
let scheduler = createTestableScheduler();
// make our hooks
let syncFunction = options.syncFunction || Promise.resolve;
scheduler._engine.sync = syncFunction;
// we expect _setTimeout to be called *twice* - first is the initial sync,
// and there's no need to test the delay used for that. options.expectedDelay
// is to check the *subsequent* timer.
let numCalls = 0;
scheduler._setTimeout = function(delay) {
++numCalls;
print("Test scheduler _setTimeout call number " + numCalls + " with delay=" + delay);
switch (numCalls) {
case 1:
// this is the first and boring schedule as it initializes - do nothing
// other than return a timer that fires immediately.
return setTimeout(() => scheduler._doSync(), 0);
break;
case 2:
// This is the one we are interested in, so check things.
if (options.expectedDelay) {
// a little slop is OK as it takes a few ms to actually set the timer
ok(Math.abs(options.expectedDelay * 1000 - delay) < 500, [options.expectedDelay * 1000, delay]);
}
// and return a timeout that "never" fires
return setTimeout(() => scheduler._doSync(), 10000000);
break;
default:
// This is unexpected!
ok(false, numCalls);
}
};
// And a callback made once we've determined the next delay. This is always
// called even if _setTimeout isn't (due to no timer being created)
scheduler._onAutoReschedule = () => {
// Most tests expect a new timer, so this is "opt out"
let expectNewTimer = options.expectNewTimer === undefined ? true : options.expectNewTimer;
ok(expectNewTimer ? scheduler._timer : !scheduler._timer);
}
// calling .init fires things off...
scheduler.init();
return scheduler;
}
add_task(function* testSuccess() {
// promises which resolve once we've got all the expected notifications.
let allNotifications = [
promiseObserver("readinglist:sync:start"),
promiseObserver("readinglist:sync:finish"),
];
// New delay should be "as regularly scheduled".
prefs.set("schedule", 100);
let scheduler = createScheduler({expectedDelay: 100});
yield Promise.all(allNotifications);
scheduler.finalize();
});
add_task(function* testOffline() {
let scheduler = createScheduler({expectNewTimer: false});
Services.io.offline = true;
ok(!scheduler._canSync(), "_canSync is false when offline.")
ok(!scheduler._timer, "there is no current timer while offline.")
Services.io.offline = false;
ok(scheduler._canSync(), "_canSync is true when online.")
ok(scheduler._timer, "there is a new timer when back online.")
scheduler.finalize();
});
add_task(function* testRetryableError() {
let allNotifications = [
promiseObserver("readinglist:sync:start"),
promiseObserver("readinglist:sync:error"),
];
prefs.set("retry", 10);
let scheduler = createScheduler({
expectedDelay: 10,
syncFunction: () => Promise.reject("transient"),
});
yield Promise.all(allNotifications);
scheduler.finalize();
});
add_task(function* testAuthError() {
prefs.set("retry", 10);
// We expect an auth error to result in no new timer (as it's waiting for
// some indication it can proceed), but with the next delay being a normal
// "retry" interval (so when we can proceed it is probably already stale, so
// is effectively "immediate")
let scheduler = createScheduler({
expectedDelay: 10,
syncFunction: () => {
return Promise.reject(ReadingListScheduler._engine.ERROR_AUTHENTICATION);
},
expectNewTimer: false
});
// XXX - TODO - send an observer that "unblocks" us and ensure we actually
// do unblock.
scheduler.finalize();
});
add_task(function* testBackoff() {
let scheduler = createScheduler({expectedDelay: 1000});
Services.obs.notifyObservers(null, "readinglist:backoff-requested", 1000);
// XXX - this needs a little love as nothing checks createScheduler actually
// made the checks we think it does.
scheduler.finalize();
});
function run_test() {
run_next_test();
}

View File

@ -0,0 +1,5 @@
[DEFAULT]
head = head.js
firefox-appdir = browser
[test_scheduler.js]

View File

@ -48,7 +48,7 @@ function test() {
statusText: "OK", statusText: "OK",
type: "json", type: "json",
fullMimeType: "application/json; charset=utf-8", fullMimeType: "application/json; charset=utf-8",
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02), size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.03),
time: true time: true
}); });
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(4), verifyRequestItemTarget(RequestsMenu.getItemAtIndex(4),
@ -67,7 +67,7 @@ function test() {
statusText: "OK", statusText: "OK",
type: "png", type: "png",
fullMimeType: "image/png", fullMimeType: "image/png",
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.75), size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.76),
time: true time: true
}); });

View File

@ -228,8 +228,8 @@ function test() {
statusText: "Meh", statusText: "Meh",
type: "2", type: "2",
fullMimeType: "text/2", fullMimeType: "text/2",
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01), transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01), size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02),
time: true time: true
}); });
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(c), verifyRequestItemTarget(RequestsMenu.getItemAtIndex(c),
@ -239,8 +239,8 @@ function test() {
statusText: "Meh", statusText: "Meh",
type: "3", type: "3",
fullMimeType: "text/3", fullMimeType: "text/3",
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02), transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.03),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02), size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.03),
time: true time: true
}); });
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(d), verifyRequestItemTarget(RequestsMenu.getItemAtIndex(d),
@ -250,8 +250,8 @@ function test() {
statusText: "Meh", statusText: "Meh",
type: "4", type: "4",
fullMimeType: "text/4", fullMimeType: "text/4",
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.03), transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.04),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.03), size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.04),
time: true time: true
}); });
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(e), verifyRequestItemTarget(RequestsMenu.getItemAtIndex(e),
@ -261,8 +261,8 @@ function test() {
statusText: "Meh", statusText: "Meh",
type: "5", type: "5",
fullMimeType: "text/5", fullMimeType: "text/5",
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.04), transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.05),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.04), size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.05),
time: true time: true
}); });

View File

@ -143,8 +143,8 @@ function test() {
statusText: "Meh", statusText: "Meh",
type: "2", type: "2",
fullMimeType: "text/2", fullMimeType: "text/2",
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01), transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01), size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02),
time: true time: true
}); });
} }
@ -156,8 +156,8 @@ function test() {
statusText: "Meh", statusText: "Meh",
type: "3", type: "3",
fullMimeType: "text/3", fullMimeType: "text/3",
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02), transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.03),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02), size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.03),
time: true time: true
}); });
} }
@ -169,8 +169,8 @@ function test() {
statusText: "Meh", statusText: "Meh",
type: "4", type: "4",
fullMimeType: "text/4", fullMimeType: "text/4",
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.03), transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.04),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.03), size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.04),
time: true time: true
}); });
} }
@ -182,8 +182,8 @@ function test() {
statusText: "Meh", statusText: "Meh",
type: "5", type: "5",
fullMimeType: "text/5", fullMimeType: "text/5",
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.04), transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.05),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.04), size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.05),
time: true time: true
}); });
} }

View File

@ -47,11 +47,6 @@ var Resource = Class({
this.uri = uri; this.uri = uri;
}, },
/**
* Return the trailing name component of this.uri.
*/
get basename() { return this.uri.path.replace(/\/+$/, '').replace(/\\/g,'/').replace( /.*\//, '' ); },
/** /**
* Is there more than 1 child Resource? * Is there more than 1 child Resource?
*/ */
@ -238,6 +233,13 @@ var FileResource = Class({
return this._refreshDeferred.promise; return this._refreshDeferred.promise;
}, },
/**
* Return the trailing name component of this Resource
*/
get basename() {
return this.path.replace(/\/+$/, '').replace(/\\/g,'/').replace( /.*\//, '' );
},
/** /**
* A string to be used when displaying this Resource in views * A string to be used when displaying this Resource in views
*/ */

View File

@ -13,7 +13,29 @@ add_task(function*() {
let root = [...projecteditor.project.allStores()][0].root; let root = [...projecteditor.project.allStores()][0].root;
is(root.path, TEMP_PATH, "The root store is set to the correct temp path."); is(root.path, TEMP_PATH, "The root store is set to the correct temp path.");
for (let child of root.children) { for (let child of root.children) {
yield renameWithContextMenu(projecteditor, projecteditor.projectTree.getViewContainer(child)); yield renameWithContextMenu(projecteditor,
projecteditor.projectTree.getViewContainer(child),
".renamed");
}
});
add_task(function*() {
let projecteditor = yield addProjectEditorTabForTempDirectory();
ok(true, "ProjectEditor has loaded");
let root = [...projecteditor.project.allStores()][0].root;
is(root.path, TEMP_PATH, "The root store is set to the correct temp path.");
let childrenList = new Array();
for (let child of root.children) {
yield renameWithContextMenu(projecteditor,
projecteditor.projectTree.getViewContainer(child),
".ren\u0061\u0308med");
childrenList.push(child.basename + ".ren\u0061\u0308med");
}
for (let child of root.children) {
is (childrenList.indexOf(child.basename) == -1, false,
"Failed to update tree with non-ascii character");
} }
}); });
@ -25,7 +47,7 @@ function openContextMenuOn(node) {
); );
} }
function renameWithContextMenu(projecteditor, container) { function renameWithContextMenu(projecteditor, container, newName) {
let defer = promise.defer(); let defer = promise.defer();
let popup = projecteditor.contextMenuPopup; let popup = projecteditor.contextMenuPopup;
let resource = container.resource; let resource = container.resource;
@ -39,7 +61,7 @@ function renameWithContextMenu(projecteditor, container) {
projecteditor.project.on("refresh-complete", function refreshComplete() { projecteditor.project.on("refresh-complete", function refreshComplete() {
projecteditor.project.off("refresh-complete", refreshComplete); projecteditor.project.off("refresh-complete", refreshComplete);
OS.File.stat(resource.path + ".renamed").then(() => { OS.File.stat(resource.path + newName).then(() => {
ok (true, "File is renamed"); ok (true, "File is renamed");
defer.resolve(); defer.resolve();
}, (ex) => { }, (ex) => {
@ -50,7 +72,8 @@ function renameWithContextMenu(projecteditor, container) {
renameCommand.click(); renameCommand.click();
popup.hidePopup(); popup.hidePopup();
EventUtils.sendString(resource.basename + ".renamed", projecteditor.window); let input = container.elt.previousElementSibling;
input.value = resource.basename + newName;
EventUtils.synthesizeKey("VK_RETURN", {}, projecteditor.window); EventUtils.synthesizeKey("VK_RETURN", {}, projecteditor.window);
}); });

View File

@ -54,7 +54,7 @@ function* testGraph(graph) {
is(graph._maxTooltip.querySelector("[text=value]").textContent, "60", is(graph._maxTooltip.querySelector("[text=value]").textContent, "60",
"The maximum tooltip displays the correct value."); "The maximum tooltip displays the correct value.");
is(graph._avgTooltip.querySelector("[text=value]").textContent, "41.71", is(graph._avgTooltip.querySelector("[text=value]").textContent, "41.72",
"The average tooltip displays the correct value."); "The average tooltip displays the correct value.");
is(graph._minTooltip.querySelector("[text=value]").textContent, "10", is(graph._minTooltip.querySelector("[text=value]").textContent, "10",
"The minimum tooltip displays the correct value."); "The minimum tooltip displays the correct value.");

View File

@ -8,7 +8,7 @@ let {ViewHelpers} = Cu.import("resource:///modules/devtools/ViewHelpers.jsm", {}
function test() { function test() {
let l10n = new ViewHelpers.L10N(); let l10n = new ViewHelpers.L10N();
is(l10n.numberWithDecimals(1234.56789, 2), "1,234.56", is(l10n.numberWithDecimals(1234.56789, 2), "1,234.57",
"The first number was properly localized."); "The first number was properly localized.");
is(l10n.numberWithDecimals(0.0001, 2), "0", is(l10n.numberWithDecimals(0.0001, 2), "0",
"The second number was properly localized."); "The second number was properly localized.");

View File

@ -369,10 +369,6 @@ ViewHelpers.L10N.prototype = {
if (isNaN(aNumber) || aNumber == null) { if (isNaN(aNumber) || aNumber == null) {
return "0"; return "0";
} }
// Remove {n} trailing decimals. Can't use toFixed(n) because
// toLocaleString converts the number to a string. Also can't use
// toLocaleString(, { maximumFractionDigits: n }) because it's not
// implemented on OS X (bug 368838). Gross.
let localized = aNumber.toLocaleString(); // localize let localized = aNumber.toLocaleString(); // localize
// If no grouping or decimal separators are available, bail out, because // If no grouping or decimal separators are available, bail out, because
@ -381,9 +377,10 @@ ViewHelpers.L10N.prototype = {
return localized; return localized;
} }
let padded = localized + new Array(aDecimals).join("0"); // pad with zeros return aNumber.toLocaleString(undefined, {
let match = padded.match("([^]*?\\d{" + aDecimals + "})\\d*$"); maximumFractionDigits: aDecimals,
return match.pop(); minimumFractionDigits: aDecimals
});
} }
}; };

View File

@ -447,13 +447,15 @@ StyleEditorUI.prototype = {
* Editor to create UI for. * Editor to create UI for.
*/ */
_sourceLoaded: function(editor) { _sourceLoaded: function(editor) {
let ordinal = editor.styleSheet.styleSheetIndex;
ordinal = ordinal == -1 ? Number.MAX_SAFE_INTEGER : ordinal;
// add new sidebar item and editor to the UI // add new sidebar item and editor to the UI
this._view.appendTemplatedItem(STYLE_EDITOR_TEMPLATE, { this._view.appendTemplatedItem(STYLE_EDITOR_TEMPLATE, {
data: { data: {
editor: editor editor: editor
}, },
disableAnimations: this._alwaysDisableAnimations, disableAnimations: this._alwaysDisableAnimations,
ordinal: editor.styleSheet.styleSheetIndex, ordinal: ordinal,
onCreate: function(summary, details, data) { onCreate: function(summary, details, data) {
let editor = data.editor; let editor = data.editor;
editor.summary = summary; editor.summary = summary;

View File

@ -15,6 +15,7 @@ const require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devt
const Editor = require("devtools/sourceeditor/editor"); const Editor = require("devtools/sourceeditor/editor");
const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {}); const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
const {CssLogic} = require("devtools/styleinspector/css-logic"); const {CssLogic} = require("devtools/styleinspector/css-logic");
const {console} = require("resource://gre/modules/devtools/Console.jsm");
Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm"); Cu.import("resource://gre/modules/FileUtils.jsm");
@ -251,11 +252,27 @@ StyleSheetEditor.prototype = {
callback(source); callback(source);
} }
return source; return source;
}, e => {
if (this._isDestroyed) {
console.warn("Could not fetch the source for " +
this.styleSheet.href +
", the editor was destroyed");
Cu.reportError(e);
} else {
throw e;
}
}); });
}, e => { }, e => {
this.emit("error", { key: LOAD_ERROR, append: this.styleSheet.href }); if (this._isDestroyed) {
throw e; console.warn("Could not fetch the source for " +
}) this.styleSheet.href +
", the editor was destroyed");
Cu.reportError(e);
} else {
this.emit("error", { key: LOAD_ERROR, append: this.styleSheet.href });
throw e;
}
});
}, },
/** /**
@ -712,6 +729,7 @@ StyleSheetEditor.prototype = {
this.cssSheet.off("property-change", this._onPropertyChange); this.cssSheet.off("property-change", this._onPropertyChange);
this.cssSheet.off("media-rules-changed", this._onMediaRulesChanged); this.cssSheet.off("media-rules-changed", this._onMediaRulesChanged);
this.styleSheet.off("error", this._onError); this.styleSheet.off("error", this._onError);
this._isDestroyed = true;
} }
} }

View File

@ -9,7 +9,7 @@
// //
thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Unknown sheet source"); thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Unknown sheet source");
const TESTCASE_URI = TEST_BASE + "autocomplete.html"; const TESTCASE_URI = TEST_BASE_HTTP + "autocomplete.html";
const MAX_SUGGESTIONS = 15; const MAX_SUGGESTIONS = 15;
// Pref which decides if CSS autocompletion is enabled in Style Editor or not. // Pref which decides if CSS autocompletion is enabled in Style Editor or not.

View File

@ -8,7 +8,7 @@
// //
thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Unknown sheet source"); thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Unknown sheet source");
const TESTCASE_URI = TEST_BASE + "four.html"; const TESTCASE_URI = TEST_BASE_HTTP + "four.html";
let gUI; let gUI;

View File

@ -2,8 +2,8 @@
/* Any copyright is dedicated to the Public Domain. /* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */ http://creativecommons.org/publicdomain/zero/1.0/ */
const TESTCASE_URI_HTML = TEST_BASE + "simple.html"; const TESTCASE_URI_HTML = TEST_BASE_HTTP + "simple.html";
const TESTCASE_URI_CSS = TEST_BASE + "simple.css"; const TESTCASE_URI_CSS = TEST_BASE_HTTP + "simple.css";
const Cc = Components.classes; const Cc = Components.classes;
const Ci = Components.interfaces; const Ci = Components.interfaces;

View File

@ -9,7 +9,7 @@
// //
thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: summary is undefined"); thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: summary is undefined");
const TESTCASE_URI = TEST_BASE + "simple.html"; const TESTCASE_URI = TEST_BASE_HTTP + "simple.html";
let gUI; let gUI;

View File

@ -11,8 +11,8 @@ thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Unknown sheet source");
let gUI; let gUI;
const FIRST_TEST_PAGE = TEST_BASE + "inline-1.html" const FIRST_TEST_PAGE = TEST_BASE_HTTP + "inline-1.html"
const SECOND_TEST_PAGE = TEST_BASE + "inline-2.html" const SECOND_TEST_PAGE = TEST_BASE_HTTP + "inline-2.html"
const SAVE_PATH = "test.css"; const SAVE_PATH = "test.css";
function test() function test()

View File

@ -9,7 +9,7 @@
// //
thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Unknown sheet source"); thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Unknown sheet source");
const TESTCASE_URI = TEST_BASE + "simple.html"; const TESTCASE_URI = TEST_BASE_HTTP + "simple.html";
let TESTCASE_CSS_SOURCE = "body{background-color:red;"; let TESTCASE_CSS_SOURCE = "body{background-color:red;";

View File

@ -2,7 +2,7 @@
/* Any copyright is dedicated to the Public Domain. /* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */ http://creativecommons.org/publicdomain/zero/1.0/ */
const TESTCASE_URI = TEST_BASE + "nostyle.html"; const TESTCASE_URI = TEST_BASE_HTTP + "nostyle.html";
function test() function test()

View File

@ -9,7 +9,7 @@
// //
thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Unknown sheet source"); thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Unknown sheet source");
const TESTCASE_URI = TEST_BASE + "minified.html"; const TESTCASE_URI = TEST_BASE_HTTP + "minified.html";
let gUI; let gUI;

View File

@ -6,11 +6,11 @@ Components.utils.import("resource://gre/modules/Task.jsm");
let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}); let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
let {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {}); let {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
const TESTCASE_URI_HTML = TEST_BASE + "sourcemaps-watching.html"; const TESTCASE_URI_HTML = TEST_BASE_HTTP + "sourcemaps-watching.html";
const TESTCASE_URI_CSS = TEST_BASE + "sourcemap-css/sourcemaps.css"; const TESTCASE_URI_CSS = TEST_BASE_HTTP + "sourcemap-css/sourcemaps.css";
const TESTCASE_URI_REG_CSS = TEST_BASE + "simple.css"; const TESTCASE_URI_REG_CSS = TEST_BASE_HTTP + "simple.css";
const TESTCASE_URI_SCSS = TEST_BASE + "sourcemap-sass/sourcemaps.scss"; const TESTCASE_URI_SCSS = TEST_BASE_HTTP + "sourcemap-sass/sourcemaps.scss";
const TESTCASE_URI_MAP = TEST_BASE + "sourcemap-css/sourcemaps.css.map"; const TESTCASE_URI_MAP = TEST_BASE_HTTP + "sourcemap-css/sourcemaps.css.map";
const TESTCASE_SCSS_NAME = "sourcemaps.scss"; const TESTCASE_SCSS_NAME = "sourcemaps.scss";
const TRANSITIONS_PREF = "devtools.styleeditor.transitions"; const TRANSITIONS_PREF = "devtools.styleeditor.transitions";

View File

@ -9,7 +9,7 @@
// //
thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Unknown sheet source"); thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Unknown sheet source");
const TESTCASE_URI = TEST_BASE + "four.html"; const TESTCASE_URI = TEST_BASE_HTTP + "four.html";
let gUI; let gUI;

View File

@ -2,7 +2,7 @@
/* Any copyright is dedicated to the Public Domain. /* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */ http://creativecommons.org/publicdomain/zero/1.0/ */
const TESTCASE_URI = TEST_BASE + "simple.html"; const TESTCASE_URI = TEST_BASE_HTTP + "simple.html";
let gOriginalWidth; // these are set by runTests() let gOriginalWidth; // these are set by runTests()
let gOriginalHeight; let gOriginalHeight;

View File

@ -354,6 +354,7 @@ skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
[browser_webconsole_autocomplete-properties-with-non-alphanumeric-names.js] [browser_webconsole_autocomplete-properties-with-non-alphanumeric-names.js]
[browser_console_hide_jsterm_when_devtools_chrome_enabled_false.js] [browser_console_hide_jsterm_when_devtools_chrome_enabled_false.js]
[browser_console_history_persist.js]
[browser_webconsole_output_01.js] [browser_webconsole_output_01.js]
skip-if = e10s # Bug 1042253 - webconsole e10s tests skip-if = e10s # Bug 1042253 - webconsole e10s tests
[browser_webconsole_output_02.js] [browser_webconsole_output_02.js]

View File

@ -0,0 +1,96 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* 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/. */
// Test that console command input is persisted across toolbox loads.
// See Bug 943306.
"use strict";
const TEST_URI = "data:text/html;charset=utf-8,Web Console test for persisting history - bug 943306";
const INPUT_HISTORY_COUNT = 10;
let test = asyncTest(function* () {
info ("Setting custom input history pref to " + INPUT_HISTORY_COUNT);
Services.prefs.setIntPref("devtools.webconsole.inputHistoryCount", INPUT_HISTORY_COUNT);
// First tab: run a bunch of commands and then make sure that you can
// navigate through their history.
yield loadTab(TEST_URI);
let hud1 = yield openConsole();
is (JSON.stringify(hud1.jsterm.history), "[]", "No history on first tab initially");
yield populateInputHistory(hud1);
is (JSON.stringify(hud1.jsterm.history), '["0","1","2","3","4","5","6","7","8","9"]',
"First tab has populated history");
// Second tab: Just make sure that you can navigate through the history
// generated by the first tab.
yield loadTab(TEST_URI);
let hud2 = yield openConsole();
is (JSON.stringify(hud2.jsterm.history), '["0","1","2","3","4","5","6","7","8","9"]',
"Second tab has populated history");
yield testNaviatingHistoryInUI(hud2);
is (JSON.stringify(hud2.jsterm.history), '["0","1","2","3","4","5","6","7","8","9",""]',
"An empty entry has been added in the second tab due to history perusal");
// Third tab: Should have the same history as first tab, but if we run a
// command, then the history of the first and second shouldn't be affected
yield loadTab(TEST_URI);
let hud3 = yield openConsole();
is (JSON.stringify(hud3.jsterm.history), '["0","1","2","3","4","5","6","7","8","9"]',
"Third tab has populated history");
// Set input value separately from execute so UP arrow accurately navigates history.
hud3.jsterm.setInputValue('"hello from third tab"');
hud3.jsterm.execute();
is (JSON.stringify(hud1.jsterm.history), '["0","1","2","3","4","5","6","7","8","9"]',
"First tab history hasn't changed due to command in third tab");
is (JSON.stringify(hud2.jsterm.history), '["0","1","2","3","4","5","6","7","8","9",""]',
"Second tab history hasn't changed due to command in third tab");
is (JSON.stringify(hud3.jsterm.history), '["1","2","3","4","5","6","7","8","9","\\"hello from third tab\\""]',
"Third tab has updated history (and purged the first result) after running a command");
// Fourth tab: Should have the latest command from the third tab, followed
// by the rest of the history from the first tab.
yield loadTab(TEST_URI);
let hud4 = yield openConsole();
is (JSON.stringify(hud4.jsterm.history), '["1","2","3","4","5","6","7","8","9","\\"hello from third tab\\""]',
"Fourth tab has most recent history");
info ("Clearing custom input history pref");
Services.prefs.clearUserPref("devtools.webconsole.inputHistoryCount");
});
/**
* Populate the history by running the following commands:
* [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
*/
function* populateInputHistory(hud) {
let jsterm = hud.jsterm;
let {inputNode} = jsterm;
for (let i = 0; i < INPUT_HISTORY_COUNT; i++) {
// Set input value separately from execute so UP arrow accurately navigates history.
jsterm.setInputValue(i);
jsterm.execute();
}
}
/**
* Check pressing up results in history traversal like:
* [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
*/
function* testNaviatingHistoryInUI(hud) {
let jsterm = hud.jsterm;
let {inputNode} = jsterm;
inputNode.focus();
// Count backwards from original input and make sure that pressing up
// restores this.
for (let i = INPUT_HISTORY_COUNT - 1; i >= 0; i--) {
EventUtils.synthesizeKey("VK_UP", {});
is(inputNode.value, i, "Pressing up restores last input");
}
}

View File

@ -13,6 +13,7 @@ let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
let {require, TargetFactory} = devtools; let {require, TargetFactory} = devtools;
let {Utils: WebConsoleUtils} = require("devtools/toolkit/webconsole/utils"); let {Utils: WebConsoleUtils} = require("devtools/toolkit/webconsole/utils");
let {Messages} = require("devtools/webconsole/console-output"); let {Messages} = require("devtools/webconsole/console-output");
const asyncStorage = require("devtools/toolkit/shared/async-storage");
// promise._reportErrors = true; // please never leave me. // promise._reportErrors = true; // please never leave me.
//Services.prefs.setBoolPref("devtools.debugger.log", true); //Services.prefs.setBoolPref("devtools.debugger.log", true);
@ -322,6 +323,9 @@ let finishTest = Task.async(function* () {
registerCleanupFunction(function*() { registerCleanupFunction(function*() {
gDevTools.testing = false; gDevTools.testing = false;
// Remove stored console commands in between tests
yield asyncStorage.removeItem("webConsoleHistory");
dumpConsoles(); dumpConsoles();
if (HUDService.getBrowserConsole()) { if (HUDService.getBrowserConsole()) {

View File

@ -26,6 +26,8 @@ loader.lazyGetter(this, "ConsoleOutput",
() => require("devtools/webconsole/console-output").ConsoleOutput); () => require("devtools/webconsole/console-output").ConsoleOutput);
loader.lazyGetter(this, "Messages", loader.lazyGetter(this, "Messages",
() => require("devtools/webconsole/console-output").Messages); () => require("devtools/webconsole/console-output").Messages);
loader.lazyGetter(this, "asyncStorage",
() => require("devtools/toolkit/shared/async-storage"));
loader.lazyImporter(this, "EnvironmentClient", "resource://gre/modules/devtools/dbg-client.jsm"); loader.lazyImporter(this, "EnvironmentClient", "resource://gre/modules/devtools/dbg-client.jsm");
loader.lazyImporter(this, "ObjectClient", "resource://gre/modules/devtools/dbg-client.jsm"); loader.lazyImporter(this, "ObjectClient", "resource://gre/modules/devtools/dbg-client.jsm");
loader.lazyImporter(this, "VariablesView", "resource:///modules/devtools/VariablesView.jsm"); loader.lazyImporter(this, "VariablesView", "resource:///modules/devtools/VariablesView.jsm");
@ -176,6 +178,7 @@ const MIN_FONT_SIZE = 10;
const PREF_CONNECTION_TIMEOUT = "devtools.debugger.remote-timeout"; const PREF_CONNECTION_TIMEOUT = "devtools.debugger.remote-timeout";
const PREF_PERSISTLOG = "devtools.webconsole.persistlog"; const PREF_PERSISTLOG = "devtools.webconsole.persistlog";
const PREF_MESSAGE_TIMESTAMP = "devtools.webconsole.timestampMessages"; const PREF_MESSAGE_TIMESTAMP = "devtools.webconsole.timestampMessages";
const PREF_INPUT_HISTORY_COUNT = "devtools.webconsole.inputHistoryCount";
/** /**
* A WebConsoleFrame instance is an interactive console initialized *per target* * A WebConsoleFrame instance is an interactive console initialized *per target*
@ -443,12 +446,29 @@ WebConsoleFrame.prototype = {
/** /**
* Initialize the WebConsoleFrame instance. * Initialize the WebConsoleFrame instance.
* @return object * @return object
* A promise object for the initialization. * A promise object that resolves once the frame is ready to use.
*/ */
init: function WCF_init() init: function()
{ {
this._initUI(); this._initUI();
return this._initConnection(); let connectionInited = this._initConnection();
// Don't reject if the history fails to load for some reason.
// This would be fine, the panel will just start with empty history.
let allReady = this.jsterm.historyLoaded.catch(() => {}).then(() => {
return connectionInited;
});
// This notification is only used in tests. Don't chain it onto
// the returned promise because the console panel needs to be attached
// to the toolbox before the web-console-created event is receieved.
let notifyObservers = () => {
let id = WebConsoleUtils.supportsString(this.hudId);
Services.obs.notifyObservers(id, "web-console-created", null);
};
allReady.then(notifyObservers, notifyObservers);
return allReady;
}, },
/** /**
@ -475,9 +495,6 @@ WebConsoleFrame.prototype = {
aReason.error + ": " + aReason.message); aReason.error + ": " + aReason.message);
this.outputMessage(CATEGORY_JS, node, [aReason]); this.outputMessage(CATEGORY_JS, node, [aReason]);
this._initDefer.reject(aReason); this._initDefer.reject(aReason);
}).then(() => {
let id = WebConsoleUtils.supportsString(this.hudId);
Services.obs.notifyObservers(id, "web-console-created", null);
}); });
return this._initDefer.promise; return this._initDefer.promise;
@ -3054,17 +3071,11 @@ function JSTerm(aWebConsoleFrame)
{ {
this.hud = aWebConsoleFrame; this.hud = aWebConsoleFrame;
this.hudId = this.hud.hudId; this.hudId = this.hud.hudId;
this.inputHistoryCount = Services.prefs.getIntPref(PREF_INPUT_HISTORY_COUNT);
this.lastCompletion = { value: null }; this.lastCompletion = { value: null };
this.history = []; this._loadHistory();
// Holds the number of entries in history. This value is incremented in
// this.execute().
this.historyIndex = 0; // incremented on this.execute()
// Holds the index of the history entry that the user is currently viewing.
// This is reset to this.history.length when this.execute() is invoked.
this.historyPlaceHolder = 0;
this._objectActorsInVariablesViews = new Map(); this._objectActorsInVariablesViews = new Map();
this._keyPress = this._keyPress.bind(this); this._keyPress = this._keyPress.bind(this);
@ -3079,6 +3090,38 @@ function JSTerm(aWebConsoleFrame)
JSTerm.prototype = { JSTerm.prototype = {
SELECTED_FRAME: -1, SELECTED_FRAME: -1,
/**
* Load the console history from previous sessions.
* @private
*/
_loadHistory: function() {
this.history = [];
this.historyIndex = this.historyPlaceHolder = 0;
this.historyLoaded = asyncStorage.getItem("webConsoleHistory").then(value => {
if (Array.isArray(value)) {
// Since it was gotten asynchronously, there could be items already in
// the history. It's not likely but stick them onto the end anyway.
this.history = value.concat(this.history);
// Holds the number of entries in history. This value is incremented in
// this.execute().
this.historyIndex = this.history.length;
// Holds the index of the history entry that the user is currently viewing.
// This is reset to this.history.length when this.execute() is invoked.
this.historyPlaceHolder = this.history.length;
}
}, console.error);
},
/**
* Stores the console history for future sessions.
*/
storeHistory: function() {
asyncStorage.setItem("webConsoleHistory", this.history);
},
/** /**
* Stores the data for the last completion. * Stores the data for the last completion.
* @type object * @type object
@ -3388,6 +3431,12 @@ JSTerm.prototype = {
// value that was not evaluated yet. // value that was not evaluated yet.
this.history[this.historyIndex++] = aExecuteString; this.history[this.historyIndex++] = aExecuteString;
this.historyPlaceHolder = this.history.length; this.historyPlaceHolder = this.history.length;
if (this.history.length > this.inputHistoryCount) {
this.history.splice(0, this.history.length - this.inputHistoryCount);
this.historyIndex = this.historyPlaceHolder = this.history.length;
}
this.storeHistory();
WebConsoleUtils.usageCount++; WebConsoleUtils.usageCount++;
this.setInputValue(""); this.setInputValue("");
this.clearCompletion(); this.clearCompletion();

View File

@ -495,9 +495,14 @@ label.requests-menu-status-code {
} }
.tabpanel-summary-value { .tabpanel-summary-value {
color: inherit;
-moz-padding-start: 3px; -moz-padding-start: 3px;
} }
.theme-dark .tabpanel-summary-value {
color: var(--theme-selection-color);
}
/* Headers tabpanel */ /* Headers tabpanel */
#headers-summary-status, #headers-summary-status,

View File

@ -0,0 +1,28 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#include "BluetoothInterfaceHelpers.h"
BEGIN_BLUETOOTH_NAMESPACE
//
// Conversion
//
nsresult
Convert(nsresult aIn, BluetoothStatus& aOut)
{
if (NS_SUCCEEDED(aIn)) {
aOut = STATUS_SUCCESS;
} else if (aIn == NS_ERROR_OUT_OF_MEMORY) {
aOut = STATUS_NOMEM;
} else {
aOut = STATUS_FAIL;
}
return NS_OK;
}
END_BLUETOOTH_NAMESPACE

View File

@ -12,6 +12,13 @@
BEGIN_BLUETOOTH_NAMESPACE BEGIN_BLUETOOTH_NAMESPACE
//
// Conversion
//
nsresult
Convert(nsresult aIn, BluetoothStatus& aOut);
// //
// Result handling // Result handling
// //
@ -49,6 +56,9 @@ public:
static void static void
Dispatch(Obj* aObj, Res (Obj::*aMethod)(), const InitOp& aInitOp) Dispatch(Obj* aObj, Res (Obj::*aMethod)(), const InitOp& aInitOp)
{ {
if (!aObj) {
return; // silently return if no result runnable has been given
}
nsRefPtr<SelfType> runnable = Create(aObj, aMethod, aInitOp); nsRefPtr<SelfType> runnable = Create(aObj, aMethod, aInitOp);
if (!runnable) { if (!runnable) {
BT_LOGR("BluetoothResultRunnable0::Create failed"); BT_LOGR("BluetoothResultRunnable0::Create failed");
@ -108,6 +118,9 @@ public:
static void static void
Dispatch(Obj* aObj, Res (Obj::*aMethod)(Arg1), const InitOp& aInitOp) Dispatch(Obj* aObj, Res (Obj::*aMethod)(Arg1), const InitOp& aInitOp)
{ {
if (!aObj) {
return; // silently return if no result runnable has been given
}
nsRefPtr<SelfType> runnable = Create(aObj, aMethod, aInitOp); nsRefPtr<SelfType> runnable = Create(aObj, aMethod, aInitOp);
if (!runnable) { if (!runnable) {
BT_LOGR("BluetoothResultRunnable1::Create failed"); BT_LOGR("BluetoothResultRunnable1::Create failed");
@ -174,6 +187,9 @@ public:
Dispatch(Obj* aObj, Res (Obj::*aMethod)(Arg1, Arg2, Arg3), Dispatch(Obj* aObj, Res (Obj::*aMethod)(Arg1, Arg2, Arg3),
const InitOp& aInitOp) const InitOp& aInitOp)
{ {
if (!aObj) {
return; // silently return if no result runnable has been given
}
nsRefPtr<SelfType> runnable = Create(aObj, aMethod, aInitOp); nsRefPtr<SelfType> runnable = Create(aObj, aMethod, aInitOp);
if (!runnable) { if (!runnable) {
BT_LOGR("BluetoothResultRunnable3::Create failed"); BT_LOGR("BluetoothResultRunnable3::Create failed");

View File

@ -335,6 +335,9 @@ public:
{ {
BT_WARNING("BluetoothAvrcpInterface::Cleanup failed: %d", BT_WARNING("BluetoothAvrcpInterface::Cleanup failed: %d",
(int)aStatus); (int)aStatus);
sBtAvrcpInterface = nullptr;
if (mRes) { if (mRes) {
if (aStatus == STATUS_UNSUPPORTED) { if (aStatus == STATUS_UNSUPPORTED) {
/* Not all versions of Bluedroid support AVRCP. So if the /* Not all versions of Bluedroid support AVRCP. So if the
@ -371,6 +374,9 @@ public:
{ {
BT_WARNING("BluetoothA2dpInterface::Cleanup failed: %d", BT_WARNING("BluetoothA2dpInterface::Cleanup failed: %d",
(int)aStatus); (int)aStatus);
sBtA2dpInterface = nullptr;
if (mRes) { if (mRes) {
mRes->OnError(NS_ERROR_FAILURE); mRes->OnError(NS_ERROR_FAILURE);
} }

View File

@ -395,7 +395,7 @@ BluetoothDaemonA2dpInterface::Init(
nsresult rv = mModule->RegisterModule(BluetoothDaemonA2dpModule::SERVICE_ID, nsresult rv = mModule->RegisterModule(BluetoothDaemonA2dpModule::SERVICE_ID,
0x00, BluetoothDaemonA2dpModule::MAX_NUM_CLIENTS, res); 0x00, BluetoothDaemonA2dpModule::MAX_NUM_CLIENTS, res);
if (NS_FAILED(rv) && aRes) { if (NS_FAILED(rv) && aRes) {
DispatchError(aRes, STATUS_FAIL); DispatchError(aRes, rv);
} }
} }
@ -443,8 +443,12 @@ void
BluetoothDaemonA2dpInterface::Cleanup( BluetoothDaemonA2dpInterface::Cleanup(
BluetoothA2dpResultHandler* aRes) BluetoothA2dpResultHandler* aRes)
{ {
mModule->UnregisterModule(BluetoothDaemonA2dpModule::SERVICE_ID, nsresult rv = mModule->UnregisterModule(
new CleanupResultHandler(mModule, aRes)); BluetoothDaemonA2dpModule::SERVICE_ID,
new CleanupResultHandler(mModule, aRes));
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
/* Connect / Disconnect */ /* Connect / Disconnect */
@ -455,7 +459,10 @@ BluetoothDaemonA2dpInterface::Connect(
{ {
MOZ_ASSERT(mModule); MOZ_ASSERT(mModule);
mModule->ConnectCmd(aBdAddr, aRes); nsresult rv = mModule->ConnectCmd(aBdAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
@ -464,7 +471,10 @@ BluetoothDaemonA2dpInterface::Disconnect(
{ {
MOZ_ASSERT(mModule); MOZ_ASSERT(mModule);
mModule->DisconnectCmd(aBdAddr, aRes); nsresult rv = mModule->DisconnectCmd(aBdAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
@ -477,4 +487,16 @@ BluetoothDaemonA2dpInterface::DispatchError(
ConstantInitOp1<BluetoothStatus>(aStatus)); ConstantInitOp1<BluetoothStatus>(aStatus));
} }
void
BluetoothDaemonA2dpInterface::DispatchError(
BluetoothA2dpResultHandler* aRes, nsresult aRv)
{
BluetoothStatus status;
if (NS_WARN_IF(NS_FAILED(Convert(aRv, status)))) {
status = STATUS_FAIL;
}
DispatchError(aRes, status);
}
END_BLUETOOTH_NAMESPACE END_BLUETOOTH_NAMESPACE

View File

@ -155,6 +155,7 @@ public:
private: private:
void DispatchError(BluetoothA2dpResultHandler* aRes, void DispatchError(BluetoothA2dpResultHandler* aRes,
BluetoothStatus aStatus); BluetoothStatus aStatus);
void DispatchError(BluetoothA2dpResultHandler* aRes, nsresult aRv);
BluetoothDaemonA2dpModule* mModule; BluetoothDaemonA2dpModule* mModule;
}; };

View File

@ -893,7 +893,7 @@ BluetoothDaemonAvrcpInterface::Init(
BluetoothDaemonAvrcpModule::MAX_NUM_CLIENTS, 0x00, res); BluetoothDaemonAvrcpModule::MAX_NUM_CLIENTS, 0x00, res);
if (NS_FAILED(rv) && aRes) { if (NS_FAILED(rv) && aRes) {
DispatchError(aRes, STATUS_FAIL); DispatchError(aRes, rv);
} }
} }
@ -943,8 +943,12 @@ BluetoothDaemonAvrcpInterface::Cleanup(
{ {
MOZ_ASSERT(mModule); MOZ_ASSERT(mModule);
mModule->UnregisterModule(BluetoothDaemonAvrcpModule::SERVICE_ID, nsresult rv = mModule->UnregisterModule(
new CleanupResultHandler(mModule, aRes)); BluetoothDaemonAvrcpModule::SERVICE_ID,
new CleanupResultHandler(mModule, aRes));
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
@ -954,7 +958,11 @@ BluetoothDaemonAvrcpInterface::GetPlayStatusRsp(
{ {
MOZ_ASSERT(mModule); MOZ_ASSERT(mModule);
mModule->GetPlayStatusRspCmd(aPlayStatus, aSongLen, aSongPos, aRes); nsresult rv = mModule->GetPlayStatusRspCmd(aPlayStatus, aSongLen,
aSongPos, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
@ -964,7 +972,10 @@ BluetoothDaemonAvrcpInterface::ListPlayerAppAttrRsp(
{ {
MOZ_ASSERT(mModule); MOZ_ASSERT(mModule);
mModule->ListPlayerAppAttrRspCmd(aNumAttr, aPAttrs, aRes); nsresult rv = mModule->ListPlayerAppAttrRspCmd(aNumAttr, aPAttrs, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
@ -973,7 +984,10 @@ BluetoothDaemonAvrcpInterface::ListPlayerAppValueRsp(
{ {
MOZ_ASSERT(mModule); MOZ_ASSERT(mModule);
mModule->ListPlayerAppValueRspCmd(aNumVal, aPVals, aRes); nsresult rv = mModule->ListPlayerAppValueRspCmd(aNumVal, aPVals, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
@ -983,7 +997,11 @@ BluetoothDaemonAvrcpInterface::GetPlayerAppValueRsp(
{ {
MOZ_ASSERT(mModule); MOZ_ASSERT(mModule);
mModule->GetPlayerAppValueRspCmd(aNumAttrs, aIds, aValues, aRes); nsresult rv = mModule->GetPlayerAppValueRspCmd(aNumAttrs, aIds,
aValues, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
@ -993,7 +1011,11 @@ BluetoothDaemonAvrcpInterface::GetPlayerAppAttrTextRsp(
{ {
MOZ_ASSERT(mModule); MOZ_ASSERT(mModule);
mModule->GetPlayerAppAttrTextRspCmd(aNumAttr, aIds, aTexts, aRes); nsresult rv = mModule->GetPlayerAppAttrTextRspCmd(aNumAttr, aIds,
aTexts, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
@ -1003,7 +1025,11 @@ BluetoothDaemonAvrcpInterface::GetPlayerAppValueTextRsp(
{ {
MOZ_ASSERT(mModule); MOZ_ASSERT(mModule);
mModule->GetPlayerAppValueTextRspCmd(aNumVal, aIds, aTexts, aRes); nsresult rv = mModule->GetPlayerAppValueTextRspCmd(aNumVal, aIds,
aTexts, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
@ -1013,7 +1039,10 @@ BluetoothDaemonAvrcpInterface::GetElementAttrRsp(
{ {
MOZ_ASSERT(mModule); MOZ_ASSERT(mModule);
mModule->GetElementAttrRspCmd(aNumAttr, aAttr, aRes); nsresult rv = mModule->GetElementAttrRspCmd(aNumAttr, aAttr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
@ -1022,7 +1051,10 @@ BluetoothDaemonAvrcpInterface::SetPlayerAppValueRsp(
{ {
MOZ_ASSERT(mModule); MOZ_ASSERT(mModule);
mModule->SetPlayerAppValueRspCmd(aRspStatus, aRes); nsresult rv = mModule->SetPlayerAppValueRspCmd(aRspStatus, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
@ -1034,7 +1066,11 @@ BluetoothDaemonAvrcpInterface::RegisterNotificationRsp(
{ {
MOZ_ASSERT(mModule); MOZ_ASSERT(mModule);
mModule->RegisterNotificationRspCmd(aEvent, aType, aParam, aRes); nsresult rv = mModule->RegisterNotificationRspCmd(aEvent, aType,
aParam, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
@ -1043,7 +1079,10 @@ BluetoothDaemonAvrcpInterface::SetVolume(
{ {
MOZ_ASSERT(mModule); MOZ_ASSERT(mModule);
mModule->SetVolumeCmd(aVolume, aRes); nsresult rv = mModule->SetVolumeCmd(aVolume, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
@ -1056,4 +1095,16 @@ BluetoothDaemonAvrcpInterface::DispatchError(
ConstantInitOp1<BluetoothStatus>(aStatus)); ConstantInitOp1<BluetoothStatus>(aStatus));
} }
void
BluetoothDaemonAvrcpInterface::DispatchError(
BluetoothAvrcpResultHandler* aRes, nsresult aRv)
{
BluetoothStatus status;
if (NS_WARN_IF(NS_FAILED(Convert(aRv, status)))) {
status = STATUS_FAIL;
}
DispatchError(aRes, status);
}
END_BLUETOOTH_NAMESPACE END_BLUETOOTH_NAMESPACE

View File

@ -346,6 +346,7 @@ public:
private: private:
void DispatchError(BluetoothAvrcpResultHandler* aRes, void DispatchError(BluetoothAvrcpResultHandler* aRes,
BluetoothStatus aStatus); BluetoothStatus aStatus);
void DispatchError(BluetoothAvrcpResultHandler* aRes, nsresult aRv);
BluetoothDaemonAvrcpModule* mModule; BluetoothDaemonAvrcpModule* mModule;
}; };

View File

@ -1512,7 +1512,7 @@ BluetoothDaemonHandsfreeInterface::Init(
aMaxNumClients, res); aMaxNumClients, res);
if (NS_FAILED(rv) && aRes) { if (NS_FAILED(rv) && aRes) {
DispatchError(aRes, STATUS_FAIL); DispatchError(aRes, rv);
} }
} }
@ -1532,6 +1532,7 @@ public:
{ {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
BT_LOGR("%s:%d", __func__, __LINE__);
if (mRes) { if (mRes) {
mRes->OnError(aStatus); mRes->OnError(aStatus);
} }
@ -1541,6 +1542,7 @@ public:
{ {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
BT_LOGR("%s:%d", __func__, __LINE__);
// Clear notification handler _after_ module has been // Clear notification handler _after_ module has been
// unregistered. While unregistering the module, we might // unregistered. While unregistering the module, we might
// still receive notifications. // still receive notifications.
@ -1560,8 +1562,14 @@ void
BluetoothDaemonHandsfreeInterface::Cleanup( BluetoothDaemonHandsfreeInterface::Cleanup(
BluetoothHandsfreeResultHandler* aRes) BluetoothHandsfreeResultHandler* aRes)
{ {
mModule->UnregisterModule(BluetoothDaemonHandsfreeModule::SERVICE_ID, BT_LOGR("%s:%d", __func__, __LINE__);
new CleanupResultHandler(mModule, aRes)); nsresult rv = mModule->UnregisterModule(
BluetoothDaemonHandsfreeModule::SERVICE_ID,
new CleanupResultHandler(mModule, aRes));
BT_LOGR("%s:%d", __func__, __LINE__);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
/* Connect / Disconnect */ /* Connect / Disconnect */
@ -1572,7 +1580,10 @@ BluetoothDaemonHandsfreeInterface::Connect(
{ {
MOZ_ASSERT(mModule); MOZ_ASSERT(mModule);
mModule->ConnectCmd(aBdAddr, aRes); nsresult rv = mModule->ConnectCmd(aBdAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
@ -1581,7 +1592,10 @@ BluetoothDaemonHandsfreeInterface::Disconnect(
{ {
MOZ_ASSERT(mModule); MOZ_ASSERT(mModule);
mModule->DisconnectCmd(aBdAddr, aRes); nsresult rv = mModule->DisconnectCmd(aBdAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
@ -1590,7 +1604,10 @@ BluetoothDaemonHandsfreeInterface::ConnectAudio(
{ {
MOZ_ASSERT(mModule); MOZ_ASSERT(mModule);
mModule->ConnectAudioCmd(aBdAddr, aRes); nsresult rv = mModule->ConnectAudioCmd(aBdAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
@ -1599,7 +1616,10 @@ BluetoothDaemonHandsfreeInterface::DisconnectAudio(
{ {
MOZ_ASSERT(mModule); MOZ_ASSERT(mModule);
mModule->DisconnectAudioCmd(aBdAddr, aRes); nsresult rv = mModule->DisconnectAudioCmd(aBdAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
/* Voice Recognition */ /* Voice Recognition */
@ -1610,7 +1630,10 @@ BluetoothDaemonHandsfreeInterface::StartVoiceRecognition(
{ {
MOZ_ASSERT(mModule); MOZ_ASSERT(mModule);
mModule->StartVoiceRecognitionCmd(aBdAddr, aRes); nsresult rv = mModule->StartVoiceRecognitionCmd(aBdAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
@ -1619,7 +1642,10 @@ BluetoothDaemonHandsfreeInterface::StopVoiceRecognition(
{ {
MOZ_ASSERT(mModule); MOZ_ASSERT(mModule);
mModule->StopVoiceRecognitionCmd(aBdAddr, aRes); nsresult rv = mModule->StopVoiceRecognitionCmd(aBdAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
/* Volume */ /* Volume */
@ -1631,7 +1657,10 @@ BluetoothDaemonHandsfreeInterface::VolumeControl(
{ {
MOZ_ASSERT(mModule); MOZ_ASSERT(mModule);
mModule->VolumeControlCmd(aType, aVolume, aBdAddr, aRes); nsresult rv = mModule->VolumeControlCmd(aType, aVolume, aBdAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
/* Device status */ /* Device status */
@ -1644,8 +1673,12 @@ BluetoothDaemonHandsfreeInterface::DeviceStatusNotification(
{ {
MOZ_ASSERT(mModule); MOZ_ASSERT(mModule);
mModule->DeviceStatusNotificationCmd(aNtkState, aSvcType, aSignal, nsresult rv = mModule->DeviceStatusNotificationCmd(aNtkState, aSvcType,
aBattChg, aRes); aSignal, aBattChg,
aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
/* Responses */ /* Responses */
@ -1657,7 +1690,10 @@ BluetoothDaemonHandsfreeInterface::CopsResponse(
{ {
MOZ_ASSERT(mModule); MOZ_ASSERT(mModule);
mModule->CopsResponseCmd(aCops, aBdAddr, aRes); nsresult rv = mModule->CopsResponseCmd(aCops, aBdAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
@ -1669,8 +1705,12 @@ BluetoothDaemonHandsfreeInterface::CindResponse(
{ {
MOZ_ASSERT(mModule); MOZ_ASSERT(mModule);
mModule->CindResponseCmd(aSvc, aNumActive, aNumHeld, aCallSetupState, nsresult rv = mModule->CindResponseCmd(aSvc, aNumActive, aNumHeld,
aSignal, aRoam, aBattChg, aBdAddr, aRes); aCallSetupState, aSignal,
aRoam, aBattChg, aBdAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
@ -1680,7 +1720,10 @@ BluetoothDaemonHandsfreeInterface::FormattedAtResponse(
{ {
MOZ_ASSERT(mModule); MOZ_ASSERT(mModule);
mModule->FormattedAtResponseCmd(aRsp, aBdAddr, aRes); nsresult rv = mModule->FormattedAtResponseCmd(aRsp, aBdAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
@ -1690,7 +1733,11 @@ BluetoothDaemonHandsfreeInterface::AtResponse(
{ {
MOZ_ASSERT(mModule); MOZ_ASSERT(mModule);
mModule->AtResponseCmd(aResponseCode, aErrorCode, aBdAddr, aRes); nsresult rv = mModule->AtResponseCmd(aResponseCode, aErrorCode,
aBdAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
@ -1706,8 +1753,11 @@ BluetoothDaemonHandsfreeInterface::ClccResponse(
{ {
MOZ_ASSERT(mModule); MOZ_ASSERT(mModule);
mModule->ClccResponseCmd(aIndex, aDir, aState, aMode, aMpty, aNumber, nsresult rv = mModule->ClccResponseCmd(aIndex, aDir, aState, aMode, aMpty,
aType, aBdAddr, aRes); aNumber, aType, aBdAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
/* Phone State */ /* Phone State */
@ -1722,8 +1772,12 @@ BluetoothDaemonHandsfreeInterface::PhoneStateChange(
{ {
MOZ_ASSERT(mModule); MOZ_ASSERT(mModule);
mModule->PhoneStateChangeCmd(aNumActive, aNumHeld, aCallSetupState, aNumber, nsresult rv = mModule->PhoneStateChangeCmd(aNumActive, aNumHeld,
aType, aRes); aCallSetupState, aNumber,
aType, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
/* Wide Band Speech */ /* Wide Band Speech */
@ -1735,7 +1789,10 @@ BluetoothDaemonHandsfreeInterface::ConfigureWbs(
{ {
MOZ_ASSERT(mModule); MOZ_ASSERT(mModule);
mModule->ConfigureWbsCmd(aBdAddr, aConfig, aRes); nsresult rv = mModule->ConfigureWbsCmd(aBdAddr, aConfig, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
@ -1748,4 +1805,16 @@ BluetoothDaemonHandsfreeInterface::DispatchError(
ConstantInitOp1<BluetoothStatus>(aStatus)); ConstantInitOp1<BluetoothStatus>(aStatus));
} }
void
BluetoothDaemonHandsfreeInterface::DispatchError(
BluetoothHandsfreeResultHandler* aRes, nsresult aRv)
{
BluetoothStatus status;
if (NS_WARN_IF(NS_FAILED(Convert(aRv, status)))) {
status = STATUS_FAIL;
}
DispatchError(aRes, status);
}
END_BLUETOOTH_NAMESPACE END_BLUETOOTH_NAMESPACE

View File

@ -472,6 +472,7 @@ public:
private: private:
void DispatchError(BluetoothHandsfreeResultHandler* aRes, void DispatchError(BluetoothHandsfreeResultHandler* aRes,
BluetoothStatus aStatus); BluetoothStatus aStatus);
void DispatchError(BluetoothHandsfreeResultHandler* aRes, nsresult aRv);
BluetoothDaemonHandsfreeModule* mModule; BluetoothDaemonHandsfreeModule* mModule;
}; };

View File

@ -1893,7 +1893,15 @@ void
BluetoothDaemonInterface::OnDisconnect(enum Channel aChannel) BluetoothDaemonInterface::OnDisconnect(enum Channel aChannel)
{ {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mResultHandlerQ.IsEmpty());
if (mResultHandlerQ.IsEmpty()) {
if (sNotificationHandler) {
// Bluetooth daemon crashed; clear state
sNotificationHandler->AdapterStateChangedNotification(false);
sNotificationHandler = nullptr;
}
return;
}
switch (aChannel) { switch (aChannel) {
case CMD_CHANNEL: case CMD_CHANNEL:
@ -2194,19 +2202,31 @@ BluetoothDaemonInterface::Cleanup(BluetoothResultHandler* aRes)
mResultHandlerQ.AppendElement(aRes); mResultHandlerQ.AppendElement(aRes);
// Cleanup, step 1: Unregister Socket module // Cleanup, step 1: Unregister Socket module
mProtocol->UnregisterModuleCmd(0x02, new CleanupResultHandler(this)); nsresult rv = mProtocol->UnregisterModuleCmd(
0x02, new CleanupResultHandler(this));
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
BluetoothDaemonInterface::Enable(BluetoothResultHandler* aRes) BluetoothDaemonInterface::Enable(BluetoothResultHandler* aRes)
{ {
static_cast<BluetoothDaemonCoreModule*>(mProtocol)->EnableCmd(aRes); nsresult rv =
static_cast<BluetoothDaemonCoreModule*>(mProtocol)->EnableCmd(aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
BluetoothDaemonInterface::Disable(BluetoothResultHandler* aRes) BluetoothDaemonInterface::Disable(BluetoothResultHandler* aRes)
{ {
static_cast<BluetoothDaemonCoreModule*>(mProtocol)->DisableCmd(aRes); nsresult rv =
static_cast<BluetoothDaemonCoreModule*>(mProtocol)->DisableCmd(aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
/* Adapter Properties */ /* Adapter Properties */
@ -2214,24 +2234,33 @@ BluetoothDaemonInterface::Disable(BluetoothResultHandler* aRes)
void void
BluetoothDaemonInterface::GetAdapterProperties(BluetoothResultHandler* aRes) BluetoothDaemonInterface::GetAdapterProperties(BluetoothResultHandler* aRes)
{ {
static_cast<BluetoothDaemonCoreModule*> nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->GetAdapterPropertiesCmd(aRes); (mProtocol)->GetAdapterPropertiesCmd(aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
BluetoothDaemonInterface::GetAdapterProperty(const nsAString& aName, BluetoothDaemonInterface::GetAdapterProperty(const nsAString& aName,
BluetoothResultHandler* aRes) BluetoothResultHandler* aRes)
{ {
static_cast<BluetoothDaemonCoreModule*> nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->GetAdapterPropertyCmd(aName, aRes); (mProtocol)->GetAdapterPropertyCmd(aName, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
BluetoothDaemonInterface::SetAdapterProperty( BluetoothDaemonInterface::SetAdapterProperty(
const BluetoothNamedValue& aProperty, BluetoothResultHandler* aRes) const BluetoothNamedValue& aProperty, BluetoothResultHandler* aRes)
{ {
static_cast<BluetoothDaemonCoreModule*> nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->SetAdapterPropertyCmd(aProperty, aRes); (mProtocol)->SetAdapterPropertyCmd(aProperty, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
/* Remote Device Properties */ /* Remote Device Properties */
@ -2240,8 +2269,11 @@ void
BluetoothDaemonInterface::GetRemoteDeviceProperties( BluetoothDaemonInterface::GetRemoteDeviceProperties(
const nsAString& aRemoteAddr, BluetoothResultHandler* aRes) const nsAString& aRemoteAddr, BluetoothResultHandler* aRes)
{ {
static_cast<BluetoothDaemonCoreModule*> nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->GetRemoteDevicePropertiesCmd(aRemoteAddr, aRes); (mProtocol)->GetRemoteDevicePropertiesCmd(aRemoteAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
@ -2249,8 +2281,11 @@ BluetoothDaemonInterface::GetRemoteDeviceProperty(
const nsAString& aRemoteAddr, const nsAString& aName, const nsAString& aRemoteAddr, const nsAString& aName,
BluetoothResultHandler* aRes) BluetoothResultHandler* aRes)
{ {
static_cast<BluetoothDaemonCoreModule*> nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->GetRemoteDevicePropertyCmd(aRemoteAddr, aName, aRes); (mProtocol)->GetRemoteDevicePropertyCmd(aRemoteAddr, aName, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
@ -2258,8 +2293,11 @@ BluetoothDaemonInterface::SetRemoteDeviceProperty(
const nsAString& aRemoteAddr, const BluetoothNamedValue& aProperty, const nsAString& aRemoteAddr, const BluetoothNamedValue& aProperty,
BluetoothResultHandler* aRes) BluetoothResultHandler* aRes)
{ {
static_cast<BluetoothDaemonCoreModule*> nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->SetRemoteDevicePropertyCmd(aRemoteAddr, aProperty, aRes); (mProtocol)->SetRemoteDevicePropertyCmd(aRemoteAddr, aProperty, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
/* Remote Services */ /* Remote Services */
@ -2269,16 +2307,22 @@ BluetoothDaemonInterface::GetRemoteServiceRecord(const nsAString& aRemoteAddr,
const uint8_t aUuid[16], const uint8_t aUuid[16],
BluetoothResultHandler* aRes) BluetoothResultHandler* aRes)
{ {
static_cast<BluetoothDaemonCoreModule*>( nsresult rv = static_cast<BluetoothDaemonCoreModule*>
mProtocol)->GetRemoteServiceRecordCmd(aRemoteAddr, aUuid, aRes); (mProtocol)->GetRemoteServiceRecordCmd(aRemoteAddr, aUuid, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
BluetoothDaemonInterface::GetRemoteServices(const nsAString& aRemoteAddr, BluetoothDaemonInterface::GetRemoteServices(const nsAString& aRemoteAddr,
BluetoothResultHandler* aRes) BluetoothResultHandler* aRes)
{ {
static_cast<BluetoothDaemonCoreModule*>( nsresult rv = static_cast<BluetoothDaemonCoreModule*>
mProtocol)->GetRemoteServicesCmd(aRemoteAddr, aRes); (mProtocol)->GetRemoteServicesCmd(aRemoteAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
/* Discovery */ /* Discovery */
@ -2286,14 +2330,21 @@ BluetoothDaemonInterface::GetRemoteServices(const nsAString& aRemoteAddr,
void void
BluetoothDaemonInterface::StartDiscovery(BluetoothResultHandler* aRes) BluetoothDaemonInterface::StartDiscovery(BluetoothResultHandler* aRes)
{ {
static_cast<BluetoothDaemonCoreModule*>(mProtocol)->StartDiscoveryCmd(aRes); nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->StartDiscoveryCmd(aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
BluetoothDaemonInterface::CancelDiscovery(BluetoothResultHandler* aRes) BluetoothDaemonInterface::CancelDiscovery(BluetoothResultHandler* aRes)
{ {
static_cast<BluetoothDaemonCoreModule*> nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->CancelDiscoveryCmd(aRes); (mProtocol)->CancelDiscoveryCmd(aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
/* Bonds */ /* Bonds */
@ -2303,24 +2354,33 @@ BluetoothDaemonInterface::CreateBond(const nsAString& aBdAddr,
BluetoothTransport aTransport, BluetoothTransport aTransport,
BluetoothResultHandler* aRes) BluetoothResultHandler* aRes)
{ {
static_cast<BluetoothDaemonCoreModule*> nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->CreateBondCmd(aBdAddr, aTransport, aRes); (mProtocol)->CreateBondCmd(aBdAddr, aTransport, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
BluetoothDaemonInterface::RemoveBond(const nsAString& aBdAddr, BluetoothDaemonInterface::RemoveBond(const nsAString& aBdAddr,
BluetoothResultHandler* aRes) BluetoothResultHandler* aRes)
{ {
static_cast<BluetoothDaemonCoreModule*> nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->RemoveBondCmd(aBdAddr, aRes); (mProtocol)->RemoveBondCmd(aBdAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
BluetoothDaemonInterface::CancelBond(const nsAString& aBdAddr, BluetoothDaemonInterface::CancelBond(const nsAString& aBdAddr,
BluetoothResultHandler* aRes) BluetoothResultHandler* aRes)
{ {
static_cast<BluetoothDaemonCoreModule*> nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->CancelBondCmd(aBdAddr, aRes); (mProtocol)->CancelBondCmd(aBdAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
/* Connection */ /* Connection */
@ -2339,8 +2399,11 @@ BluetoothDaemonInterface::PinReply(const nsAString& aBdAddr, bool aAccept,
const nsAString& aPinCode, const nsAString& aPinCode,
BluetoothResultHandler* aRes) BluetoothResultHandler* aRes)
{ {
static_cast<BluetoothDaemonCoreModule*> nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->PinReplyCmd(aBdAddr, aAccept, aPinCode, aRes); (mProtocol)->PinReplyCmd(aBdAddr, aAccept, aPinCode, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
@ -2349,8 +2412,11 @@ BluetoothDaemonInterface::SspReply(const nsAString& aBdAddr,
bool aAccept, uint32_t aPasskey, bool aAccept, uint32_t aPasskey,
BluetoothResultHandler* aRes) BluetoothResultHandler* aRes)
{ {
static_cast<BluetoothDaemonCoreModule*> nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->SspReplyCmd(aBdAddr, aVariant, aAccept, aPasskey, aRes); (mProtocol)->SspReplyCmd(aBdAddr, aVariant, aAccept, aPasskey, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
/* DUT Mode */ /* DUT Mode */
@ -2359,8 +2425,11 @@ void
BluetoothDaemonInterface::DutModeConfigure(bool aEnable, BluetoothDaemonInterface::DutModeConfigure(bool aEnable,
BluetoothResultHandler* aRes) BluetoothResultHandler* aRes)
{ {
static_cast<BluetoothDaemonCoreModule*> nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->DutModeConfigureCmd(aEnable, aRes); (mProtocol)->DutModeConfigureCmd(aEnable, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
@ -2368,8 +2437,11 @@ BluetoothDaemonInterface::DutModeSend(uint16_t aOpcode, uint8_t* aBuf,
uint8_t aLen, uint8_t aLen,
BluetoothResultHandler* aRes) BluetoothResultHandler* aRes)
{ {
static_cast<BluetoothDaemonCoreModule*> nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->DutModeSendCmd(aOpcode, aBuf, aLen, aRes); (mProtocol)->DutModeSendCmd(aOpcode, aBuf, aLen, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
/* LE Mode */ /* LE Mode */
@ -2379,8 +2451,11 @@ BluetoothDaemonInterface::LeTestMode(uint16_t aOpcode, uint8_t* aBuf,
uint8_t aLen, uint8_t aLen,
BluetoothResultHandler* aRes) BluetoothResultHandler* aRes)
{ {
static_cast<BluetoothDaemonCoreModule*> nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->LeTestModeCmd(aOpcode, aBuf, aLen, aRes); (mProtocol)->LeTestModeCmd(aOpcode, aBuf, aLen, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
/* Energy Information */ /* Energy Information */
@ -2401,6 +2476,18 @@ BluetoothDaemonInterface::DispatchError(BluetoothResultHandler* aRes,
ConstantInitOp1<BluetoothStatus>(aStatus)); ConstantInitOp1<BluetoothStatus>(aStatus));
} }
void
BluetoothDaemonInterface::DispatchError(BluetoothResultHandler* aRes,
nsresult aRv)
{
BluetoothStatus status;
if (NS_WARN_IF(NS_FAILED(Convert(aRv, status)))) {
status = STATUS_FAIL;
}
DispatchError(aRes, status);
}
// Profile Interfaces // Profile Interfaces
// //

View File

@ -135,6 +135,7 @@ protected:
private: private:
void DispatchError(BluetoothResultHandler* aRes, BluetoothStatus aStatus); void DispatchError(BluetoothResultHandler* aRes, BluetoothStatus aStatus);
void DispatchError(BluetoothResultHandler* aRes, nsresult aRv);
nsCString mListenSocketName; nsCString mListenSocketName;
nsRefPtr<BluetoothDaemonListenSocket> mListenSocket; nsRefPtr<BluetoothDaemonListenSocket> mListenSocket;

View File

@ -315,8 +315,11 @@ BluetoothDaemonSocketInterface::Listen(BluetoothSocketType aType,
{ {
MOZ_ASSERT(mModule); MOZ_ASSERT(mModule);
mModule->ListenCmd(aType, aServiceName, aServiceUuid, aChannel, nsresult rv = mModule->ListenCmd(aType, aServiceName, aServiceUuid,
aEncrypt, aAuth, aRes); aChannel, aEncrypt, aAuth, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
@ -329,7 +332,11 @@ BluetoothDaemonSocketInterface::Connect(const nsAString& aBdAddr,
{ {
MOZ_ASSERT(mModule); MOZ_ASSERT(mModule);
mModule->ConnectCmd(aBdAddr, aType, aUuid, aChannel, aEncrypt, aAuth, aRes); nsresult rv = mModule->ConnectCmd(aBdAddr, aType, aUuid, aChannel,
aEncrypt, aAuth, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
@ -338,7 +345,10 @@ BluetoothDaemonSocketInterface::Accept(int aFd,
{ {
MOZ_ASSERT(mModule); MOZ_ASSERT(mModule);
mModule->AcceptCmd(aFd, aRes); nsresult rv = mModule->AcceptCmd(aFd, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
} }
void void
@ -346,7 +356,32 @@ BluetoothDaemonSocketInterface::Close(BluetoothSocketResultHandler* aRes)
{ {
MOZ_ASSERT(mModule); MOZ_ASSERT(mModule);
mModule->CloseCmd(aRes); nsresult rv = mModule->CloseCmd(aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
BluetoothDaemonSocketInterface::DispatchError(
BluetoothSocketResultHandler* aRes, BluetoothStatus aStatus)
{
BluetoothResultRunnable1<BluetoothSocketResultHandler, void,
BluetoothStatus, BluetoothStatus>::Dispatch(
aRes, &BluetoothSocketResultHandler::OnError,
ConstantInitOp1<BluetoothStatus>(aStatus));
}
void
BluetoothDaemonSocketInterface::DispatchError(
BluetoothSocketResultHandler* aRes, nsresult aRv)
{
BluetoothStatus status;
if (NS_WARN_IF(NS_FAILED(Convert(aRv, status)))) {
status = STATUS_FAIL;
}
DispatchError(aRes, status);
} }
END_BLUETOOTH_NAMESPACE END_BLUETOOTH_NAMESPACE

View File

@ -113,6 +113,10 @@ public:
void Close(BluetoothSocketResultHandler* aRes); void Close(BluetoothSocketResultHandler* aRes);
private: private:
void DispatchError(BluetoothSocketResultHandler* aRes,
BluetoothStatus aStatus);
void DispatchError(BluetoothSocketResultHandler* aRes, nsresult aRv);
BluetoothDaemonSocketModule* mModule; BluetoothDaemonSocketModule* mModule;
}; };

View File

@ -310,7 +310,8 @@ public:
BT_LOGR("BluetoothInterface::Disable failed: %d", aStatus); BT_LOGR("BluetoothInterface::Disable failed: %d", aStatus);
BluetoothService::AcknowledgeToggleBt(true); // Always make progress; even on failures
BluetoothService::AcknowledgeToggleBt(false);
} }
}; };

View File

@ -432,6 +432,9 @@ public:
void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
{ {
BT_WARNING("BluetoothHandsfreeInterface::Cleanup failed: %d", (int)aStatus); BT_WARNING("BluetoothHandsfreeInterface::Cleanup failed: %d", (int)aStatus);
sBluetoothHfpInterface = nullptr;
if (mRes) { if (mRes) {
mRes->OnError(NS_ERROR_FAILURE); mRes->OnError(NS_ERROR_FAILURE);
} }

View File

@ -10,6 +10,7 @@ if CONFIG['MOZ_B2G_BT']:
'BluetoothDevice.cpp', 'BluetoothDevice.cpp',
'BluetoothHidManager.cpp', 'BluetoothHidManager.cpp',
'BluetoothInterface.cpp', 'BluetoothInterface.cpp',
'BluetoothInterfaceHelpers.cpp',
'BluetoothManager.cpp', 'BluetoothManager.cpp',
'BluetoothProfileController.cpp', 'BluetoothProfileController.cpp',
'BluetoothPropertyContainer.cpp', 'BluetoothPropertyContainer.cpp',

View File

@ -42,6 +42,8 @@ static const char *sEGLExtensionNames[] = {
static PRLibrary* LoadApitraceLibrary() static PRLibrary* LoadApitraceLibrary()
{ {
// Initialization of gfx prefs here is only needed during the unit tests...
gfxPrefs::GetSingleton();
if (!gfxPrefs::UseApitrace()) { if (!gfxPrefs::UseApitrace()) {
return nullptr; return nullptr;
} }

View File

@ -391,7 +391,7 @@ pref("devtools.errorconsole.enabled", false);
// to communicate with a usb cable via adb forward. // to communicate with a usb cable via adb forward.
pref("devtools.debugger.unix-domain-socket", "/data/data/@ANDROID_PACKAGE_NAME@/firefox-debugger-socket"); pref("devtools.debugger.unix-domain-socket", "/data/data/@ANDROID_PACKAGE_NAME@/firefox-debugger-socket");
pref("font.size.inflation.minTwips", 120); pref("font.size.inflation.minTwips", 0);
// When true, zooming will be enabled on all sites, even ones that declare user-scalable=no. // When true, zooming will be enabled on all sites, even ones that declare user-scalable=no.
pref("browser.ui.zoom.force-user-scalable", false); pref("browser.ui.zoom.force-user-scalable", false);

View File

@ -45,17 +45,19 @@ public class ReferrerReceiver extends BroadcastReceiver {
return; return;
} }
// Track the referrer object for distribution handling.
ReferrerDescriptor referrer = new ReferrerDescriptor(intent.getStringExtra("referrer")); ReferrerDescriptor referrer = new ReferrerDescriptor(intent.getStringExtra("referrer"));
// Track the referrer object for distribution handling. if (!TextUtils.equals(referrer.source, MOZILLA_UTM_SOURCE)) {
return;
}
if (TextUtils.equals(referrer.campaign, DISTRIBUTION_UTM_CAMPAIGN)) { if (TextUtils.equals(referrer.campaign, DISTRIBUTION_UTM_CAMPAIGN)) {
Distribution.onReceivedReferrer(context, referrer); Distribution.onReceivedReferrer(context, referrer);
} else { } else {
Log.d(LOGTAG, "Not downloading distribution: non-matching campaign."); Log.d(LOGTAG, "Not downloading distribution: non-matching campaign.");
} // If this is a Mozilla campaign, pass the campaign along to Gecko.
// It'll pretend to be a "playstore" distribution for BLP purposes.
// If this is a Mozilla campaign, pass the campaign along to Gecko.
if (TextUtils.equals(referrer.source, MOZILLA_UTM_SOURCE)) {
propagateMozillaCampaign(referrer); propagateMozillaCampaign(referrer);
} }

View File

@ -20,7 +20,7 @@ public class ActivityUtils {
// Hide/show the system notification bar // Hide/show the system notification bar
Window window = activity.getWindow(); Window window = activity.getWindow();
if (Versions.feature11Plus) { if (Versions.feature16Plus) {
final int newVis; final int newVis;
if (fullscreen) { if (fullscreen) {
newVis = View.SYSTEM_UI_FLAG_FULLSCREEN | newVis = View.SYSTEM_UI_FLAG_FULLSCREEN |
@ -40,12 +40,12 @@ public class ActivityUtils {
public static boolean isFullScreen(final Activity activity) { public static boolean isFullScreen(final Activity activity) {
final Window window = activity.getWindow(); final Window window = activity.getWindow();
if (Versions.feature11Plus) { if (Versions.feature16Plus) {
final int vis = window.getDecorView().getSystemUiVisibility(); final int vis = window.getDecorView().getSystemUiVisibility();
return (vis & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0; return (vis & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
} else {
final int flags = window.getAttributes().flags;
return ((flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0);
} }
final int flags = window.getAttributes().flags;
return ((flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0);
} }
} }

View File

@ -626,6 +626,17 @@ var BrowserApp = {
}); });
}); });
NativeWindow.contextmenus.add(stringGetter("contextmenu.addToReadingList"),
NativeWindow.contextmenus.linkOpenableContext,
function(aTarget) {
let url = NativeWindow.contextmenus._getLinkURL(aTarget);
Messaging.sendRequestForResult({
type: "Reader:AddToList",
title: truncate(url, MAX_TITLE_LENGTH),
url: truncate(url, MAX_URI_LENGTH),
}).catch(Cu.reportError);
});
NativeWindow.contextmenus.add(stringGetter("contextmenu.copyLink"), NativeWindow.contextmenus.add(stringGetter("contextmenu.copyLink"),
NativeWindow.contextmenus.linkCopyableContext, NativeWindow.contextmenus.linkCopyableContext,
function(aTarget) { function(aTarget) {

View File

@ -1,24 +1,24 @@
/* This Source Code Form is subject to the terms of the Mozilla Public /* 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 * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict;"
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, 'Services', XPCOMUtils.defineLazyModuleGetter(this, "Services",
'resource://gre/modules/Services.jsm'); "resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, 'Preferences', XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
'resource://gre/modules/Preferences.jsm'); "resource://gre/modules/FileUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, 'FileUtils', XPCOMUtils.defineLazyModuleGetter(this, "Log",
'resource://gre/modules/FileUtils.jsm'); "resource://gre/modules/Log.jsm");
XPCOMUtils.defineLazyModuleGetter(this, 'Log', XPCOMUtils.defineLazyModuleGetter(this, "OS",
'resource://gre/modules/Log.jsm'); "resource://gre/modules/osfile.jsm");
XPCOMUtils.defineLazyModuleGetter(this, 'Task', XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils",
'resource://gre/modules/Task.jsm'); "resource://services-common/utils.js");
XPCOMUtils.defineLazyModuleGetter(this, 'OS',
'resource://gre/modules/osfile.jsm'); Cu.import("resource://gre/modules/Preferences.jsm");
XPCOMUtils.defineLazyModuleGetter(this, 'CommonUtils', Cu.import("resource://gre/modules/Task.jsm");
'resource://services-common/utils.js');
this.EXPORTED_SYMBOLS = [ this.EXPORTED_SYMBOLS = [
"LogManager", "LogManager",
@ -28,7 +28,7 @@ const DEFAULT_MAX_ERROR_AGE = 20 * 24 * 60 * 60; // 20 days
// "shared" logs (ie, where the same log name is used by multiple LogManager // "shared" logs (ie, where the same log name is used by multiple LogManager
// instances) are a fact of life here - eg, FirefoxAccounts logs are used by // instances) are a fact of life here - eg, FirefoxAccounts logs are used by
// both Sync and Reading-list. // both Sync and Reading List.
// However, different instances have different pref branches, so we need to // However, different instances have different pref branches, so we need to
// handle when one pref branch says "Debug" and the other says "Error" // handle when one pref branch says "Debug" and the other says "Error"
// So we (a) keep singleton console and dump appenders and (b) keep track // So we (a) keep singleton console and dump appenders and (b) keep track
@ -103,7 +103,7 @@ LogManager.prototype = {
// The file appender doesn't get the special singleton behaviour. // The file appender doesn't get the special singleton behaviour.
let fapp = this._fileAppender = new Log.StorageStreamAppender(formatter); let fapp = this._fileAppender = new Log.StorageStreamAppender(formatter);
// the stream gets a default of Debug as the user must go out of there way // the stream gets a default of Debug as the user must go out of their way
// to see the stuff spewed to it. // to see the stuff spewed to it.
this._observeStreamPref = setupAppender(fapp, "log.appender.file.level", Log.Level.Debug); this._observeStreamPref = setupAppender(fapp, "log.appender.file.level", Log.Level.Debug);
@ -151,7 +151,7 @@ LogManager.prototype = {
const BUFFER_SIZE = 8192; const BUFFER_SIZE = 8192;
// get a binary stream // get a binary stream
let binaryStream = Cc['@mozilla.org/binaryinputstream;1'].createInstance(Ci.nsIBinaryInputStream); let binaryStream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream);
binaryStream.setInputStream(inputStream); binaryStream.setInputStream(inputStream);
yield OS.File.makeDir(outputFile.parent.path, { ignoreExisting: true }); yield OS.File.makeDir(outputFile.parent.path, { ignoreExisting: true });
let output = yield OS.File.open(outputFile.path, { write: true} ); let output = yield OS.File.open(outputFile.path, { write: true} );
@ -165,12 +165,14 @@ LogManager.prototype = {
yield output.write(new Uint8Array(chunk)); yield output.write(new Uint8Array(chunk));
} }
} finally { } finally {
inputStream.close(); try {
binaryStream.close(); binaryStream.close(); // inputStream is closed by the binaryStream
yield output.close(); yield output.close();
} catch (ex) {
this._log.error("Failed to close the input stream", ex);
}
} }
this._log.trace("finished copy to", outputFile.path); this._log.trace("finished copy to", outputFile.path);
return (yield OS.File.stat(outputFile.path)).lastModificationDate;
}), }),
/** /**
@ -179,70 +181,65 @@ LogManager.prototype = {
* Returns a promise that resolves on completion or rejects if the file could * Returns a promise that resolves on completion or rejects if the file could
* not be written. * not be written.
*/ */
resetFileLog(reason) { resetFileLog: Task.async(function* (reason) {
return new Promise((resolve, reject) => { try {
try { let flushToFile;
let flushToFile; let reasonPrefix;
let reasonPrefix; switch (reason) {
switch (reason) { case this.REASON_SUCCESS:
case this.REASON_SUCCESS: flushToFile = this._prefs.get("log.appender.file.logOnSuccess", false);
flushToFile = this._prefs.get("log.appender.file.logOnSuccess"); reasonPrefix = "success";
reasonPrefix = "success"; break;
break; case this.REASON_ERROR:
case this.REASON_ERROR: flushToFile = this._prefs.get("log.appender.file.logOnError", true);
flushToFile = this._prefs.get("log.appender.file.logOnError"); reasonPrefix = "error";
reasonPrefix = "error"; break;
break; default:
default: throw new Error("Invalid reason");
return reject(new Error("Invalid reason"));
}
let inStream = this._fileAppender.getInputStream();
this._fileAppender.reset();
if (flushToFile && inStream) {
this._log.debug("Flushing file log");
let filename = this.logFilePrefix + "-" + reasonPrefix + "-" + Date.now() + ".txt";
let file = this._logFileDirectory;
file.append(filename);
this._log.trace("Beginning stream copy to " + file.leafName + ": " +
Date.now());
this._copyStreamToFile(inStream, file).then(
modDate => {
this._log.trace("onCopyComplete: " + Date.now());
this._log.trace("Output file timestamp: " + modDate + " (" + modDate.getTime() + ")");
},
err => {
this._log.error("Failed to copy log stream to file", err)
reject(err)
}
).then(
() => {
// It's not completely clear to markh why we only do log cleanups
// for errors, but for now the Sync semantics have been copied...
// (one theory is that only cleaning up on error makes it less
// likely old error logs would be removed, but that's not true if
// there are occasional errors - let's address this later!)
if (reason == this.REASON_ERROR &&
!this._cleaningUpFileLogs) {
this._log.trace("Scheduling cleanup.");
// Note we don't return or wait on this promise - it continues
// in the background
this.cleanupLogs().then(null, err => {
this._log.error("Failed to cleanup logs", err);
});
}
resolve();
}
);
} else {
resolve();
}
} catch (ex) {
this._log.error("Failed to resetFileLog", ex)
reject(ex);
} }
})
}, // might as well avoid creating an input stream if we aren't going to use it.
if (!flushToFile) {
this._fileAppender.reset();
return;
}
let inStream = this._fileAppender.getInputStream();
this._fileAppender.reset();
if (inStream) {
this._log.debug("Flushing file log");
// We have reasonPrefix at the start of the filename so all "error"
// logs are grouped in about:sync-log.
let filename = reasonPrefix + "-" + this.logFilePrefix + "-" + Date.now() + ".txt";
let file = this._logFileDirectory;
file.append(filename);
this._log.trace("Beginning stream copy to " + file.leafName + ": " +
Date.now());
try {
yield this._copyStreamToFile(inStream, file);
this._log.trace("onCopyComplete", Date.now());
} catch (ex) {
this._log.error("Failed to copy log stream to file", ex);
return;
}
// It's not completely clear to markh why we only do log cleanups
// for errors, but for now the Sync semantics have been copied...
// (one theory is that only cleaning up on error makes it less
// likely old error logs would be removed, but that's not true if
// there are occasional errors - let's address this later!)
if (reason == this.REASON_ERROR && !this._cleaningUpFileLogs) {
this._log.trace("Scheduling cleanup.");
// Note we don't return/yield or otherwise wait on this promise - it
// continues in the background
this.cleanupLogs().catch(err => {
this._log.error("Failed to cleanup logs", err);
});
}
}
} catch (ex) {
this._log.error("Failed to resetFileLog", ex)
}
}),
/** /**
* Finds all logs older than maxErrorAge and deletes them using async I/O. * Finds all logs older than maxErrorAge and deletes them using async I/O.
@ -255,7 +252,8 @@ LogManager.prototype = {
this._log.debug("Log cleanup threshold time: " + threshold); this._log.debug("Log cleanup threshold time: " + threshold);
yield iterator.forEach(Task.async(function* (entry) { yield iterator.forEach(Task.async(function* (entry) {
if (!entry.name.startsWith(this.logFilePrefix + "-")) { if (!entry.name.startsWith("error-" + this.logFilePrefix + "-") &&
!entry.name.startsWith("success-" + this.logFilePrefix + "-")) {
return; return;
} }
try { try {

View File

@ -23,7 +23,7 @@ function getAppenders(log) {
} }
// Test that the correct thing happens when no prefs exist for the log manager. // Test that the correct thing happens when no prefs exist for the log manager.
add_test(function test_noPrefs() { add_task(function* test_noPrefs() {
// tell the log manager to init with a pref branch that doesn't exist. // tell the log manager to init with a pref branch that doesn't exist.
let lm = new LogManager("no-such-branch.", ["TestLog"], "test"); let lm = new LogManager("no-such-branch.", ["TestLog"], "test");
@ -36,11 +36,10 @@ add_test(function test_noPrefs() {
equal(fapps.length, 1, "only 1 file appender"); equal(fapps.length, 1, "only 1 file appender");
equal(fapps[0].level, Log.Level.Debug); equal(fapps[0].level, Log.Level.Debug);
lm.finalize(); lm.finalize();
run_next_test();
}); });
// Test that changes to the prefs used by the log manager are updated dynamically. // Test that changes to the prefs used by the log manager are updated dynamically.
add_test(function test_PrefChanges() { add_task(function* test_PrefChanges() {
Services.prefs.setCharPref("log-manager.test.log.appender.console", "Trace"); Services.prefs.setCharPref("log-manager.test.log.appender.console", "Trace");
Services.prefs.setCharPref("log-manager.test.log.appender.dump", "Trace"); Services.prefs.setCharPref("log-manager.test.log.appender.dump", "Trace");
Services.prefs.setCharPref("log-manager.test.log.appender.file.level", "Trace"); Services.prefs.setCharPref("log-manager.test.log.appender.file.level", "Trace");
@ -66,11 +65,10 @@ add_test(function test_PrefChanges() {
equal(dapp.level, Log.Level.Error); equal(dapp.level, Log.Level.Error);
equal(fapp.level, Log.Level.Debug); equal(fapp.level, Log.Level.Debug);
lm.finalize(); lm.finalize();
run_next_test();
}); });
// Test that the same log used by multiple log managers does the right thing. // Test that the same log used by multiple log managers does the right thing.
add_test(function test_SharedLogs() { add_task(function* test_SharedLogs() {
// create the prefs for the first instance. // create the prefs for the first instance.
Services.prefs.setCharPref("log-manager-1.test.log.appender.console", "Trace"); Services.prefs.setCharPref("log-manager-1.test.log.appender.console", "Trace");
Services.prefs.setCharPref("log-manager-1.test.log.appender.dump", "Trace"); Services.prefs.setCharPref("log-manager-1.test.log.appender.dump", "Trace");
@ -102,6 +100,4 @@ add_test(function test_SharedLogs() {
lm1.finalize(); lm1.finalize();
lm2.finalize(); lm2.finalize();
run_next_test();
}); });

View File

@ -617,7 +617,7 @@ this.BrowserIDManager.prototype = {
// that there is no authentication dance still under way. // that there is no authentication dance still under way.
this._shouldHaveSyncKeyBundle = true; this._shouldHaveSyncKeyBundle = true;
Weave.Status.login = this._authFailureReason; Weave.Status.login = this._authFailureReason;
Services.obs.notifyObservers(null, "weave:service:login:error", null); Services.obs.notifyObservers(null, "weave:ui:login:error", null);
throw err; throw err;
}); });
}, },

View File

@ -1770,7 +1770,7 @@ add_task(function test_sync_engine_generic_fail() {
let entries = logsdir.directoryEntries; let entries = logsdir.directoryEntries;
do_check_true(entries.hasMoreElements()); do_check_true(entries.hasMoreElements());
let logfile = entries.getNext().QueryInterface(Ci.nsILocalFile); let logfile = entries.getNext().QueryInterface(Ci.nsILocalFile);
do_check_true(logfile.leafName.startsWith("sync-error-"), logfile.leafName); do_check_true(logfile.leafName.startsWith("error-sync-"), logfile.leafName);
clean(); clean();
server.stop(deferred.resolve); server.stop(deferred.resolve);
@ -1801,7 +1801,7 @@ add_test(function test_logs_on_sync_error_despite_shouldReportError() {
let entries = logsdir.directoryEntries; let entries = logsdir.directoryEntries;
do_check_true(entries.hasMoreElements()); do_check_true(entries.hasMoreElements());
let logfile = entries.getNext().QueryInterface(Ci.nsILocalFile); let logfile = entries.getNext().QueryInterface(Ci.nsILocalFile);
do_check_true(logfile.leafName.startsWith("sync-error-"), logfile.leafName); do_check_true(logfile.leafName.startsWith("error-sync-"), logfile.leafName);
clean(); clean();
run_next_test(); run_next_test();
@ -1828,7 +1828,7 @@ add_test(function test_logs_on_login_error_despite_shouldReportError() {
let entries = logsdir.directoryEntries; let entries = logsdir.directoryEntries;
do_check_true(entries.hasMoreElements()); do_check_true(entries.hasMoreElements());
let logfile = entries.getNext().QueryInterface(Ci.nsILocalFile); let logfile = entries.getNext().QueryInterface(Ci.nsILocalFile);
do_check_true(logfile.leafName.startsWith("sync-error-"), logfile.leafName); do_check_true(logfile.leafName.startsWith("error-sync-"), logfile.leafName);
clean(); clean();
run_next_test(); run_next_test();
@ -1862,7 +1862,7 @@ add_task(function test_engine_applyFailed() {
let entries = logsdir.directoryEntries; let entries = logsdir.directoryEntries;
do_check_true(entries.hasMoreElements()); do_check_true(entries.hasMoreElements());
let logfile = entries.getNext().QueryInterface(Ci.nsILocalFile); let logfile = entries.getNext().QueryInterface(Ci.nsILocalFile);
do_check_true(logfile.leafName.startsWith("sync-error-"), logfile.leafName); do_check_true(logfile.leafName.startsWith("error-sync-"), logfile.leafName);
clean(); clean();
server.stop(deferred.resolve); server.stop(deferred.resolve);

View File

@ -108,7 +108,7 @@ add_test(function test_logOnSuccess_true() {
do_check_true(entries.hasMoreElements()); do_check_true(entries.hasMoreElements());
let logfile = entries.getNext().QueryInterface(Ci.nsILocalFile); let logfile = entries.getNext().QueryInterface(Ci.nsILocalFile);
do_check_eq(logfile.leafName.slice(-4), ".txt"); do_check_eq(logfile.leafName.slice(-4), ".txt");
do_check_true(logfile.leafName.startsWith("sync-success-"), logfile.leafName); do_check_true(logfile.leafName.startsWith("success-sync-"), logfile.leafName);
do_check_false(entries.hasMoreElements()); do_check_false(entries.hasMoreElements());
// Ensure the log message was actually written to file. // Ensure the log message was actually written to file.
@ -175,7 +175,7 @@ add_test(function test_sync_error_logOnError_true() {
do_check_true(entries.hasMoreElements()); do_check_true(entries.hasMoreElements());
let logfile = entries.getNext().QueryInterface(Ci.nsILocalFile); let logfile = entries.getNext().QueryInterface(Ci.nsILocalFile);
do_check_eq(logfile.leafName.slice(-4), ".txt"); do_check_eq(logfile.leafName.slice(-4), ".txt");
do_check_true(logfile.leafName.startsWith("sync-error-"), logfile.leafName); do_check_true(logfile.leafName.startsWith("error-sync-"), logfile.leafName);
do_check_false(entries.hasMoreElements()); do_check_false(entries.hasMoreElements());
// Ensure the log message was actually written to file. // Ensure the log message was actually written to file.
@ -242,7 +242,7 @@ add_test(function test_login_error_logOnError_true() {
do_check_true(entries.hasMoreElements()); do_check_true(entries.hasMoreElements());
let logfile = entries.getNext().QueryInterface(Ci.nsILocalFile); let logfile = entries.getNext().QueryInterface(Ci.nsILocalFile);
do_check_eq(logfile.leafName.slice(-4), ".txt"); do_check_eq(logfile.leafName.slice(-4), ".txt");
do_check_true(logfile.leafName.startsWith("sync-error-"), logfile.leafName); do_check_true(logfile.leafName.startsWith("error-sync-"), logfile.leafName);
do_check_false(entries.hasMoreElements()); do_check_false(entries.hasMoreElements());
// Ensure the log message was actually written to file. // Ensure the log message was actually written to file.
@ -281,7 +281,7 @@ add_test(function test_logErrorCleanup_age() {
_("Making some files."); _("Making some files.");
for (let i = 0; i < numLogs; i++) { for (let i = 0; i < numLogs; i++) {
let now = Date.now(); let now = Date.now();
let filename = "sync-error-" + now + "" + i + ".txt"; let filename = "error-sync-" + now + "" + i + ".txt";
let newLog = FileUtils.getFile("ProfD", ["weave", "logs", filename]); let newLog = FileUtils.getFile("ProfD", ["weave", "logs", filename]);
let foStream = FileUtils.openFileOutputStream(newLog); let foStream = FileUtils.openFileOutputStream(newLog);
foStream.write(errString, errString.length); foStream.write(errString, errString.length);

View File

@ -0,0 +1,35 @@
---
# For complete sample of all build and test jobs,
# see <gecko>/testing/taskcluster/tasks/job_flags.yml
$inherits:
from: tasks/branches/base_job_flags.yml
builds:
flame-kk:
platforms:
- b2g
types:
opt:
task: tasks/builds/b2g_flame_kk_opt.yml
flame-kk-eng:
platforms:
- b2g
types:
opt:
task: tasks/builds/b2g_flame_kk_eng.yml
tests:
gaia-build:
allowed_build_tasks:
tasks/builds/b2g_flame_kk_opt.yml:
task: tasks/tests/b2g_build_test.yml
tasks/builds/b2g_flame_kk_eng.yml:
task: tasks/tests/b2g_build_test.yml
gaia-linter:
allowed_build_tasks:
tasks/builds/b2g_flame_kk_opt.yml:
task: tasks/tests/b2g_linter.yml
tasks/builds/b2g_flame_kk_eng.yml:
task: tasks/tests/b2g_linter.yml

View File

@ -0,0 +1,35 @@
---
# For complete sample of all build and test jobs,
# see <gecko>/testing/taskcluster/tasks/job_flags.yml
$inherits:
from: tasks/branches/base_job_flags.yml
builds:
flame-kk:
platforms:
- b2g
types:
opt:
task: tasks/builds/b2g_flame_kk_opt.yml
flame-kk-eng:
platforms:
- b2g
types:
opt:
task: tasks/builds/b2g_flame_kk_eng.yml
tests:
gaia-build:
allowed_build_tasks:
tasks/builds/b2g_flame_kk_opt.yml:
task: tasks/tests/b2g_build_test.yml
tasks/builds/b2g_flame_kk_eng.yml:
task: tasks/tests/b2g_build_test.yml
gaia-linter:
allowed_build_tasks:
tasks/builds/b2g_flame_kk_opt.yml:
task: tasks/tests/b2g_linter.yml
tasks/builds/b2g_flame_kk_eng.yml:
task: tasks/tests/b2g_linter.yml

View File

@ -104,11 +104,11 @@ let StyleSheetsActor = exports.StyleSheetsActor = protocol.ActorClass({
let actors = []; let actors = [];
for (let doc of documents) { for (let doc of documents) {
let sheets = yield this._addStyleSheets(doc.styleSheets); let sheets = yield this._addStyleSheets(doc);
actors = actors.concat(sheets); actors = actors.concat(sheets);
// Recursively handle style sheets of the documents in iframes. // Recursively handle style sheets of the documents in iframes.
for (let iframe of doc.getElementsByTagName("iframe")) { for (let iframe of doc.querySelectorAll("iframe, browser, frame")) {
if (iframe.contentDocument) { if (iframe.contentDocument) {
// Sometimes, iframes don't have any document, like the // Sometimes, iframes don't have any document, like the
// one that are over deeply nested (bug 285395) // one that are over deeply nested (bug 285395)
@ -121,25 +121,54 @@ let StyleSheetsActor = exports.StyleSheetsActor = protocol.ActorClass({
}, },
/** /**
* Add all the stylesheets to the map and create an actor for each one * Check if we should be showing this stylesheet.
* if not already created.
* *
* @param {[DOMStyleSheet]} styleSheets * @param {Document} doc
* Stylesheets to add * Document for which we're checking
* @param {DOMCSSStyleSheet} sheet
* Stylesheet we're interested in
*
* @return boolean
* Whether the stylesheet should be listed.
*/
_shouldListSheet: function(doc, sheet) {
// Special case about:PreferenceStyleSheet, as it is generated on the
// fly and the URI is not registered with the about: handler.
// https://bugzilla.mozilla.org/show_bug.cgi?id=935803#c37
if (sheet.href && sheet.href.toLowerCase() == "about:preferencestylesheet") {
return false;
}
return true;
},
/**
* Add all the stylesheets for this document to the map and create an actor
* for each one if not already created.
*
* @param {Document} doc
* Document for which to add stylesheets
* *
* @return {Promise} * @return {Promise}
* Promise that resolves to an array of StyleSheetActors * Promise that resolves to an array of StyleSheetActors
*/ */
_addStyleSheets: function(styleSheets) _addStyleSheets: function(doc)
{ {
return Task.spawn(function*() { return Task.spawn(function*() {
let isChrome = Services.scriptSecurityManager.isSystemPrincipal(doc.nodePrincipal);
let styleSheets = isChrome ? DOMUtils.getAllStyleSheets(doc) : doc.styleSheets;
let actors = []; let actors = [];
for (let i = 0; i < styleSheets.length; i++) { for (let i = 0; i < styleSheets.length; i++) {
let actor = this.parentActor.createStyleSheetActor(styleSheets[i]); let sheet = styleSheets[i];
if (!this._shouldListSheet(doc, sheet)) {
continue;
}
let actor = this.parentActor.createStyleSheetActor(sheet);
actors.push(actor); actors.push(actor);
// Get all sheets, including imported ones // Get all sheets, including imported ones
let imports = yield this._getImported(actor); let imports = yield this._getImported(doc, actor);
actors = actors.concat(imports); actors = actors.concat(imports);
} }
return actors; return actors;
@ -149,12 +178,14 @@ let StyleSheetsActor = exports.StyleSheetsActor = protocol.ActorClass({
/** /**
* Get all the stylesheets @imported from a stylesheet. * Get all the stylesheets @imported from a stylesheet.
* *
* @param {Document} doc
* The document including the stylesheet
* @param {DOMStyleSheet} styleSheet * @param {DOMStyleSheet} styleSheet
* Style sheet to search * Style sheet to search
* @return {Promise} * @return {Promise}
* A promise that resolves with an array of StyleSheetActors * A promise that resolves with an array of StyleSheetActors
*/ */
_getImported: function(styleSheet) { _getImported: function(doc, styleSheet) {
return Task.spawn(function*() { return Task.spawn(function*() {
let rules = yield styleSheet.getCSSRules(); let rules = yield styleSheet.getCSSRules();
let imported = []; let imported = [];
@ -164,14 +195,14 @@ let StyleSheetsActor = exports.StyleSheetsActor = protocol.ActorClass({
if (rule.type == Ci.nsIDOMCSSRule.IMPORT_RULE) { if (rule.type == Ci.nsIDOMCSSRule.IMPORT_RULE) {
// Associated styleSheet may be null if it has already been seen due // Associated styleSheet may be null if it has already been seen due
// to duplicate @imports for the same URL. // to duplicate @imports for the same URL.
if (!rule.styleSheet) { if (!rule.styleSheet || !this._shouldListSheet(doc, rule.styleSheet)) {
continue; continue;
} }
let actor = this.parentActor.createStyleSheetActor(rule.styleSheet); let actor = this.parentActor.createStyleSheetActor(rule.styleSheet);
imported.push(actor); imported.push(actor);
// recurse imports in this stylesheet as well // recurse imports in this stylesheet as well
let children = yield this._getImported(actor); let children = yield this._getImported(doc, actor);
imported = imported.concat(children); imported = imported.concat(children);
} }
else if (rule.type != Ci.nsIDOMCSSRule.CHARSET_RULE) { else if (rule.type != Ci.nsIDOMCSSRule.CHARSET_RULE) {

View File

@ -154,7 +154,11 @@ WebConsoleClient.prototype = {
}; };
this._client.request(packet, response => { this._client.request(packet, response => {
this.pendingEvaluationResults.set(response.resultID, aOnResponse); // Null check this in case the client has been detached while waiting
// for a response.
if (this.pendingEvaluationResults) {
this.pendingEvaluationResults.set(response.resultID, aOnResponse);
}
}); });
}, },

View File

@ -82,3 +82,17 @@ xul|*.inline-link:-moz-focusring {
color: #ff9500; color: #ff9500;
text-decoration: underline; text-decoration: underline;
} }
xul|button:-moz-focusring,
xul|menulist:-moz-focusring,
xul|checkbox:-moz-focusring > .checkbox-check,
xul|radio[focused="true"] > .radio-check,
xul|tab:-moz-focusring > .tab-middle > .tab-text {
outline: 2px solid rgba(0,149,221,0.5);
outline-offset: 1px;
-moz-outline-radius: 2px;
}
xul|radio[focused="true"] > .radio-check {
-moz-outline-radius: 100%;
}

View File

@ -94,9 +94,7 @@ XXX: apply styles to all themes until bug 509642 is fixed
-moz-appearance: none; -moz-appearance: none;
border-radius: 3px; border-radius: 3px;
padding: 0; padding: 0;
%ifndef WINDOWS_AERO background-color: rgba(250,250,250,.3)
background-color: rgba(250,250,250,.3);
%endif
} }
.popup-notification-menubutton:hover:active { .popup-notification-menubutton:hover:active {
@ -109,17 +107,25 @@ XXX: apply styles to all themes until bug 509642 is fixed
-moz-appearance: none; -moz-appearance: none;
margin: 0; margin: 0;
border: 1px solid rgba(0,0,0,.35); border: 1px solid rgba(0,0,0,.35);
%ifdef WINDOWS_AERO
background-image: linear-gradient(rgba(250,250,250,.6), rgba(175,175,175,.25) 49%, rgba(0,0,0,.12) 51%, rgba(0,0,0,.09) 60%, rgba(0,0,0,.05));
box-shadow: 0 0 1px 1px rgba(255,255,255,.75) inset;
%else
box-shadow: 0 1px 0 rgba(255,255,255,.5) inset, box-shadow: 0 1px 0 rgba(255,255,255,.5) inset,
0 2px 2px rgba(255,255,255,.35) inset, 0 2px 2px rgba(255,255,255,.35) inset,
0 -1px 2px rgba(0,0,0,.1) inset, 0 -1px 2px rgba(0,0,0,.1) inset,
0 1px 0 rgba(255,255,255,.35); 0 1px 0 rgba(255,255,255,.35);
%endif
} }
%ifdef WINDOWS_AERO
@media (-moz-windows-glass) {
.popup-notification-menubutton:not([type="menu-button"]),
.popup-notification-menubutton > .button-menubutton-button,
.popup-notification-menubutton > .button-menubutton-dropmarker {
background-color: transparent;
background-image: linear-gradient(rgba(250,250,250,.6), rgba(175,175,175,.25) 49%, rgba(0,0,0,.12) 51%, rgba(0,0,0,.09) 60%, rgba(0,0,0,.05));
box-shadow: 0 0 1px 1px rgba(255,255,255,.75) inset;
}
}
%endif
.popup-notification-menubutton > .button-menubutton-button { .popup-notification-menubutton > .button-menubutton-button {
background-color: transparent; background-color: transparent;
padding: 1px; padding: 1px;
@ -145,40 +151,51 @@ XXX: apply styles to all themes until bug 509642 is fixed
} }
%ifdef WINDOWS_AERO %ifdef WINDOWS_AERO
.popup-notification-menubutton > .button-menubutton-button:-moz-locale-dir(ltr), @media (-moz-windows-glass) {
.popup-notification-menubutton > .button-menubutton-dropmarker:-moz-locale-dir(rtl) { .popup-notification-menubutton > .button-menubutton-button:-moz-locale-dir(ltr),
border-radius: 2px 0 0 2px; .popup-notification-menubutton > .button-menubutton-dropmarker:-moz-locale-dir(rtl) {
} border-radius: 2px 0 0 2px;
}
.popup-notification-menubutton > .button-menubutton-button:-moz-locale-dir(rtl), .popup-notification-menubutton > .button-menubutton-button:-moz-locale-dir(rtl),
.popup-notification-menubutton > .button-menubutton-dropmarker:-moz-locale-dir(ltr) { .popup-notification-menubutton > .button-menubutton-dropmarker:-moz-locale-dir(ltr) {
border-radius: 0 2px 2px 0; border-radius: 0 2px 2px 0;
}
} }
%endif %endif
.popup-notification-menubutton:not([type="menu-button"]):hover, .popup-notification-menubutton:not([type="menu-button"]):hover,
.popup-notification-menubutton > .button-menubutton-button:hover, .popup-notification-menubutton > .button-menubutton-button:hover,
.popup-notification-menubutton > .button-menubutton-dropmarker:hover { .popup-notification-menubutton > .button-menubutton-dropmarker:hover {
%ifdef WINDOWS_AERO
background-image: linear-gradient(rgba(250,250,250,.9), rgba(200,200,200,.6) 49%, rgba(0,0,0,.23) 51%, rgba(0,0,0,.17) 60%, rgba(0,0,0,.05));
box-shadow: 0 0 0 1px white inset,
0 0 2px 1px rgba(255,255,255,.75) inset;
%else
background-color: rgba(250,250,250,.6); background-color: rgba(250,250,250,.6);
%endif
} }
.popup-notification-menubutton:not([type="menu-button"]):hover:active, .popup-notification-menubutton:not([type="menu-button"]):hover:active,
.popup-notification-menubutton > .button-menubutton-button:hover:active, .popup-notification-menubutton > .button-menubutton-button:hover:active,
.popup-notification-menubutton > .button-menubutton-dropmarker:hover:active, .popup-notification-menubutton > .button-menubutton-dropmarker:hover:active,
.popup-notification-menubutton[open="true"] > .button-menubutton-dropmarker { .popup-notification-menubutton[open="true"] > .button-menubutton-dropmarker {
%ifdef WINDOWS_AERO
background-image: linear-gradient(rgba(250,250,250,.9), rgba(200,200,200,.6) 49%, rgba(0,0,0,.23) 51%, rgba(0,0,0,.17) 60%, rgba(0,0,0,.05));
%else
background-color: rgba(0,0,0,.05); background-color: rgba(0,0,0,.05);
%endif
box-shadow: 0 0 2px rgba(0,0,0,.65) inset; box-shadow: 0 0 2px rgba(0,0,0,.65) inset;
} }
%ifdef WINDOWS_AERO
@media (-moz-windows-glass) {
.popup-notification-menubutton:not([type="menu-button"]):hover,
.popup-notification-menubutton > .button-menubutton-button:hover,
.popup-notification-menubutton > .button-menubutton-dropmarker:hover {
background-color: transparent;
background-image: linear-gradient(rgba(250,250,250,.9), rgba(200,200,200,.6) 49%, rgba(0,0,0,.23) 51%, rgba(0,0,0,.17) 60%, rgba(0,0,0,.05));
box-shadow: 0 0 0 1px white inset,
0 0 2px 1px rgba(255,255,255,.75) inset;
}
.popup-notification-menubutton:not([type="menu-button"]):hover:active,
.popup-notification-menubutton > .button-menubutton-button:hover:active,
.popup-notification-menubutton > .button-menubutton-dropmarker:hover:active,
.popup-notification-menubutton[open="true"] > .button-menubutton-dropmarker {
background-color: transparent;
background-image: linear-gradient(rgba(250,250,250,.9), rgba(200,200,200,.6) 49%, rgba(0,0,0,.23) 51%, rgba(0,0,0,.17) 60%, rgba(0,0,0,.05));
}
}
%endif
/*}*/ /*}*/
%endif %endif

View File

@ -50,7 +50,7 @@ using namespace mozilla::gfx;
using namespace mozilla::jni; using namespace mozilla::jni;
using namespace mozilla::widget; using namespace mozilla::widget;
AndroidBridge* AndroidBridge::sBridge; AndroidBridge* AndroidBridge::sBridge = nullptr;
pthread_t AndroidBridge::sJavaUiThread = -1; pthread_t AndroidBridge::sJavaUiThread = -1;
static unsigned sJavaEnvThreadIndex = 0; static unsigned sJavaEnvThreadIndex = 0;
static jobject sGlobalContext = nullptr; static jobject sGlobalContext = nullptr;
@ -161,14 +161,12 @@ AndroidBridge::ConstructBridge(JNIEnv *jEnv, Object::Param clsLoader)
PR_NewThreadPrivateIndex(&sJavaEnvThreadIndex, JavaThreadDetachFunc); PR_NewThreadPrivateIndex(&sJavaEnvThreadIndex, JavaThreadDetachFunc);
AndroidBridge *bridge = new AndroidBridge(); MOZ_ASSERT(!sBridge);
if (!bridge->Init(jEnv, clsLoader)) { sBridge = new AndroidBridge;
delete bridge; sBridge->Init(jEnv, clsLoader); // Success or crash
}
sBridge = bridge;
} }
bool void
AndroidBridge::Init(JNIEnv *jEnv, Object::Param clsLoader) AndroidBridge::Init(JNIEnv *jEnv, Object::Param clsLoader)
{ {
ALOG_BRIDGE("AndroidBridge::Init"); ALOG_BRIDGE("AndroidBridge::Init");
@ -244,8 +242,6 @@ AndroidBridge::Init(JNIEnv *jEnv, Object::Param clsLoader)
// jEnv should NOT be cached here by anything -- the jEnv here // jEnv should NOT be cached here by anything -- the jEnv here
// is not valid for the real gecko main thread, which is set // is not valid for the real gecko main thread, which is set
// at SetMainThread time. // at SetMainThread time.
return true;
} }
bool bool

View File

@ -364,7 +364,7 @@ protected:
~AndroidBridge(); ~AndroidBridge();
void InitStubs(JNIEnv *jEnv); void InitStubs(JNIEnv *jEnv);
bool Init(JNIEnv *jEnv, jni::Object::Param clsLoader); void Init(JNIEnv *jEnv, jni::Object::Param clsLoader);
bool mOpenedGraphicsLibraries; bool mOpenedGraphicsLibraries;
void OpenGraphicsLibraries(); void OpenGraphicsLibraries();

View File

@ -41,8 +41,9 @@ public:
return mVendor; return mVendor;
} }
// This spoofed value wins, even if the environment variable
// MOZ_GFX_SPOOF_GL_VENDOR was set.
void SpoofVendor(const nsCString& s) { void SpoofVendor(const nsCString& s) {
EnsureInitialized();
mVendor = s; mVendor = s;
} }
@ -51,8 +52,9 @@ public:
return mRenderer; return mRenderer;
} }
// This spoofed value wins, even if the environment variable
// MOZ_GFX_SPOOF_GL_RENDERER was set.
void SpoofRenderer(const nsCString& s) { void SpoofRenderer(const nsCString& s) {
EnsureInitialized();
mRenderer = s; mRenderer = s;
} }
@ -61,8 +63,9 @@ public:
return mVersion; return mVersion;
} }
// This spoofed value wins, even if the environment variable
// MOZ_GFX_SPOOF_GL_VERSION was set.
void SpoofVersion(const nsCString& s) { void SpoofVersion(const nsCString& s) {
EnsureInitialized();
mVersion = s; mVersion = s;
} }
@ -85,21 +88,32 @@ public:
gl->MakeCurrent(); gl->MakeCurrent();
const char *spoofedVendor = PR_GetEnv("MOZ_GFX_SPOOF_GL_VENDOR"); if (mVendor.IsEmpty()) {
if (spoofedVendor) const char *spoofedVendor = PR_GetEnv("MOZ_GFX_SPOOF_GL_VENDOR");
if (spoofedVendor) {
mVendor.Assign(spoofedVendor); mVendor.Assign(spoofedVendor);
else } else {
mVendor.Assign((const char*)gl->fGetString(LOCAL_GL_VENDOR)); mVendor.Assign((const char*)gl->fGetString(LOCAL_GL_VENDOR));
const char *spoofedRenderer = PR_GetEnv("MOZ_GFX_SPOOF_GL_RENDERER"); }
if (spoofedRenderer) }
if (mRenderer.IsEmpty()) {
const char *spoofedRenderer = PR_GetEnv("MOZ_GFX_SPOOF_GL_RENDERER");
if (spoofedRenderer) {
mRenderer.Assign(spoofedRenderer); mRenderer.Assign(spoofedRenderer);
else } else {
mRenderer.Assign((const char*)gl->fGetString(LOCAL_GL_RENDERER)); mRenderer.Assign((const char*)gl->fGetString(LOCAL_GL_RENDERER));
const char *spoofedVersion = PR_GetEnv("MOZ_GFX_SPOOF_GL_VERSION"); }
if (spoofedVersion) }
if (mVersion.IsEmpty()) {
const char *spoofedVersion = PR_GetEnv("MOZ_GFX_SPOOF_GL_VERSION");
if (spoofedVersion) {
mVersion.Assign(spoofedVersion); mVersion.Assign(spoofedVersion);
else } else {
mVersion.Assign((const char*)gl->fGetString(LOCAL_GL_VERSION)); mVersion.Assign((const char*)gl->fGetString(LOCAL_GL_VERSION));
}
}
mReady = true; mReady = true;
} }
@ -153,9 +167,10 @@ GfxInfo::EnsureInitialized()
if (mInitialized) if (mInitialized)
return; return;
mGLStrings->EnsureInitialized(); if (!mozilla::AndroidBridge::Bridge()) {
gfxWarning() << "AndroidBridge missing during initialization";
MOZ_ASSERT(mozilla::AndroidBridge::Bridge()); return;
}
if (mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "MODEL", mModel)) { if (mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "MODEL", mModel)) {
mAdapterDescription.AppendPrintf("Model: %s", NS_LossyConvertUTF16toASCII(mModel).get()); mAdapterDescription.AppendPrintf("Model: %s", NS_LossyConvertUTF16toASCII(mModel).get());
@ -601,7 +616,6 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature,
/* void spoofVendorID (in DOMString aVendorID); */ /* void spoofVendorID (in DOMString aVendorID); */
NS_IMETHODIMP GfxInfo::SpoofVendorID(const nsAString & aVendorID) NS_IMETHODIMP GfxInfo::SpoofVendorID(const nsAString & aVendorID)
{ {
EnsureInitialized();
mGLStrings->SpoofVendor(NS_LossyConvertUTF16toASCII(aVendorID)); mGLStrings->SpoofVendor(NS_LossyConvertUTF16toASCII(aVendorID));
return NS_OK; return NS_OK;
} }
@ -609,7 +623,6 @@ NS_IMETHODIMP GfxInfo::SpoofVendorID(const nsAString & aVendorID)
/* void spoofDeviceID (in unsigned long aDeviceID); */ /* void spoofDeviceID (in unsigned long aDeviceID); */
NS_IMETHODIMP GfxInfo::SpoofDeviceID(const nsAString & aDeviceID) NS_IMETHODIMP GfxInfo::SpoofDeviceID(const nsAString & aDeviceID)
{ {
EnsureInitialized();
mGLStrings->SpoofRenderer(NS_LossyConvertUTF16toASCII(aDeviceID)); mGLStrings->SpoofRenderer(NS_LossyConvertUTF16toASCII(aDeviceID));
return NS_OK; return NS_OK;
} }
@ -617,7 +630,6 @@ NS_IMETHODIMP GfxInfo::SpoofDeviceID(const nsAString & aDeviceID)
/* void spoofDriverVersion (in DOMString aDriverVersion); */ /* void spoofDriverVersion (in DOMString aDriverVersion); */
NS_IMETHODIMP GfxInfo::SpoofDriverVersion(const nsAString & aDriverVersion) NS_IMETHODIMP GfxInfo::SpoofDriverVersion(const nsAString & aDriverVersion)
{ {
EnsureInitialized();
mGLStrings->SpoofVersion(NS_LossyConvertUTF16toASCII(aDriverVersion)); mGLStrings->SpoofVersion(NS_LossyConvertUTF16toASCII(aDriverVersion));
return NS_OK; return NS_OK;
} }