Merge m-c to m-i

This commit is contained in:
Phil Ringnalda 2014-11-15 15:41:15 -08:00
commit b1544015f4
57 changed files with 1465 additions and 360 deletions

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ccda843399fe0d26fac7747fd787e5fe2e8dfdf7"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1dd8ad8f96988afebc9691e1b818fa37aa32c790"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
@ -23,7 +23,7 @@
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="ec7f77a328cb8bddd1907f90d16865a986bb4fef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="11ad0ea69796915552c9bae148d81fddf9856ddb"/>
<!-- Stock Android things -->
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>

View File

@ -19,13 +19,13 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ccda843399fe0d26fac7747fd787e5fe2e8dfdf7"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1dd8ad8f96988afebc9691e1b818fa37aa32c790"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="67f2907bc340bad250b4ea6ce2902b52896c9ef0"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="ec7f77a328cb8bddd1907f90d16865a986bb4fef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="11ad0ea69796915552c9bae148d81fddf9856ddb"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
<project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>

View File

@ -17,10 +17,10 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ccda843399fe0d26fac7747fd787e5fe2e8dfdf7"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1dd8ad8f96988afebc9691e1b818fa37aa32c790"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="ec7f77a328cb8bddd1907f90d16865a986bb4fef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="11ad0ea69796915552c9bae148d81fddf9856ddb"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<!-- Stock Android things -->

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ccda843399fe0d26fac7747fd787e5fe2e8dfdf7"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1dd8ad8f96988afebc9691e1b818fa37aa32c790"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
@ -23,7 +23,7 @@
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="ec7f77a328cb8bddd1907f90d16865a986bb4fef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="11ad0ea69796915552c9bae148d81fddf9856ddb"/>
<!-- Stock Android things -->
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>

View File

@ -19,13 +19,13 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ccda843399fe0d26fac7747fd787e5fe2e8dfdf7"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1dd8ad8f96988afebc9691e1b818fa37aa32c790"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="67f2907bc340bad250b4ea6ce2902b52896c9ef0"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="ec7f77a328cb8bddd1907f90d16865a986bb4fef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="11ad0ea69796915552c9bae148d81fddf9856ddb"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
<project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ccda843399fe0d26fac7747fd787e5fe2e8dfdf7"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1dd8ad8f96988afebc9691e1b818fa37aa32c790"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
@ -23,7 +23,7 @@
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="ec7f77a328cb8bddd1907f90d16865a986bb4fef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="11ad0ea69796915552c9bae148d81fddf9856ddb"/>
<!-- Stock Android things -->
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>

View File

@ -17,10 +17,10 @@
</project>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ccda843399fe0d26fac7747fd787e5fe2e8dfdf7"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1dd8ad8f96988afebc9691e1b818fa37aa32c790"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="ec7f77a328cb8bddd1907f90d16865a986bb4fef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="11ad0ea69796915552c9bae148d81fddf9856ddb"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<!-- Stock Android things -->

View File

@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
"revision": "1f41a654b3311705e7d71ff9b2137d3340c6c272",
"revision": "eba399d3ba9724c77e073adccc0dfe9f8169dceb",
"repo_path": "integration/gaia-central"
}

View File

@ -17,11 +17,11 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ccda843399fe0d26fac7747fd787e5fe2e8dfdf7"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1dd8ad8f96988afebc9691e1b818fa37aa32c790"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="ec7f77a328cb8bddd1907f90d16865a986bb4fef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="11ad0ea69796915552c9bae148d81fddf9856ddb"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
<project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>

View File

@ -15,7 +15,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ccda843399fe0d26fac7747fd787e5fe2e8dfdf7"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1dd8ad8f96988afebc9691e1b818fa37aa32c790"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -17,10 +17,10 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ccda843399fe0d26fac7747fd787e5fe2e8dfdf7"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1dd8ad8f96988afebc9691e1b818fa37aa32c790"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="ec7f77a328cb8bddd1907f90d16865a986bb4fef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="11ad0ea69796915552c9bae148d81fddf9856ddb"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<!-- Stock Android things -->

View File

@ -17,12 +17,12 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ccda843399fe0d26fac7747fd787e5fe2e8dfdf7"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1dd8ad8f96988afebc9691e1b818fa37aa32c790"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="ec7f77a328cb8bddd1907f90d16865a986bb4fef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="11ad0ea69796915552c9bae148d81fddf9856ddb"/>
<project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>

View File

@ -1,5 +1,5 @@
<?xml version="1.0"?>
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1415397193000">
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1416004676000">
<emItems>
<emItem blockID="i58" id="webmaster@buzzzzvideos.info">
<versionRange minVersion="0" maxVersion="*">
@ -533,6 +533,12 @@
</versionRange>
<prefs>
</prefs>
</emItem>
<emItem blockID="i784" id="{41e5ef7a-171d-4ab5-8351-951c65a29908}">
<versionRange minVersion="0" maxVersion="*" severity="3">
</versionRange>
<prefs>
</prefs>
</emItem>
<emItem blockID="i374" id="update@firefox.com">
<versionRange minVersion="0" maxVersion="*" severity="3">
@ -886,6 +892,12 @@
</versionRange>
<prefs>
</prefs>
</emItem>
<emItem blockID="i782" id="safebrowse@safebrowse.co">
<versionRange minVersion="0" maxVersion="*" severity="3">
</versionRange>
<prefs>
</prefs>
</emItem>
<emItem blockID="i654" id="{7b1bf0b6-a1b9-42b0-b75d-252036438bdc}">
<versionRange minVersion="27.8" maxVersion="27.9" severity="3">
@ -1542,6 +1554,12 @@
</versionRange>
<prefs>
</prefs>
</emItem>
<emItem blockID="i780" id="{b6ef1336-69bb-45b6-8cba-e578fc0e4433}">
<versionRange minVersion="0" maxVersion="*" severity="3">
</versionRange>
<prefs>
</prefs>
</emItem>
<emItem blockID="i96" id="youtubeee@youtuber3.com">
<versionRange minVersion="0" maxVersion="*">

View File

@ -399,8 +399,6 @@ const CustomizableWidgets = [
}
}, {
id: "social-share-button",
tooltiptext: "social-share-button.label",
label: "social-share-button.tooltiptext",
// custom build our button so we can attach to the share command
type: "custom",
onBuild: function(aDocument) {

View File

@ -183,7 +183,17 @@ let TimelineController = {
* updating the UI as needed.
*/
_stopRecordingAndDiscardData: function*() {
// Clear the markers before calling async method _stopRecording to properly
// reset the selection if markers were already received. Bug 1092452.
this._markers.length = 0;
this._memory.length = 0;
yield this._stopRecording();
// Clear the markers after _stopRecording has finished. It's possible that
// server sent new markers before it received the request to stop sending
// them and client received them while we were waiting for _stopRecording
// to finish. Bug 1067287.
this._markers.length = 0;
this._memory.length = 0;
},

View File

@ -914,6 +914,9 @@ bin/libfreebl_32int64_3.so
@BINPATH@/components/DataStoreImpl.js
@BINPATH@/components/dom_datastore.xpt
; Shutdown Terminator
@BINPATH@/components/nsTerminatorTelemetry.js
@BINPATH@/components/terminator.manifest
#ifdef MOZ_ASAN
#ifdef CLANG_CXX

View File

@ -1303,15 +1303,15 @@ this.UITour = {
addNavBarWidget: function (aTarget, aMessageManager, aCallbackID) {
if (aTarget.node) {
log.error("UITour: can't add a widget already present: " + data.target);
log.error("addNavBarWidget: can't add a widget already present:", aTarget);
return;
}
if (!aTarget.allowAdd) {
log.error("UITour: not allowed to add this widget: " + data.target);
log.error("addNavBarWidget: not allowed to add this widget:", aTarget);
return;
}
if (!aTarget.widgetName) {
log.error("UITour: can't add a widget without a widgetName property: " + data.target);
log.error("addNavBarWidget: can't add a widget without a widgetName property:", aTarget);
return;
}

View File

@ -26,6 +26,8 @@ leak:libfontconfig.so
leak:GI___strdup
# The symbol is really __GI___strdup, but if you have the leading _, it doesn't suppress it.
# Bug 1078015 - If the process terminates during a PR_Sleep, LSAN detects a leak
leak:PR_Sleep
###
### Bug 979928 - WebRTC leaks. m2, m3.
@ -92,6 +94,7 @@ leak:processInternalEntity
###
leak:libcairo.so
leak:libdl.so
leak:libdricore.so
leak:libGL.so
leak:libglib-2.0.so

View File

@ -84,7 +84,7 @@ CameraControlImpl::OnHardwareStateChange(CameraControlListener::HardwareState aN
}
#ifdef PR_LOGGING
const char* state[] = { "open", "closed", "failed" };
const char* state[] = { "closed", "open", "failed" };
MOZ_ASSERT(aNewState >= 0);
if (static_cast<unsigned int>(aNewState) < sizeof(state) / sizeof(state[0])) {
DOM_CAMERA_LOGI("New hardware state is '%s'\n", state[aNewState]);

View File

@ -313,22 +313,114 @@ nsGonkCameraControl::SetConfigurationImpl(const Configuration& aConfig)
return StartPreviewImpl();
}
nsresult
nsGonkCameraControl::MaybeAdjustVideoSize()
{
MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
MOZ_ASSERT(mSeparateVideoAndPreviewSizesSupported);
const Size& preview = mCurrentConfiguration.mPreviewSize;
// Some camera drivers will ignore our preview size if it's larger
// than the currently set video recording size, so in picture mode, we
// give preview size priority, and bump up the video size just in case.
// This is done on a best-effort basis.
if (preview.width <= mLastRecorderSize.width &&
preview.height <= mLastRecorderSize.height) {
DOM_CAMERA_LOGI("Video size %ux%u is suitable for preview size %ux%u\n",
mLastRecorderSize.width, mLastRecorderSize.height,
preview.width, preview.height);
return NS_OK;
}
nsTArray<Size> sizes;
nsresult rv = Get(CAMERA_PARAM_SUPPORTED_VIDEOSIZES, sizes);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
const uint32_t previewArea = preview.width * preview.height;
uint32_t bestDelta = UINT32_MAX;
bool foundBest = false;
SizeIndex best;
for (SizeIndex i = 0; i < sizes.Length(); ++i) {
const Size& s = sizes[i];
if (s.width < preview.width || s.height < preview.height) {
continue;
}
const uint32_t area = s.width * s.height;
const uint32_t delta = area - previewArea;
if (delta < bestDelta) {
bestDelta = delta;
best = i;
foundBest = true;
}
}
if (!foundBest) {
// If no candidate was found, the driver will be fine with a video size
// smaller than the chosen preview size.
DOM_CAMERA_LOGI("No video size candidate for preview size %ux%u (0x%x)\n",
preview.width, preview.height, rv);
return NS_OK;
}
DOM_CAMERA_LOGI("Adjusting video size upwards to %ux%u\n",
sizes[best].width, sizes[best].height);
rv = Set(CAMERA_PARAM_VIDEOSIZE, sizes[best]);
if (NS_FAILED(rv)) {
DOM_CAMERA_LOGW("Failed to adjust video size for preview size %ux%u (0x%x)\n",
preview.width, preview.height, rv);
return rv;
}
mLastRecorderSize = preview;
return NS_OK;
}
nsresult
nsGonkCameraControl::SetPictureConfiguration(const Configuration& aConfig)
{
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
nsresult rv = SetPreviewSize(aConfig.mPreviewSize);
nsTArray<Size> sizes;
nsresult rv = Get(CAMERA_PARAM_SUPPORTED_PREVIEWSIZES, sizes);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
Size preview;
rv = GetSupportedSize(aConfig.mPreviewSize, sizes, preview);
if (NS_FAILED(rv)) {
DOM_CAMERA_LOGE(
"Failed to find a supported preview size, requested size %ux%u (0x%x)",
aConfig.mPreviewSize.width, aConfig.mPreviewSize.height, rv);
return rv;
}
rv = Set(CAMERA_PARAM_PREVIEWSIZE, preview);
if (NS_FAILED(rv)) {
DOM_CAMERA_LOGE("Failed to set supported preview size %ux%u (0x%x)",
preview.width, preview.height, rv);
return rv;
}
mCurrentConfiguration.mPreviewSize = preview;
if (mSeparateVideoAndPreviewSizesSupported) {
MaybeAdjustVideoSize();
}
mParams.Get(CAMERA_PARAM_PREVIEWFRAMERATE, mPreviewFps);
DOM_CAMERA_LOGI("picture mode preview: wanted %ux%u, got %ux%u (%u fps)\n",
aConfig.mPreviewSize.width, aConfig.mPreviewSize.height,
mCurrentConfiguration.mPreviewSize.width, mCurrentConfiguration.mPreviewSize.height,
mPreviewFps);
aConfig.mPreviewSize.width, aConfig.mPreviewSize.height,
preview.width, preview.height,
mPreviewFps);
return NS_OK;
}
@ -695,11 +787,10 @@ nsGonkCameraControl::SetThumbnailSizeImpl(const Size& aSize)
int area = supportedSizes[i].width * supportedSizes[i].height;
int delta = abs(area - targetArea);
if (area != 0
&& delta < smallestDelta
&& supportedSizes[i].width * mLastPictureSize.height /
supportedSizes[i].height == mLastPictureSize.width
) {
if (area != 0 &&
delta < smallestDelta &&
supportedSizes[i].width * mLastPictureSize.height ==
mLastPictureSize.width * supportedSizes[i].height) {
smallestDelta = delta;
smallestDeltaIndex = i;
}
@ -1249,71 +1340,9 @@ nsGonkCameraControl::OnTakePictureError()
NS_ERROR_FAILURE);
}
nsresult
nsGonkCameraControl::SetPreviewSize(const Size& aSize)
{
MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
nsTArray<Size> previewSizes;
nsresult rv = Get(CAMERA_PARAM_SUPPORTED_PREVIEWSIZES, previewSizes);
if (NS_FAILED(rv)) {
DOM_CAMERA_LOGE("Camera failed to return any preview sizes (0x%x)\n", rv);
return rv;
}
Size best;
rv = GetSupportedSize(aSize, previewSizes, best);
if (NS_FAILED(rv)) {
DOM_CAMERA_LOGE("Failed to find a supported preview size, requested size %dx%d",
aSize.width, aSize.height);
return rv;
}
if (mSeparateVideoAndPreviewSizesSupported) {
// Some camera drivers will ignore our preview size if it's larger
// than the currently set video recording size, so we need to set
// the video size here as well, just in case.
if (best.width > mLastRecorderSize.width || best.height > mLastRecorderSize.height) {
SetVideoSize(best);
}
} else {
mLastRecorderSize = best;
}
mCurrentConfiguration.mPreviewSize = best;
return Set(CAMERA_PARAM_PREVIEWSIZE, best);
}
nsresult
nsGonkCameraControl::SetVideoSize(const Size& aSize)
{
MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
if (!mSeparateVideoAndPreviewSizesSupported) {
DOM_CAMERA_LOGE("Camera does not support setting separate video size\n");
return NS_ERROR_NOT_AVAILABLE;
}
nsTArray<Size> videoSizes;
nsresult rv = Get(CAMERA_PARAM_SUPPORTED_VIDEOSIZES, videoSizes);
if (NS_FAILED(rv)) {
DOM_CAMERA_LOGE("Camera failed to return any video sizes (0x%x)\n", rv);
return rv;
}
Size best;
rv = GetSupportedSize(aSize, videoSizes, best);
if (NS_FAILED(rv)) {
DOM_CAMERA_LOGE("Failed to find a supported video size, requested size %dx%d",
aSize.width, aSize.height);
return rv;
}
mLastRecorderSize = best;
return Set(CAMERA_PARAM_VIDEOSIZE, best);
}
nsresult
nsGonkCameraControl::GetSupportedSize(const Size& aSize,
const nsTArray<Size>& supportedSizes,
const nsTArray<Size>& aSupportedSizes,
Size& best)
{
nsresult rv = NS_ERROR_INVALID_ARG;
@ -1323,24 +1352,25 @@ nsGonkCameraControl::GetSupportedSize(const Size& aSize,
if (!aSize.width && !aSize.height) {
// no size specified, take the first supported size
best = supportedSizes[0];
best = aSupportedSizes[0];
return NS_OK;
} else if (aSize.width && aSize.height) {
// both height and width specified, find the supported size closest to
// the requested size, looking for an exact match first
for (nsTArray<Size>::index_type i = 0; i < supportedSizes.Length(); i++) {
Size size = supportedSizes[i];
for (SizeIndex i = 0; i < aSupportedSizes.Length(); ++i) {
Size size = aSupportedSizes[i];
if (size.width == aSize.width && size.height == aSize.height) {
best = size;
return NS_OK;
}
}
// no exact matches--look for a match closest in area
uint32_t targetArea = aSize.width * aSize.height;
for (nsTArray<Size>::index_type i = 0; i < supportedSizes.Length(); i++) {
Size size = supportedSizes[i];
uint32_t delta = abs((long int)(size.width * size.height - targetArea));
// no exact match on dimensions--look for a match closest in area
const uint32_t targetArea = aSize.width * aSize.height;
for (SizeIndex i = 0; i < aSupportedSizes.Length(); i++) {
Size size = aSupportedSizes[i];
uint32_t delta =
abs(static_cast<long int>(size.width * size.height - targetArea));
if (delta < minSizeDelta) {
minSizeDelta = delta;
best = size;
@ -1349,9 +1379,9 @@ nsGonkCameraControl::GetSupportedSize(const Size& aSize,
}
} else if (!aSize.width) {
// width not specified, find closest height match
for (nsTArray<Size>::index_type i = 0; i < supportedSizes.Length(); i++) {
Size size = supportedSizes[i];
delta = abs((long int)(size.height - aSize.height));
for (SizeIndex i = 0; i < aSupportedSizes.Length(); i++) {
Size size = aSupportedSizes[i];
delta = abs(static_cast<long int>(size.height - aSize.height));
if (delta < minSizeDelta) {
minSizeDelta = delta;
best = size;
@ -1360,9 +1390,9 @@ nsGonkCameraControl::GetSupportedSize(const Size& aSize,
}
} else if (!aSize.height) {
// height not specified, find closest width match
for (nsTArray<Size>::index_type i = 0; i < supportedSizes.Length(); i++) {
Size size = supportedSizes[i];
delta = abs((long int)(size.width - aSize.width));
for (SizeIndex i = 0; i < aSupportedSizes.Length(); i++) {
Size size = aSupportedSizes[i];
delta = abs(static_cast<long int>(size.width - aSize.width));
if (delta < minSizeDelta) {
minSizeDelta = delta;
best = size;
@ -1370,9 +1400,147 @@ nsGonkCameraControl::GetSupportedSize(const Size& aSize,
}
}
}
return rv;
}
nsresult
nsGonkCameraControl::SetVideoAndPreviewSize(const Size& aPreviewSize, const Size& aVideoSize)
{
MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
MOZ_ASSERT(mSeparateVideoAndPreviewSizesSupported);
DOM_CAMERA_LOGI("Setting video size to %ux%u, preview size to %ux%u\n",
aVideoSize.width, aVideoSize.height,
aPreviewSize.width, aPreviewSize.height);
Size oldSize;
nsresult rv = Get(CAMERA_PARAM_PREVIEWSIZE, oldSize);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = Set(CAMERA_PARAM_PREVIEWSIZE, aPreviewSize);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = Set(CAMERA_PARAM_VIDEOSIZE, aVideoSize);
if (NS_WARN_IF(NS_FAILED(rv))) {
Set(CAMERA_PARAM_VIDEOSIZE, oldSize); // error, try to restore the original preview size
return rv;
}
mCurrentConfiguration.mPreviewSize = aPreviewSize;
mLastRecorderSize = aVideoSize;
return NS_OK;
}
nsresult
nsGonkCameraControl::SelectVideoAndPreviewSize(const Configuration& aConfig, const Size& aVideoSize)
{
MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
MOZ_ASSERT(mSeparateVideoAndPreviewSizesSupported);
nsTArray<Size> sizes;
nsresult rv = Get(CAMERA_PARAM_SUPPORTED_VIDEOSIZES, sizes);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
Size video;
rv = GetSupportedSize(aVideoSize, sizes, video);
if (NS_FAILED(rv)) {
DOM_CAMERA_LOGE("Failed to find a supported video size, requested size %ux%u",
aVideoSize.width, aVideoSize.height);
return rv;
}
rv = Get(CAMERA_PARAM_SUPPORTED_PREVIEWSIZES, sizes);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
Size preview;
rv = GetSupportedSize(aConfig.mPreviewSize, sizes, preview);
if (NS_FAILED(rv)) {
DOM_CAMERA_LOGE("Failed to find a supported preview size, requested size %ux%u",
aConfig.mPreviewSize.width, aConfig.mPreviewSize.height);
return rv;
}
Size preferred;
rv = Get(CAMERA_PARAM_PREFERRED_PREVIEWSIZE_FOR_VIDEO, preferred);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// If the requested preview size has the same aspect ratio as the
// requested video size, *and* is the same size or smaller than
// the preferred video size, then we're done.
const uint32_t preferredArea = preferred.width * preferred.height;
if (video.width * aConfig.mPreviewSize.height == aConfig.mPreviewSize.width * video.height &&
preview.width * preview.height <= preferredArea) {
// We're done: set the video and preview sizes and return...
return SetVideoAndPreviewSize(preview, video);
}
// Otherwise, if the requested preview size is larger than the preferred
// size, or there is an aspect ratio mismatch, then we need to set the
// preview size to the closest size smaller than the preferred size,
// preferably with the same aspect ratio as the requested video size.
SizeIndex bestSizeMatch;
SizeIndex bestSizeMatchWithAspectRatio;
bool foundSizeMatch = false;
bool foundSizeMatchWithAspectRatio = false;
uint32_t bestAreaDelta = UINT32_MAX;
uint32_t bestAreaDeltaWithAspect = UINT32_MAX;
for (SizeIndex i = 0; i < sizes.Length(); ++i) {
const Size& s = sizes[i];
const uint32_t area = s.width * s.height;
if (area > preferredArea) {
continue;
}
const uint32_t delta = preferredArea - area;
if (s.width * video.height == video.width * s.height) {
if (delta == 0) {
// exact match, including aspect ratio--we can stop now
bestSizeMatchWithAspectRatio = i;
foundSizeMatchWithAspectRatio = true;
break;
} else if (delta < bestAreaDeltaWithAspect) {
// aspect ratio match
bestAreaDeltaWithAspect = delta;
bestSizeMatchWithAspectRatio = i;
foundSizeMatchWithAspectRatio = true;
}
} else if (delta < bestAreaDelta) {
bestAreaDelta = delta;
bestSizeMatch = i;
foundSizeMatch = true;
}
}
if (foundSizeMatchWithAspectRatio) {
preview = sizes[bestSizeMatchWithAspectRatio];
} else if (foundSizeMatch) {
DOM_CAMERA_LOGW("Unable to match a preview size with aspect ratio of video size %ux%u\n",
video.width, video.height);
preview = sizes[bestSizeMatch];
} else {
DOM_CAMERA_LOGE("Unable to find a preview size for video size %ux%u\n",
video.width, video.height);
return NS_ERROR_INVALID_ARG;
}
return SetVideoAndPreviewSize(preview, video);
}
nsresult
nsGonkCameraControl::SetVideoConfiguration(const Configuration& aConfig)
{
@ -1392,12 +1560,11 @@ nsGonkCameraControl::SetVideoConfiguration(const Configuration& aConfig)
return NS_ERROR_INVALID_ARG;
}
mCurrentConfiguration.mRecorderProfile = aConfig.mRecorderProfile;
const RecorderProfile::Video& video(profile->GetVideo());
const Size& size = video.GetSize();
int fps = video.GetFramesPerSecond();
if (fps <= 0 || size.width <= 0 || size.height <= 0) {
DOM_CAMERA_LOGE("Can't configure video with fps=%d, width=%d, height=%d\n",
const uint32_t fps = video.GetFramesPerSecond();
if (fps == 0 || fps > INT_MAX || size.width == 0 || size.height == 0) {
DOM_CAMERA_LOGE("Can't configure video with fps=%u, width=%u, height=%u\n",
fps, size.width, size.height);
return NS_ERROR_FAILURE;
}
@ -1410,32 +1577,26 @@ nsGonkCameraControl::SetVideoConfiguration(const Configuration& aConfig)
if (mSeparateVideoAndPreviewSizesSupported) {
// The camera supports two video streams: a low(er) resolution preview
// stream and and a potentially high(er) resolution stream for encoding.
rv = SetVideoSize(size);
// stream and and a potentially high(er) resolution stream for encoding.
rv = SelectVideoAndPreviewSize(aConfig, size);
if (NS_FAILED(rv)) {
DOM_CAMERA_LOGE("Failed to set video mode video size (0x%x)\n", rv);
return rv;
}
// The video size must be set first, before the preview size, because
// some platforms have a dependency between the two.
rv = SetPreviewSize(aConfig.mPreviewSize);
if (NS_FAILED(rv)) {
DOM_CAMERA_LOGE("Failed to set video mode preview size (0x%x)\n", rv);
DOM_CAMERA_LOGE("Failed to set video and preview sizes (0x%x)\n", rv);
return rv;
}
} else {
// The camera only supports a single video stream: in this case, we set
// the preview size to be the desired video recording size, and ignore
// the specified preview size.
rv = SetPreviewSize(size);
rv = Set(CAMERA_PARAM_PREVIEWSIZE, size);
if (NS_FAILED(rv)) {
DOM_CAMERA_LOGE("Failed to set video mode preview size (0x%x)\n", rv);
return rv;
}
mCurrentConfiguration.mPreviewSize = size;
}
rv = Set(CAMERA_PARAM_PREVIEWFRAMERATE, fps);
rv = Set(CAMERA_PARAM_PREVIEWFRAMERATE, static_cast<int>(fps));
if (NS_FAILED(rv)) {
DOM_CAMERA_LOGE("Failed to set video mode frame rate (0x%x)\n", rv);
return rv;

View File

@ -98,6 +98,8 @@ protected:
using CameraControlImpl::OnConfigurationChange;
using CameraControlImpl::OnUserError;
typedef nsTArray<Size>::index_type SizeIndex;
virtual void BeginBatchParameterSet() MOZ_OVERRIDE;
virtual void EndBatchParameterSet() MOZ_OVERRIDE;
@ -130,8 +132,9 @@ protected:
nsresult SetupRecording(int aFd, int aRotation, uint64_t aMaxFileSizeBytes,
uint64_t aMaxVideoLengthMs);
nsresult SetupRecordingFlash(bool aAutoEnableLowLightTorch);
nsresult SetPreviewSize(const Size& aSize);
nsresult SetVideoSize(const Size& aSize);
nsresult SelectVideoAndPreviewSize(const Configuration& aConfig, const Size& aVideoSize);
nsresult SetVideoAndPreviewSize(const Size& aPreviewSize, const Size& aVideoSize);
nsresult MaybeAdjustVideoSize();
nsresult PausePreview();
nsresult GetSupportedSize(const Size& aSize, const nsTArray<Size>& supportedSizes, Size& best);

View File

@ -117,6 +117,8 @@ GonkCameraParameters::Parameters::GetTextKey(uint32_t aKey)
return KEY_RECORDING_HINT;
case CAMERA_PARAM_PICTURE_QUALITY:
return KEY_JPEG_QUALITY;
case CAMERA_PARAM_PREFERRED_PREVIEWSIZE_FOR_VIDEO:
return KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO;
case CAMERA_PARAM_SUPPORTED_PREVIEWSIZES:
return KEY_SUPPORTED_PREVIEW_SIZES;

View File

@ -54,6 +54,7 @@ enum {
CAMERA_PARAM_LUMINANCE,
CAMERA_PARAM_SCENEMODE_HDR_RETURNNORMALPICTURE,
CAMERA_PARAM_RECORDINGHINT,
CAMERA_PARAM_PREFERRED_PREVIEWSIZE_FOR_VIDEO,
// supported features
CAMERA_PARAM_SUPPORTED_PREVIEWSIZES,

View File

@ -1469,25 +1469,6 @@ CanvasRenderingContext2D::ClearTarget()
state->colorStyles[Style::FILL] = NS_RGB(0,0,0);
state->colorStyles[Style::STROKE] = NS_RGB(0,0,0);
state->shadowColor = NS_RGBA(0,0,0,0);
// For vertical writing-mode, unless text-orientation is sideways,
// we'll modify the initial value of textBaseline to 'middle'.
nsRefPtr<nsStyleContext> canvasStyle;
if (mCanvasElement && mCanvasElement->IsInDoc()) {
nsCOMPtr<nsIPresShell> presShell = GetPresShell();
if (presShell) {
canvasStyle =
nsComputedDOMStyle::GetStyleContextForElement(mCanvasElement,
nullptr,
presShell);
if (canvasStyle) {
WritingMode wm(canvasStyle);
if (wm.IsVertical() && !wm.IsSideways()) {
state->textBaseline = TextBaseline::MIDDLE;
}
}
}
}
}
NS_IMETHODIMP
@ -3284,7 +3265,6 @@ struct MOZ_STACK_CLASS CanvasBidiProcessor : public nsBidiPresUtils::BidiProcess
gfxPoint point = mPt;
bool rtl = mTextRun->IsRightToLeft();
bool verticalRun = mTextRun->IsVertical();
bool centerBaseline = mTextRun->UseCenterBaseline();
gfxFloat& inlineCoord = verticalRun ? point.y : point.x;
inlineCoord += xOffset;
@ -3353,27 +3333,20 @@ struct MOZ_STACK_CLASS CanvasBidiProcessor : public nsBidiPresUtils::BidiProcess
if (runs[c].mOrientation ==
gfxTextRunFactory::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT) {
sidewaysRestore.Init(mCtx->mTarget);
// TODO: The baseline adjustment here is kinda ad-hoc; eventually
// perhaps we should check for horizontal and vertical baseline data
// in the font, and adjust accordingly.
// (The same will be true for HTML text layout.)
const gfxFont::Metrics& metrics = mTextRun->GetFontGroup()->
GetFirstValidFont()->GetMetrics(gfxFont::eHorizontal);
gfx::Matrix mat = mCtx->mTarget->GetTransform().Copy().
mCtx->mTarget->SetTransform(mCtx->mTarget->GetTransform().Copy().
PreTranslate(baselineOrigin). // translate origin for rotation
PreRotate(gfx::Float(M_PI / 2.0)). // turn 90deg clockwise
PreTranslate(-baselineOrigin); // undo the translation
if (centerBaseline) {
// TODO: The baseline adjustment here is kinda ad hoc; eventually
// perhaps we should check for horizontal and vertical baseline data
// in the font, and adjust accordingly.
// (The same will be true for HTML text layout.)
float offset = (metrics.emAscent - metrics.emDescent) / 2;
mat = mat.PreTranslate(Point(0, offset));
// offset the (alphabetic) baseline of the
PreTranslate(-baselineOrigin). // undo the translation
PreTranslate(Point(0, (metrics.emAscent - metrics.emDescent) / 2)));
// and offset the (alphabetic) baseline of the
// horizontally-shaped text from the (centered)
// default baseline used for vertical
}
mCtx->mTarget->SetTransform(mat);
}
RefPtr<GlyphRenderingOptions> renderingOptions = font->GetGlyphRenderingOptions();
@ -3665,45 +3638,39 @@ CanvasRenderingContext2D::DrawOrMeasureText(const nsAString& aRawText,
processor.mPt.x -= anchorX * totalWidth;
// offset pt.y (or pt.x, for vertical text) based on text baseline
// offset pt.y based on text baseline
processor.mFontgrp->UpdateUserFonts(); // ensure user font generation is current
const gfxFont::Metrics& fontMetrics =
processor.mFontgrp->GetFirstValidFont()->GetMetrics(gfxFont::eHorizontal);
processor.mFontgrp->GetFirstValidFont()->GetMetrics(
((processor.mTextRunFlags & gfxTextRunFactory::TEXT_ORIENT_MASK) ==
gfxTextRunFactory::TEXT_ORIENT_HORIZONTAL)
? gfxFont::eHorizontal : gfxFont::eVertical);
gfxFloat baselineAnchor;
gfxFloat anchorY;
switch (state.textBaseline)
{
case TextBaseline::HANGING:
// fall through; best we can do with the information available
case TextBaseline::TOP:
baselineAnchor = fontMetrics.emAscent;
anchorY = fontMetrics.emAscent;
break;
case TextBaseline::MIDDLE:
baselineAnchor = (fontMetrics.emAscent - fontMetrics.emDescent) * .5f;
anchorY = (fontMetrics.emAscent - fontMetrics.emDescent) * .5f;
break;
case TextBaseline::IDEOGRAPHIC:
// fall through; best we can do with the information available
case TextBaseline::ALPHABETIC:
baselineAnchor = 0;
anchorY = 0;
break;
case TextBaseline::BOTTOM:
baselineAnchor = -fontMetrics.emDescent;
anchorY = -fontMetrics.emDescent;
break;
default:
MOZ_CRASH("unexpected TextBaseline");
}
if (processor.mTextRun->IsVertical()) {
if (processor.mTextRun->UseCenterBaseline()) {
// Adjust to account for mTextRun being shaped using center baseline
// rather than alphabetic.
baselineAnchor -= (fontMetrics.emAscent - fontMetrics.emDescent) * .5f;
}
processor.mPt.x -= baselineAnchor;
} else {
processor.mPt.y += baselineAnchor;
}
processor.mPt.y += anchorY;
// correct bounding box to get it to be the correct size/position
processor.mBoundingBox.width = totalWidth;

View File

@ -134,11 +134,24 @@ else
endif
endif
# This stanza ensures that the set of GeckoView classes does not depend on too
# much of Fennec, where "too much" is defined as the set of potentially
# non-GeckoView classes that GeckoView already depended on at a certain point in
# time. The idea is to set a high-water mark that is not to be crossed.
classycle_jar := $(topsrcdir)/mobile/android/build/classycle/classycle-1.4.1.jar
.geckoview.deps: geckoview.ddf $(classycle_jar) $(ALL_JARS)
java -cp $(classycle_jar) \
classycle.dependency.DependencyChecker \
-mergeInnerClasses \
-dependencies=@$< \
$(ALL_JARS)
@$(TOUCH) $@
# We touch the target file before invoking Proguard so that Proguard's
# outputs are fresher than the target, preventing a subsequent
# invocation from thinking Proguard's outputs are stale. This is safe
# because Make removes the target file if any recipe command fails.
.proguard.deps: $(ALL_JARS)
.proguard.deps: .geckoview.deps $(ALL_JARS)
$(REPORT_BUILD)
@$(TOUCH) $@
java \

View File

@ -0,0 +1,73 @@
# This is a Classycle dependency definition file that asserts that the contents
# of the GeckoView library (Classycle set [lib]) is a dependency (but does not
# depend) on Fennec (Classycle set [main]). The additional Classycle set
# [middle] consists of classes referenced by GeckoView that probably should not
# be referenced. We want this middle set to shrink over time.
show allResults
[lib] = \
org.mozilla.gecko.gfx.* \
org.mozilla.gecko.mozglue.* \
org.mozilla.gecko.sqlite.* \
org.mozilla.gecko.util.* \
org.mozilla.gecko.AndroidGamepadManager \
org.mozilla.gecko.AppConstants \
org.mozilla.gecko.BaseGeckoInterface \
org.mozilla.gecko.ContextGetter \
org.mozilla.gecko.CrashHandler \
org.mozilla.gecko.EventDispatcher \
org.mozilla.gecko.GeckoAccessibility \
org.mozilla.gecko.GeckoAppShell \
org.mozilla.gecko.GeckoBatteryManager \
org.mozilla.gecko.GeckoEditable \
org.mozilla.gecko.GeckoEditableClient \
org.mozilla.gecko.GeckoEditableListener \
org.mozilla.gecko.GeckoEvent \
org.mozilla.gecko.GeckoInputConnection \
org.mozilla.gecko.GeckoJavaSampler \
org.mozilla.gecko.GeckoNetworkManager \
org.mozilla.gecko.GeckoProfile \
org.mozilla.gecko.GeckoScreenOrientation \
org.mozilla.gecko.GeckoSharedPrefs \
org.mozilla.gecko.GeckoThread \
org.mozilla.gecko.GeckoView \
org.mozilla.gecko.GlobalHistory \
org.mozilla.gecko.InputMethods \
org.mozilla.gecko.NSSBridge \
org.mozilla.gecko.NotificationClient \
org.mozilla.gecko.NotificationHandler \
org.mozilla.gecko.PrefsHelper \
org.mozilla.gecko.SmsManager \
org.mozilla.gecko.SurfaceBits \
org.mozilla.gecko.TouchEventInterceptor \
org.mozilla.gecko.ZoomConstraints
[middle] = \
org.mozilla.gecko.prompts.* \
org.mozilla.gecko.AlertNotification \
org.mozilla.gecko.FormAssistPopup \
org.mozilla.gecko.GeckoActivity \
org.mozilla.gecko.GeckoApp \
org.mozilla.gecko.GeckoProfileDirectories \
org.mozilla.gecko.GuestSession \
org.mozilla.gecko.R \
org.mozilla.gecko.Tab \
org.mozilla.gecko.Tabs \
org.mozilla.gecko.Telemetry \
org.mozilla.gecko.TelemetryContract \
org.mozilla.gecko.ThumbnailHelper \
org.mozilla.gecko.db.BrowserDB \
org.mozilla.gecko.db.LocalBrowserDB \
org.mozilla.gecko.distribution.Distribution \
org.mozilla.gecko.favicons.Favicons \
org.mozilla.gecko.favicons.OnFaviconLoadedListener
[main] = org.mozilla.gecko.* excluding [lib] [middle]
check sets [lib] [middle] [main]
check [lib] directlyIndependentOf [main]
# This fails; if this passed, GeckoView would be ready to extract from Fennec.
# check [lib] independentOf [middle]

View File

@ -0,0 +1,22 @@
Copyright (c) 2003-2008, Franz-Josef Elmer, All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Binary file not shown.

View File

@ -55,11 +55,6 @@ MOZ_PAY=1
# Enable UI for healthreporter
MOZ_SERVICES_HEALTHREPORT=1
# Wifi-AP/cell tower data reporting is enabled on non-release builds.
if test ! "$RELEASE_BUILD"; then
MOZ_DATA_REPORTING=1
fi
# Enable runtime locale switching.
MOZ_LOCALE_SWITCHER=1

View File

@ -14,7 +14,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:0.14.1'
classpath 'com.android.tools.build:gradle:0.12.2'
}
}

View File

@ -1,6 +1,6 @@
#Thu Nov 13 14:57:51 PST 2014
#Wed Apr 10 15:27:10 PDT 2013
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.1-bin.zip
distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip

View File

@ -32,6 +32,7 @@ import org.mozilla.mozstumbler.service.stumblerthread.scanners.WifiScanner;
public final class Reporter extends BroadcastReceiver {
private static final String LOG_TAG = AppGlobals.makeLogTag(Reporter.class.getSimpleName());
public static final String ACTION_FLUSH_TO_BUNDLE = AppGlobals.ACTION_NAMESPACE + ".FLUSH";
public static final String ACTION_NEW_BUNDLE = AppGlobals.ACTION_NAMESPACE + ".NEW_BUNDLE";
private boolean mIsStarted;
/* The maximum number of Wi-Fi access points in a single observation. */
@ -62,7 +63,12 @@ public final class Reporter extends BroadcastReceiver {
mContext = context.getApplicationContext();
TelephonyManager tm = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
mPhoneType = tm.getPhoneType();
if (tm != null) {
mPhoneType = tm.getPhoneType();
} else {
Log.d(LOG_TAG, "No telephony manager.");
mPhoneType = TelephonyManager.PHONE_TYPE_NONE;
}
mIsStarted = true;

View File

@ -17,7 +17,6 @@ import org.mozilla.mozstumbler.service.stumblerthread.blocklist.WifiBlockListInt
import org.mozilla.mozstumbler.service.stumblerthread.datahandling.DataStorageManager;
import org.mozilla.mozstumbler.service.stumblerthread.scanners.ScanManager;
import org.mozilla.mozstumbler.service.stumblerthread.scanners.cellscanner.CellScanner;
import org.mozilla.mozstumbler.service.stumblerthread.scanners.cellscanner.CellScannerNoWCDMA;
import org.mozilla.mozstumbler.service.uploadthread.UploadAlarmReceiver;
import org.mozilla.mozstumbler.service.utils.NetworkUtils;
import org.mozilla.mozstumbler.service.utils.PersistentIntentService;
@ -109,10 +108,6 @@ public class StumblerService extends PersistentIntentService
return mScanManager.getCellInfoCount();
}
public int getCurrentCellInfoCount() {
return mScanManager.getCurrentCellInfoCount();
}
public boolean isGeofenced () {
return mScanManager.isGeofenced();
}
@ -125,10 +120,6 @@ public class StumblerService extends PersistentIntentService
NetworkUtils.createGlobalInstance(this);
DataStorageManager.createGlobalInstance(this, this);
if (!CellScanner.isCellScannerImplSet()) {
CellScanner.setCellScannerImpl(new CellScannerNoWCDMA(this));
}
mReporter.startup(this);
}
@ -144,8 +135,6 @@ public class StumblerService extends PersistentIntentService
public void onDestroy() {
super.onDestroy();
UploadAlarmReceiver.cancelAlarm(this, !mScanManager.isPassiveMode());
if (!mScanManager.isScanning()) {
return;
}

View File

@ -424,8 +424,8 @@ public class DataStorageManager {
}
mCurrentReports.reports.add(report);
mCurrentReports.wifiCount = wifiCount;
mCurrentReports.cellCount = cellCount;
mCurrentReports.wifiCount += wifiCount;
mCurrentReports.cellCount += cellCount;
if (mCurrentReports.reports.size() >= MAX_REPORTS_IN_MEMORY) {
// save to disk

View File

@ -65,8 +65,23 @@ public class GPSScanner implements LocationListener {
}
}
private boolean isGpsAvailable(LocationManager locationManager) {
if (locationManager == null ||
locationManager.getProvider(LocationManager.GPS_PROVIDER) == null) {
String msg = "No GPS available, scanning not started.";
Log.d(LOG_TAG, msg);
AppGlobals.guiLogError(msg);
return false;
}
return true;
}
private void startPassiveMode() {
LocationManager locationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
if (!isGpsAvailable(locationManager)) {
return;
}
locationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER,
0,
0, this);
@ -74,6 +89,10 @@ public class GPSScanner implements LocationListener {
private void startActiveMode() {
LocationManager lm = getLocationManager();
if (!isGpsAvailable(lm)) {
return;
}
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER,
ACTIVE_MODE_GPS_MIN_UPDATE_TIME_MS,
ACTIVE_MODE_GPS_MIN_UPDATE_DISTANCE_M,

View File

@ -98,6 +98,11 @@ public class ScanManager {
}
mContext = context.getApplicationContext();
if (mContext == null) {
Log.w(LOG_TAG, "No app context available.");
return;
}
if (mGPSScanner == null) {
mGPSScanner = new GPSScanner(context, this);
mWifiScanner = new WifiScanner(context);
@ -158,10 +163,6 @@ public class ScanManager {
return (mCellScanner == null)? 0 :mCellScanner.getCellInfoCount();
}
public int getCurrentCellInfoCount() {
return (mCellScanner == null)? 0 :mCellScanner.getCurrentCellInfoCount();
}
public int getLocationCount() {
return (mGPSScanner == null)? 0 : mGPSScanner.getLocationCount();
}

View File

@ -65,7 +65,11 @@ public class WifiScanner extends BroadcastReceiver {
}
private List<ScanResult> getScanResults() {
return (sIsTestMode)? mTestModeFakeScanResults : getWifiManager().getScanResults();
WifiManager manager = getWifiManager();
if (manager == null) {
return null;
}
return getWifiManager().getScanResults();
}
@ -105,8 +109,12 @@ public class WifiScanner extends BroadcastReceiver {
deactivatePeriodicScan();
}
} else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) {
ArrayList<ScanResult> scanResults = new ArrayList<ScanResult>();
for (ScanResult scanResult : getScanResults()) {
final List<ScanResult> scanResultList = getScanResults();
if (scanResultList == null) {
return;
}
final ArrayList<ScanResult> scanResults = new ArrayList<ScanResult>();
for (ScanResult scanResult : scanResultList) {
scanResult.BSSID = BSSIDBlockList.canonicalizeBSSID(scanResult.BSSID);
if (shouldLog(scanResult)) {
scanResults.add(scanResult);

View File

@ -4,21 +4,28 @@
package org.mozilla.mozstumbler.service.stumblerthread.scanners.cellscanner;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import android.os.Message;
import android.support.v4.content.LocalBroadcastManager;
import android.telephony.TelephonyManager;
import android.util.Log;
import org.mozilla.mozstumbler.service.AppGlobals;
import org.mozilla.mozstumbler.service.AppGlobals.ActiveOrPassiveStumbling;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import org.mozilla.mozstumbler.service.AppGlobals;
import java.util.concurrent.atomic.AtomicBoolean;
import org.mozilla.mozstumbler.service.AppGlobals.ActiveOrPassiveStumbling;
import org.mozilla.mozstumbler.service.stumblerthread.Reporter;
public class CellScanner {
public static final String ACTION_BASE = AppGlobals.ACTION_NAMESPACE + ".CellScanner.";
@ -30,47 +37,30 @@ public class CellScanner {
private static final long CELL_MIN_UPDATE_TIME = 1000; // milliseconds
private final Context mContext;
private static CellScannerImpl sImpl;
private Timer mCellScanTimer;
private final Set<String> mCells = new HashSet<String>();
private int mCurrentCellInfoCount;
private final ReportFlushedReceiver mReportFlushedReceiver = new ReportFlushedReceiver();
private final AtomicBoolean mReportWasFlushed = new AtomicBoolean();
private Handler mBroadcastScannedHandler;
private final CellScannerImpl mCellScannerImplementation;
public ArrayList<CellInfo> sTestingModeCellInfoArray;
public interface CellScannerImpl {
public void start();
public void stop();
public List<CellInfo> getCellInfo();
void start();
boolean isStarted();
boolean isSupportedOnThisDevice();
void stop();
List<CellInfo> getCellInfo();
}
public CellScanner(Context context) {
mContext = context;
}
private static synchronized CellScannerImpl getImplementation() {
return sImpl;
}
public static synchronized boolean isCellScannerImplSet() {
return sImpl != null;
}
/* Fennec doesn't support the apis needed for full scanning, we have different implementations.*/
public static synchronized void setCellScannerImpl(CellScannerImpl cellScanner) {
sImpl = cellScanner;
mCellScannerImplementation = new CellScannerImplementation(context);
}
public void start(final ActiveOrPassiveStumbling stumblingMode) {
if (getImplementation() == null) {
return;
}
try {
getImplementation().start();
} catch (UnsupportedOperationException uoe) {
Log.e(LOG_TAG, "Cell scanner probe failed", uoe);
if (!mCellScannerImplementation.isSupportedOnThisDevice()) {
return;
}
@ -78,58 +68,95 @@ public class CellScanner {
return;
}
LocalBroadcastManager.getInstance(mContext).registerReceiver(mReportFlushedReceiver,
new IntentFilter(Reporter.ACTION_NEW_BUNDLE));
// This is to ensure the broadcast happens from the same thread the CellScanner start() is on
mBroadcastScannedHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Intent intent = (Intent) msg.obj;
LocalBroadcastManager.getInstance(mContext).sendBroadcastSync(intent);
}
};
mCellScannerImplementation.start();
mCellScanTimer = new Timer();
mCellScanTimer.schedule(new TimerTask() {
int mPassiveScanCount;
@Override
public void run() {
if (getImplementation() == null) {
if (!mCellScannerImplementation.isStarted()) {
return;
}
if (stumblingMode == ActiveOrPassiveStumbling.PASSIVE_STUMBLING &&
mPassiveScanCount++ > AppGlobals.PASSIVE_MODE_MAX_SCANS_PER_GPS)
mPassiveScanCount++ > AppGlobals.PASSIVE_MODE_MAX_SCANS_PER_GPS)
{
mPassiveScanCount = 0;
stop();
return;
}
//if (SharedConstants.isDebug) Log.d(LOG_TAG, "Cell Scanning Timer fired");
final long curTime = System.currentTimeMillis();
ArrayList<CellInfo> cells = (sTestingModeCellInfoArray != null)? sTestingModeCellInfoArray :
new ArrayList<CellInfo>(getImplementation().getCellInfo());
new ArrayList<CellInfo>(mCellScannerImplementation.getCellInfo());
if (mReportWasFlushed.getAndSet(false)) {
clearCells();
}
mCurrentCellInfoCount = cells.size();
if (cells.isEmpty()) {
return;
}
for (CellInfo cell: cells) mCells.add(cell.getCellIdentity());
for (CellInfo cell : cells) {
addToCells(cell.getCellIdentity());
}
Intent intent = new Intent(ACTION_CELLS_SCANNED);
intent.putParcelableArrayListExtra(ACTION_CELLS_SCANNED_ARG_CELLS, cells);
intent.putExtra(ACTION_CELLS_SCANNED_ARG_TIME, curTime);
LocalBroadcastManager.getInstance(mContext).sendBroadcastSync(intent);
// send to handler, so broadcast is not from timer thread
Message message = new Message();
message.obj = intent;
mBroadcastScannedHandler.sendMessage(message);
}
}, 0, CELL_MIN_UPDATE_TIME);
}
public void stop() {
private synchronized void clearCells() {
mCells.clear();
}
private synchronized void addToCells(String cell) {
mCells.add(cell);
}
public synchronized void stop() {
mReportWasFlushed.set(false);
clearCells();
LocalBroadcastManager.getInstance(mContext).unregisterReceiver(mReportFlushedReceiver);
if (mCellScanTimer != null) {
mCellScanTimer.cancel();
mCellScanTimer = null;
}
if (getImplementation() != null) {
getImplementation().stop();
}
mCellScannerImplementation.stop();
}
public int getCellInfoCount() {
public synchronized int getCellInfoCount() {
return mCells.size();
}
public int getCurrentCellInfoCount() {
return mCurrentCellInfoCount;
private class ReportFlushedReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context c, Intent i) {
mReportWasFlushed.set(true);
}
}
}

View File

@ -10,35 +10,40 @@ import android.os.Build;
import android.telephony.CellIdentityCdma;
import android.telephony.CellIdentityGsm;
import android.telephony.CellIdentityLte;
import android.telephony.CellIdentityWcdma;
import android.telephony.CellInfoCdma;
import android.telephony.CellInfoGsm;
import android.telephony.CellInfoLte;
import android.telephony.CellInfoWcdma;
import android.telephony.CellLocation;
import android.telephony.CellSignalStrengthCdma;
import android.telephony.CellSignalStrengthGsm;
import android.telephony.CellSignalStrengthLte;
import android.telephony.CellSignalStrengthWcdma;
import android.telephony.NeighboringCellInfo;
import android.telephony.PhoneStateListener;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
import android.util.Log;
import org.mozilla.mozstumbler.service.AppGlobals;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/* Fennec does not yet support the api level for WCDMA import */
public class CellScannerNoWCDMA implements CellScanner.CellScannerImpl {
public class CellScannerImplementation implements CellScanner.CellScannerImpl {
protected static String LOG_TAG = AppGlobals.makeLogTag(CellScannerNoWCDMA.class.getSimpleName());
protected static String LOG_TAG = AppGlobals.makeLogTag(CellScannerImplementation.class.getSimpleName());
protected GetAllCellInfoScannerImpl mGetAllInfoCellScanner;
protected TelephonyManager mTelephonyManager;
protected boolean mIsStarted;
protected int mPhoneType;
protected final Context mContext;
protected volatile int mSignalStrength;
protected volatile int mCdmaDbm;
protected volatile int mSignalStrength = CellInfo.UNKNOWN_SIGNAL;
protected volatile int mCdmaDbm = CellInfo.UNKNOWN_SIGNAL;
private PhoneStateListener mPhoneStateListener;
@ -53,13 +58,28 @@ public class CellScannerNoWCDMA implements CellScanner.CellScannerImpl {
List<CellInfo> getAllCellInfo(TelephonyManager tm);
}
public CellScannerNoWCDMA(Context context) {
public CellScannerImplementation(Context context) {
mContext = context;
}
public boolean isSupportedOnThisDevice() {
TelephonyManager telephonyManager = mTelephonyManager;
if (telephonyManager == null) {
telephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
}
return telephonyManager != null &&
(telephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA ||
telephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM);
}
@Override
public void start() {
if (mIsStarted) {
public synchronized boolean isStarted() {
return mIsStarted;
}
@Override
public synchronized void start() {
if (mIsStarted || !isSupportedOnThisDevice()) {
return;
}
mIsStarted = true;
@ -72,23 +92,8 @@ public class CellScannerNoWCDMA implements CellScanner.CellScannerImpl {
}
mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
if (mTelephonyManager == null) {
throw new UnsupportedOperationException("TelephonyManager service is not available");
}
mPhoneType = mTelephonyManager.getPhoneType();
if (mPhoneType != TelephonyManager.PHONE_TYPE_GSM
&& mPhoneType != TelephonyManager.PHONE_TYPE_CDMA) {
throw new UnsupportedOperationException("Unexpected Phone Type: " + mPhoneType);
}
mSignalStrength = CellInfo.UNKNOWN_SIGNAL;
mCdmaDbm = CellInfo.UNKNOWN_SIGNAL;
}
mSignalStrength = CellInfo.UNKNOWN_SIGNAL;
mCdmaDbm = CellInfo.UNKNOWN_SIGNAL;
mPhoneStateListener = new PhoneStateListener() {
@Override
public void onSignalStrengthsChanged(SignalStrength ss) {
@ -103,9 +108,9 @@ public class CellScannerNoWCDMA implements CellScanner.CellScannerImpl {
}
@Override
public void stop() {
public synchronized void stop() {
mIsStarted = false;
if (mTelephonyManager != null) {
if (mTelephonyManager != null && mPhoneStateListener != null) {
mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
}
mSignalStrength = CellInfo.UNKNOWN_SIGNAL;
@ -113,7 +118,7 @@ public class CellScannerNoWCDMA implements CellScanner.CellScannerImpl {
}
@Override
public List<CellInfo> getCellInfo() {
public synchronized List<CellInfo> getCellInfo() {
List<CellInfo> records = new ArrayList<CellInfo>();
List<CellInfo> allCells = mGetAllInfoCellScanner.getAllCellInfo(mTelephonyManager);
@ -186,10 +191,39 @@ public class CellScannerNoWCDMA implements CellScanner.CellScannerImpl {
return records;
}
@TargetApi(18)
protected boolean addWCDMACellToList(List<CellInfo> cells,
android.telephony.CellInfo observedCell,
TelephonyManager tm) {
boolean added = false;
if (Build.VERSION.SDK_INT >= 18 &&
observedCell instanceof CellInfoWcdma) {
CellIdentityWcdma ident = ((CellInfoWcdma) observedCell).getCellIdentity();
if (ident.getMnc() != Integer.MAX_VALUE && ident.getMcc() != Integer.MAX_VALUE) {
CellInfo cell = new CellInfo(tm.getPhoneType());
CellSignalStrengthWcdma strength = ((CellInfoWcdma) observedCell).getCellSignalStrength();
cell.setWcmdaCellInfo(ident.getMcc(),
ident.getMnc(),
ident.getLac(),
ident.getCid(),
ident.getPsc(),
strength.getAsuLevel());
cells.add(cell);
added = true;
}
}
return added;
}
@TargetApi(18)
protected boolean addCellToList(List<CellInfo> cells,
android.telephony.CellInfo observedCell,
TelephonyManager tm) {
android.telephony.CellInfo observedCell,
TelephonyManager tm) {
if (tm.getPhoneType() == 0) {
return false;
}
boolean added = false;
if (observedCell instanceof CellInfoGsm) {
CellIdentityGsm ident = ((CellInfoGsm) observedCell).getCellIdentity();
@ -226,10 +260,15 @@ public class CellScannerNoWCDMA implements CellScanner.CellScannerImpl {
ident.getTac(),
strength.getAsuLevel(),
strength.getTimingAdvance());
cells.add(cell);
added = true;
cells.add(cell);
added = true;
}
}
if (!added && Build.VERSION.SDK_INT >= 18) {
added = addWCDMACellToList(cells, observedCell, tm);
}
return added;
}

View File

@ -17,7 +17,7 @@ stumbler_sources = [
'java/org/mozilla/mozstumbler/service/stumblerthread/Reporter.java',
'java/org/mozilla/mozstumbler/service/stumblerthread/scanners/cellscanner/CellInfo.java',
'java/org/mozilla/mozstumbler/service/stumblerthread/scanners/cellscanner/CellScanner.java',
'java/org/mozilla/mozstumbler/service/stumblerthread/scanners/cellscanner/CellScannerNoWCDMA.java',
'java/org/mozilla/mozstumbler/service/stumblerthread/scanners/cellscanner/CellScannerImplementation.java',
'java/org/mozilla/mozstumbler/service/stumblerthread/scanners/GPSScanner.java',
'java/org/mozilla/mozstumbler/service/stumblerthread/scanners/LocationBlockList.java',
'java/org/mozilla/mozstumbler/service/stumblerthread/scanners/ScanManager.java',

View File

@ -1125,4 +1125,4 @@ static const TransportSecurityPreload kPublicKeyPinningPreloadList[] = {
static const int32_t kUnknownId = -1;
static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1423912295636000);
static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1424516980930000);

View File

@ -16,7 +16,6 @@ at.search.yahoo.com: did not receive HSTS header
au.search.yahoo.com: did not receive HSTS header
az.search.yahoo.com: did not receive HSTS header
azprep.us: did not receive HSTS header
bassh.net: did not receive HSTS header
bccx.com: could not connect to host
be.search.yahoo.com: did not receive HSTS header
bedeta.de: could not connect to host
@ -89,12 +88,13 @@ facebook.com: did not receive HSTS header
fatzebra.com.au: did not receive HSTS header
fi.search.yahoo.com: did not receive HSTS header
filedir.com: did not receive HSTS header
fixingdns.com: could not connect to host
fixingdns.com: did not receive HSTS header
fj.search.yahoo.com: did not receive HSTS header
fr.search.yahoo.com: did not receive HSTS header
gamesdepartment.co.uk: did not receive HSTS header
get.zenpayroll.com: did not receive HSTS header
getlantern.org: did not receive HSTS header
gizzo.sk: could not connect to host
gl.search.yahoo.com: did not receive HSTS header
glass.google.com: did not receive HSTS header (error ignored - included regardless)
gm.search.yahoo.com: did not receive HSTS header
@ -108,6 +108,7 @@ gr.search.yahoo.com: did not receive HSTS header
greplin.com: could not connect to host
groups.google.com: did not receive HSTS header (error ignored - included regardless)
hackerone-user-content.com: could not connect to host
haste.ch: could not connect to host
history.google.com: did not receive HSTS header (error ignored - included regardless)
hk.search.yahoo.com: did not receive HSTS header
hn.search.yahoo.com: did not receive HSTS header
@ -121,6 +122,7 @@ id.search.yahoo.com: did not receive HSTS header
ie.search.yahoo.com: did not receive HSTS header
ilmconpm.de: did not receive HSTS header
in.search.yahoo.com: did not receive HSTS header
inertianetworks.com: did not receive HSTS header
intercom.io: did not receive HSTS header
iop.intuit.com: max-age too low: 86400
irccloud.com: did not receive HSTS header
@ -139,14 +141,13 @@ li.search.yahoo.com: did not receive HSTS header
liberty.lavabit.com: could not connect to host
lifeguard.aecom.com: max-age too low: 86400
lists.mayfirst.org: did not receive HSTS header
logentries.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
login.corp.google.com: max-age too low: 7776000 (error ignored - included regardless)
logotype.se: did not receive HSTS header
lovelycorral.com: did not receive HSTS header
lt.search.yahoo.com: did not receive HSTS header
lu.search.yahoo.com: did not receive HSTS header
lumi.do: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
luxus-russen.de: did not receive HSTS header
luxus-russen.de: could not connect to host
lv.search.yahoo.com: did not receive HSTS header
m.facebook.com: did not receive HSTS header
m.gparent.org: could not connect to host
@ -161,6 +162,7 @@ megaxchange.com: did not receive HSTS header
minikneet.nl: did not receive HSTS header
mobilethreat.net: could not connect to host
mobilethreatnetwork.net: could not connect to host
mocloud.eu: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
mt.search.yahoo.com: did not receive HSTS header
mtouch.facebook.com: did not receive HSTS header
mu.search.yahoo.com: did not receive HSTS header
@ -216,6 +218,7 @@ ru.search.yahoo.com: did not receive HSTS header
rw.search.yahoo.com: did not receive HSTS header
sah3.net: could not connect to host
saturngames.co.uk: did not receive HSTS header
schreiber-netzwerk.eu: max-age too low: 0
script.google.com: did not receive HSTS header (error ignored - included regardless)
se.search.yahoo.com: did not receive HSTS header
search.yahoo.com: did not receive HSTS header
@ -235,10 +238,13 @@ sol.io: could not connect to host
souyar.de: could not connect to host
souyar.net: could not connect to host
souyar.us: could not connect to host
spdysync.com: did not receive HSTS header
spongepowered.org: did not receive HSTS header
spreadsheets.google.com: did not receive HSTS header (error ignored - included regardless)
square.com: did not receive HSTS header
ssl.google-analytics.com: did not receive HSTS header (error ignored - included regardless)
ssl.panoramio.com: did not receive HSTS header
staticanime.net: did not receive HSTS header
stocktrade.de: could not connect to host
sunshinepress.org: could not connect to host
surfeasy.com: did not receive HSTS header
@ -252,7 +258,6 @@ tektoria.de: did not receive HSTS header
temehu.com: did not receive HSTS header
terrax.berlin: could not connect to host
th.search.yahoo.com: did not receive HSTS header
tonerjet.co.uk: could not connect to host
touch.facebook.com: did not receive HSTS header
tr.search.yahoo.com: did not receive HSTS header
translate.googleapis.com: did not receive HSTS header (error ignored - included regardless)

View File

@ -8,7 +8,7 @@
/*****************************************************************************/
#include <stdint.h>
const PRTime gPreloadListExpirationTime = INT64_C(1426331489992000);
const PRTime gPreloadListExpirationTime = INT64_C(1426936177225000);
class nsSTSPreload
{
@ -36,6 +36,7 @@ static const nsSTSPreload kSTSPreloadList[] = {
{ "aiticon.com", true },
{ "aladdinschools.appspot.com", false },
{ "alexsexton.com", true },
{ "alexyang.me", true },
{ "alpha.irccloud.com", false },
{ "andreasbreitenlohner.de", true },
{ "anetaben.nl", true },
@ -73,10 +74,12 @@ static const nsSTSPreload kSTSPreloadList[] = {
{ "barslecht.com", true },
{ "barslecht.nl", true },
{ "baruch.me", true },
{ "bassh.net", true },
{ "bautied.de", true },
{ "bayrisch-fuer-anfaenger.de", true },
{ "bccx.com", true },
{ "bcrook.com", false },
{ "beamitapp.com", true },
{ "beastowner.com", true },
{ "beastowner.li", true },
{ "bedeta.de", true },
@ -102,6 +105,7 @@ static const nsSTSPreload kSTSPreloadList[] = {
{ "blog.torproject.org", false },
{ "bodo-wolff.de", true },
{ "bohramt.de", true },
{ "bonigo.de", true },
{ "boxcryptor.com", true },
{ "brunosouza.org", true },
{ "buddhistische-weisheiten.org", true },
@ -119,15 +123,19 @@ static const nsSTSPreload kSTSPreloadList[] = {
{ "cdnb.co", true },
{ "celltek-server.de", false },
{ "certible.com", true },
{ "certly.io", true },
{ "chainmonitor.com", true },
{ "check.torproject.org", false },
{ "checkout.google.com", true },
{ "chontalpa.pw", true },
{ "chrisjean.com", true },
{ "chrome-devtools-frontend.appspot.com", true },
{ "chrome.google.com", true },
{ "chromiumcodereview.appspot.com", false },
{ "chulado.com", true },
{ "cktennis.com", true },
{ "clapping-rhymes.com", true },
{ "clintwilson.technology", true },
{ "cloud.google.com", true },
{ "cloudcert.org", true },
{ "cloudns.com.au", true },
@ -151,6 +159,7 @@ static const nsSTSPreload kSTSPreloadList[] = {
{ "crowdjuris.com", true },
{ "crypto.cat", false },
{ "cryptopartyatx.org", true },
{ "cspbuilder.info", true },
{ "cube.de", true },
{ "cujanovic.com", true },
{ "cupcake.io", true },
@ -171,14 +180,17 @@ static const nsSTSPreload kSTSPreloadList[] = {
{ "debtkit.co.uk", true },
{ "dedimax.de", true },
{ "denh.am", true },
{ "depechemode-live.com", true },
{ "derevtsov.com", false },
{ "derhil.de", true },
{ "destinationbijoux.fr", true },
{ "detectify.com", false },
{ "developer.mydigipass.com", false },
{ "devh.de", true },
{ "diamante.ro", true },
{ "die-besten-weisheiten.de", true },
{ "dillonkorman.com", true },
{ "dinamoelektrik.com", true },
{ "dist.torproject.org", false },
{ "dl.google.com", true },
{ "dlc.viasinc.com", true },
@ -211,22 +223,24 @@ static const nsSTSPreload kSTSPreloadList[] = {
{ "errors.zenpayroll.com", false },
{ "esec.rs", true },
{ "espra.com", true },
{ "ethack.org", true },
{ "ethitter.com", true },
{ "eurotramp.com", true },
{ "everhome.de", true },
{ "evstatus.com", true },
{ "explodie.org", true },
{ "f-droid.org", true },
{ "fabianfischer.de", true },
{ "factor.cc", false },
{ "fairbill.com", true },
{ "faq.lookout.com", false },
{ "fastcomcorp.net", true },
{ "fedorapeople.org", true },
{ "feedbin.com", false },
{ "ferienhaus-polchow-ruegen.de", false },
{ "fiken.no", true },
{ "firemail.io", true },
{ "fischer-its.com", true },
{ "fixingdns.com", true },
{ "fj.simple.com", false },
{ "flamer-scene.com", true },
{ "fleximus.org", false },
@ -313,13 +327,15 @@ static const nsSTSPreload kSTSPreloadList[] = {
{ "imouto.my", false },
{ "in.xero.com", false },
{ "inbox.google.com", true },
{ "inertianetworks.com", true },
{ "inkbunny.net", true },
{ "inleaked.com", true },
{ "insouciant.org", true },
{ "irische-segenswuensche.info", true },
{ "ironfistdesign.com", true },
{ "isitchristmas.com", true },
{ "it-schwerin.de", true },
{ "itsamurai.ru", true },
{ "itshost.ru", true },
{ "jackyyf.com", false },
{ "janoberst.com", true },
{ "janus-engineering.de", true },
@ -327,16 +343,19 @@ static const nsSTSPreload kSTSPreloadList[] = {
{ "jelmer.uk", true },
{ "jfreitag.de", true },
{ "jitsi.org", false },
{ "jmedved.com", true },
{ "jonas-keidel.de", true },
{ "jonaswitmer.ch", true },
{ "jonnybarnes.uk", true },
{ "julian-kipka.de", true },
{ "jwilsson.com", true },
{ "jwilsson.me", true },
{ "k-dev.de", true },
{ "kaheim.de", true },
{ "kardize24.pl", true },
{ "kartonmodellbau.org", true },
{ "kdex.de", true },
{ "keepclean.me", true },
{ "keeperapp.com", true },
{ "keepersecurity.com", true },
{ "kernel-error.de", true },
@ -349,6 +368,7 @@ static const nsSTSPreload kSTSPreloadList[] = {
{ "kinsights.com", false },
{ "kitsta.com", true },
{ "klatschreime.de", true },
{ "klausbrinch.dk", true },
{ "klaxn.com", true },
{ "kleidertauschpartys.de", true },
{ "knowledgehook.com", true },
@ -364,6 +384,7 @@ static const nsSTSPreload kSTSPreloadList[] = {
{ "launchkey.com", true },
{ "lavalite.de", true },
{ "lb-toner.de", true },
{ "leonardcamacho.me", true },
{ "library.linode.com", false },
{ "liebel.org", true },
{ "limpid.nl", true },
@ -407,6 +428,7 @@ static const nsSTSPreload kSTSPreloadList[] = {
{ "mattmccutchen.net", true },
{ "mbp.banking.co.at", false },
{ "md5file.com", true },
{ "mdfnet.se", true },
{ "mediacru.sh", true },
{ "medium.com", true },
{ "meetfinch.com", true },
@ -415,7 +437,9 @@ static const nsSTSPreload kSTSPreloadList[] = {
{ "members.mayfirst.org", false },
{ "members.nearlyfreespeech.net", false },
{ "miasarafina.de", true },
{ "michalspacek.cz", true },
{ "mig5.net", true },
{ "mike-bland.com", true },
{ "mikewest.org", true },
{ "miku.hatsune.my", true },
{ "minez-nightswatch.com", true },
@ -458,11 +482,13 @@ static const nsSTSPreload kSTSPreloadList[] = {
{ "nmctest.net", true },
{ "nos-oignons.net", true },
{ "npw.net", true },
{ "oakslighting.co.uk", true },
{ "okmx.de", true },
{ "omitech.co.uk", true },
{ "onedot.nl", true },
{ "onedrive.com", true },
{ "onedrive.live.com", false },
{ "onsitemassageco.com", true },
{ "oplop.appspot.com", true },
{ "opsmate.com", false },
{ "optimus.io", true },
@ -513,6 +539,7 @@ static const nsSTSPreload kSTSPreloadList[] = {
{ "prefontaine.name", true },
{ "profiles.google.com", true },
{ "projektzentrisch.de", true },
{ "propagandism.org", true },
{ "prowhisky.de", true },
{ "pubkey.is", true },
{ "publications.qld.gov.au", false },
@ -550,7 +577,6 @@ static const nsSTSPreload kSTSPreloadList[] = {
{ "sandbox.mydigipass.com", false },
{ "schachburg.de", true },
{ "schokokeks.org", false },
{ "schreiber-netzwerk.eu", true },
{ "schwarzer.it", true },
{ "sciencex.com", true },
{ "scotthelme.co.uk", true },
@ -582,12 +608,13 @@ static const nsSTSPreload kSTSPreloadList[] = {
{ "sites.google.com", true },
{ "skydrive.live.com", false },
{ "slattery.co", true },
{ "slevomat.cz", true },
{ "slidebatch.com", true },
{ "smartcoin.com.br", true },
{ "smartship.co.jp", true },
{ "sour.is", true },
{ "southside-crew.com", true },
{ "spartantheatre.org", true },
{ "spdysync.com", true },
{ "spencerbaer.com", true },
{ "spideroak.com", true },
{ "spreadsheets.google.com", true },
@ -611,10 +638,12 @@ static const nsSTSPreload kSTSPreloadList[] = {
{ "studydrive.net", true },
{ "subrosa.io", true },
{ "suite73.org", true },
{ "sunjaydhama.com", true },
{ "supplies24.at", true },
{ "supplies24.es", true },
{ "support.mayfirst.org", false },
{ "surkatty.org", true },
{ "swehack.org", true },
{ "sylaps.com", true },
{ "sysctl.se", true },
{ "syss.de", true },
@ -634,6 +663,7 @@ static const nsSTSPreload kSTSPreloadList[] = {
{ "therapynotes.com", false },
{ "theshadestore.com", true },
{ "thorncreek.net", false },
{ "thusoy.com", true },
{ "tickopa.co.uk", true },
{ "timtaubert.de", true },
{ "tinfoilsecurity.com", false },
@ -641,6 +671,7 @@ static const nsSTSPreload kSTSPreloadList[] = {
{ "tintenfix.net", true },
{ "tipps-fuer-den-haushalt.de", true },
{ "tittelbach.at", true },
{ "tls.li", true },
{ "tno.io", true },
{ "tobias-kluge.de", true },
{ "tollmanz.com", true },
@ -681,6 +712,7 @@ static const nsSTSPreload kSTSPreloadList[] = {
{ "usaa.com", false },
{ "uzstyle.com", false },
{ "vaddder.com", true },
{ "vhost.co.id", true },
{ "viasinc.com", false },
{ "viennan.net", true },
{ "vmoagents.com", false },
@ -692,6 +724,7 @@ static const nsSTSPreload kSTSPreloadList[] = {
{ "warrencreative.com", false },
{ "watsonhall.uk", true },
{ "webandmore.de", false },
{ "webandwords.com.au", true },
{ "webcollect.org.uk", true },
{ "webfilings-eu-mirror.appspot.com", true },
{ "webfilings-eu.appspot.com", true },
@ -702,6 +735,7 @@ static const nsSTSPreload kSTSPreloadList[] = {
{ "webmail.onlime.ch", false },
{ "webmail.schokokeks.org", false },
{ "websenat.de", true },
{ "webtiles.co.uk", true },
{ "weggeweest.nl", true },
{ "welches-kinderfahrrad.de", true },
{ "wepay.com", false },
@ -773,11 +807,13 @@ static const nsSTSPreload kSTSPreloadList[] = {
{ "xn--maraa-rta.org", true },
{ "xps2pdf.co.uk", true },
{ "y-o-w.com", true },
{ "yoursecondphone.co", true },
{ "ypart.eu", true },
{ "z.ai", true },
{ "zenpayroll.com", false },
{ "zeplin.io", false },
{ "zeropush.com", true },
{ "zixiao.wang", true },
{ "zlavomat.sk", true },
{ "zotero.org", true },
};

View File

@ -4,7 +4,6 @@
#include "mozilla/ModuleUtils.h"
#include "nsAppStartup.h"
#include "nsTerminator.h"
#include "nsUserInfo.h"
#include "nsToolkitCompsCID.h"
#include "nsFindService.h"
@ -44,12 +43,23 @@
#include "NativeFileWatcherNotSupported.h"
#endif // (XP_WIN)
#if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID)
#define MOZ_HAS_TERMINATOR
#endif
#if defined(MOZ_HAS_TERMINATOR)
#include "nsTerminator.h"
#endif
using namespace mozilla;
/////////////////////////////////////////////////////////////////////////////
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsAppStartup, Init)
#if defined(MOZ_HAS_TERMINATOR)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsTerminator)
#endif
NS_GENERIC_FACTORY_CONSTRUCTOR(nsUserInfo)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsFindService)
@ -105,7 +115,9 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(NativeFileWatcherService, Init)
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(AddonPathService, AddonPathService::GetInstance)
NS_DEFINE_NAMED_CID(NS_TOOLKIT_APPSTARTUP_CID);
#if defined(MOZ_HAS_TERMINATOR)
NS_DEFINE_NAMED_CID(NS_TOOLKIT_TERMINATOR_CID);
#endif
NS_DEFINE_NAMED_CID(NS_USERINFO_CID);
NS_DEFINE_NAMED_CID(NS_ALERTSSERVICE_CID);
#if !defined(MOZ_DISABLE_PARENTAL_CONTROLS)
@ -134,7 +146,9 @@ NS_DEFINE_NAMED_CID(NATIVE_FILEWATCHER_SERVICE_CID);
static const Module::CIDEntry kToolkitCIDs[] = {
{ &kNS_TOOLKIT_APPSTARTUP_CID, false, nullptr, nsAppStartupConstructor },
#if defined(MOZ_HAS_TERMINATOR)
{ &kNS_TOOLKIT_TERMINATOR_CID, false, nullptr, nsTerminatorConstructor },
#endif
{ &kNS_USERINFO_CID, false, nullptr, nsUserInfoConstructor },
{ &kNS_ALERTSSERVICE_CID, false, nullptr, nsAlertsServiceConstructor },
#if !defined(MOZ_DISABLE_PARENTAL_CONTROLS)
@ -165,7 +179,9 @@ static const Module::CIDEntry kToolkitCIDs[] = {
static const Module::ContractIDEntry kToolkitContracts[] = {
{ NS_APPSTARTUP_CONTRACTID, &kNS_TOOLKIT_APPSTARTUP_CID },
#if defined(MOZ_HAS_TERMINATOR)
{ NS_TOOLKIT_TERMINATOR_CONTRACTID, &kNS_TOOLKIT_TERMINATOR_CID },
#endif
{ NS_USERINFO_CONTRACTID, &kNS_USERINFO_CID },
{ NS_ALERTSERVICE_CONTRACTID, &kNS_ALERTSSERVICE_CID },
#if !defined(MOZ_DISABLE_PARENTAL_CONTROLS)

View File

@ -45,7 +45,6 @@ DIRS += [
'startup',
'statusfilter',
'telemetry',
'terminator',
'thumbnails',
'typeaheadfind',
'urlformatter',
@ -86,6 +85,9 @@ if CONFIG['MOZ_URL_CLASSIFIER']:
if CONFIG['MOZ_CAPTIVEDETECT']:
DIRS += ['captivedetect']
if CONFIG['MOZ_WIDGET_TOOLKIT'] != "gonk" and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android':
DIRS += ['terminator']
DIRS += ['build']
EXTRA_COMPONENTS += [

View File

@ -86,13 +86,17 @@ SuggestAutoComplete.prototype = {
*/
onResultsReady: function(searchString, results, comments, formHistoryResult) {
if (this._listener) {
// Create a copy of the results array to use as labels, since
// FormAutoCompleteResult doesn't like being passed the same array
// for both.
let labels = results.slice();
let result = new FormAutoCompleteResult(
searchString,
Ci.nsIAutoCompleteResult.RESULT_SUCCESS,
0,
"",
results,
results,
labels,
comments,
formHistoryResult);

View File

@ -6775,6 +6775,34 @@
"n_buckets": 10,
"description": "Sidebar showing: seconds that the sidebar has been opened"
},
"SHUTDOWN_PHASE_DURATION_TICKS_QUIT_APPLICATION": {
"expires_in_version": "never",
"kind": "exponential",
"high": 65,
"n_buckets": 10,
"description": "Duration of shutdown phase quit-application, as measured by the shutdown terminator, in seconds of activity"
},
"SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_CHANGE_TEARDOWN": {
"expires_in_version": "never",
"kind": "exponential",
"high": 65,
"n_buckets": 10,
"description": "Duration of shutdown phase profile-change-teardown, as measured by the shutdown terminator, in seconds of activity"
},
"SHUTDOWN_PHASE_DURATION_TICKS_XPCOM_WILL_SHUTDOWN": {
"expires_in_version": "never",
"kind": "exponential",
"high": 65,
"n_buckets": 10,
"description": "Duration of shutdown phase xpcom-will-shutdown, as measured by the shutdown terminator, in seconds of activity"
},
"SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_BEFORE_CHANGE": {
"expires_in_version": "never",
"kind": "exponential",
"high": 65,
"n_buckets": 10,
"description": "Duration of shutdown phase profile-before-change, as measured by the shutdown terminator, in seconds of activity"
},
"BR_9_2_1_SUBJECT_ALT_NAMES": {
"expires_in_version": "never",
"kind": "enumerated",

View File

@ -4,6 +4,8 @@
# 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/.
XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell/xpcshell.ini']
SOURCES += [
'nsTerminator.cpp',
]
@ -13,6 +15,7 @@ EXPORTS += [
]
EXTRA_COMPONENTS += [
'nsTerminatorTelemetry.js',
'terminator.manifest',
]

View File

@ -18,8 +18,14 @@
#include "nsTerminator.h"
#include "prthread.h"
#include "prmon.h"
#include "plstr.h"
#include "prio.h"
#include "nsString.h"
#include "nsServiceManagerUtils.h"
#include "nsDirectoryServiceUtils.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsIObserverService.h"
#include "nsIPrefService.h"
@ -28,11 +34,14 @@
#endif
#include "mozilla/ArrayUtils.h"
#include "mozilla/Attributes.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/MemoryChecking.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/unused.h"
#include "mozilla/Telemetry.h"
// Normally, the number of milliseconds that AsyncShutdown waits until
// it decides to crash is specified as a preference. We use the
@ -51,29 +60,76 @@ namespace mozilla {
namespace {
/**
* Set to `true` by the main thread whenever we pass a shutdown phase,
* which means that the shutdown is still ongoing. Reset to `false` by
* the Terminator thread, once it has acknowledged the progress.
*/
Atomic<bool> gProgress(false);
// Utility function: create a thread that is non-joinable,
// does not prevent the process from terminating, is never
// cooperatively scheduled, and uses a default stack size.
PRThread* CreateSystemThread(void (*start)(void* arg),
void* arg)
{
PRThread* thread = PR_CreateThread(
PR_SYSTEM_THREAD, /* This thread will not prevent the process from terminating */
start,
arg,
PR_PRIORITY_LOW,
PR_GLOBAL_THREAD /* Make sure that the thread is never cooperatively scheduled */,
PR_UNJOINABLE_THREAD,
0 /* Use default stack size */
);
MOZ_LSAN_INTENTIONALLY_LEAK_OBJECT(thread); // This pointer will never be deallocated.
return thread;
}
////////////////////////////////////////////
//
// The watchdog
//
// This nspr thread is in charge of crashing the process if any stage of shutdown
// lasts more than some predefined duration. As a side-effect, it measures the
// duration of each stage of shutdown.
//
// The heartbeat of the operation.
//
// Main thread:
//
// * Whenever a shutdown step has been completed, the main thread
// swaps gHeartbeat to 0 to mark that the shutdown process is still
// progressing. The value swapped away indicates the number of ticks
// it took for the shutdown step to advance.
//
// Watchdog thread:
//
// * Every tick, the watchdog thread increments gHearbeat atomically.
//
// A note about precision:
// Since gHeartbeat is generally reset to 0 between two ticks, this means
// that gHeartbeat stays at 0 less than one tick. Consequently, values
// extracted from gHeartbeat must be considered rounded up.
Atomic<uint32_t> gHeartbeat(0);
struct Options {
int32_t crashAfterMS;
/**
* How many ticks before we should crash the process.
*/
uint32_t crashAfterTicks;
};
/**
* Entry point for the watchdog thread
*/
void
Run(void* arg)
RunWatchdog(void* arg)
{
PR_SetCurrentThreadName("Shutdown Hang Terminator");
// Let's copy and deallocate options, that's one less leak to worry
// about.
UniquePtr<Options> options((Options*)arg);
int32_t crashAfterMS = options->crashAfterMS;
uint32_t crashAfterTicks = options->crashAfterTicks;
options = nullptr;
int32_t timeToLive = crashAfterMS;
const uint32_t timeToLive = crashAfterTicks;
while (true) {
//
// We do not want to sleep for the entire duration,
@ -86,14 +142,8 @@ Run(void* arg)
// more reasonable.
//
PR_Sleep(TICK_DURATION);
if (gProgress.exchange(false)) {
// We have passed at least one shutdown phase while waiting.
// Shutdown is still alive, reset the countdown.
timeToLive = crashAfterMS;
continue;
}
timeToLive -= TICK_DURATION;
if (timeToLive >= 0) {
if (gHeartbeat++ < timeToLive) {
continue;
}
@ -102,20 +152,177 @@ Run(void* arg)
}
}
} // anonymous namespace
////////////////////////////////////////////
//
// Writer thread
//
// This nspr thread is in charge of writing to disk statistics produced by the
// watchdog thread and collected by the main thread. Note that we use a nspr
// thread rather than usual XPCOM I/O simply because we outlive XPCOM and its
// threads.
//
static char const *const sObserverTopics[] = {
"quit-application",
"profile-change-teardown",
"profile-before-change",
"xpcom-will-shutdown",
"xpcom-shutdown",
// Utility class, used by UniquePtr<> to close nspr files.
class PR_CloseDelete
{
public:
MOZ_CONSTEXPR PR_CloseDelete() {}
PR_CloseDelete(const PR_CloseDelete& aOther)
{}
void operator()(PRFileDesc* aPtr) const
{
PR_Close(aPtr);
}
};
//
// Communication between the main thread and the writer thread.
//
// Main thread:
//
// * Whenever a shutdown step has been completed, the main thread
// obtains the number of ticks from the watchdog threads, builds
// a string representing all the data gathered so far, places
// this string in `gWriteData`, and wakes up the writer thread
// using `gWriteReady`. If `gWriteData` already contained a non-null
// pointer, this means that the writer thread is lagging behind the
// main thread, and the main thread cleans up the memory.
//
// Writer thread:
//
// * When awake, the writer thread swaps `gWriteData` to nullptr. If
// `gWriteData` contained data to write, the . If so, the writer
// thread writes the data to a file named "ShutdownDuration.json.tmp",
// then moves that file to "ShutdownDuration.json" and cleans up the
// data. If `gWriteData` contains a nullptr, the writer goes to sleep
// until it is awkened using `gWriteReady`.
//
//
// The data written by the writer thread will be read by another
// module upon the next restart and fed to Telemetry.
//
Atomic<nsCString*> gWriteData(nullptr);
PRMonitor* gWriteReady = nullptr;
void RunWriter(void* arg)
{
PR_SetCurrentThreadName("Shutdown Statistics Writer");
MOZ_LSAN_INTENTIONALLY_LEAK_OBJECT(arg);
// Shutdown will generally complete before we have a chance to
// deallocate. This is not a leak.
// Setup destinationPath and tmpFilePath
nsCString destinationPath(static_cast<char*>(arg));
nsAutoCString tmpFilePath;
tmpFilePath.Append(destinationPath);
tmpFilePath.AppendLiteral(".tmp");
// Cleanup any file leftover from a previous run
unused << PR_Delete(tmpFilePath.get());
unused << PR_Delete(destinationPath.get());
while (true) {
//
// Check whether we have received data from the main thread.
//
// We perform the check before waiting on `gWriteReady` as we may
// have received data while we were busy writing.
//
// Also note that gWriteData may have been modified several times
// since we last checked. That's ok, we are not losing any important
// data (since we keep adding data), and we are not leaking memory
// (since the main thread deallocates any data that hasn't been
// consumed by the writer thread).
//
UniquePtr<nsCString> data(gWriteData.exchange(nullptr));
if (!data) {
// Data is not available yet.
// Wait until the main thread provides it.
PR_EnterMonitor(gWriteReady);
PR_Wait(gWriteReady, PR_INTERVAL_NO_TIMEOUT);
PR_ExitMonitor(gWriteReady);
continue;
}
MOZ_LSAN_INTENTIONALLY_LEAK_OBJECT(data.get());
// Shutdown may complete before we have a chance to deallocate.
// This is not a leak.
//
// Write to a temporary file
//
// In case of any error, we simply give up. Since the data is
// hardly critical, we don't want to spend too much effort
// salvaging it.
//
UniquePtr<PRFileDesc, PR_CloseDelete>
tmpFileDesc(PR_Open(tmpFilePath.get(),
PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE,
00600));
// Shutdown may complete before we have a chance to close the file.
// This is not a leak.
MOZ_LSAN_INTENTIONALLY_LEAK_OBJECT(tmpFileDesc.get());
if (tmpFileDesc == nullptr) {
break;
}
if (PR_Write(tmpFileDesc.get(), data->get(), data->Length()) == -1) {
break;
}
tmpFileDesc.reset();
//
// Rename on top of destination file.
//
// This is not sufficient to guarantee that the destination file
// will be written correctly, but, again, we don't care enough
// about the data to make more efforts.
//
if (PR_Rename(tmpFilePath.get(), destinationPath.get()) != PR_SUCCESS) {
break;
}
}
}
/**
* A step during shutdown.
*
* Shutdown is divided in steps, which all map to an observer
* notification. The duration of a step is defined as the number of
* ticks between the time we receive a notification and the next one.
*/
struct ShutdownStep
{
char const* const mTopic;
int mTicks;
MOZ_CONSTEXPR ShutdownStep(const char *const topic)
: mTopic(topic)
, mTicks(-1)
{}
};
static ShutdownStep sShutdownSteps[] = {
ShutdownStep("quit-application"),
ShutdownStep("profile-change-teardown"),
ShutdownStep("profile-before-change"),
ShutdownStep("xpcom-will-shutdown"),
ShutdownStep("xpcom-shutdown"),
};
} // anonymous namespace
NS_IMPL_ISUPPORTS(nsTerminator, nsIObserver)
nsTerminator::nsTerminator()
: mInitialized(false)
, mCurrentStep(-1)
{
}
@ -128,45 +335,92 @@ nsTerminator::SelfInit()
return NS_ERROR_UNEXPECTED;
}
for (size_t i = 0; i < ArrayLength(sObserverTopics); ++i) {
DebugOnly<nsresult> rv = os->AddObserver(this, sObserverTopics[i], false);
for (size_t i = 0; i < ArrayLength(sShutdownSteps); ++i) {
DebugOnly<nsresult> rv = os->AddObserver(this, sShutdownSteps[i].mTopic, false);
#if defined(DEBUG)
NS_WARN_IF(NS_FAILED(rv));
#endif // defined(DEBUG)
}
return NS_OK;
}
// Actually launch the thread. This takes place at the first sign of shutdown.
// Actually launch these threads. This takes place at the first sign of shutdown.
void
nsTerminator::Start() {
// Determine how long we need to wait
nsTerminator::Start()
{
MOZ_ASSERT(!mInitialized);
StartWatchdog();
StartWriter();
mInitialized = true;
}
// Prepare, allocate and start the watchdog thread.
// By design, it will never finish, nor be deallocated.
void
nsTerminator::StartWatchdog()
{
int32_t crashAfterMS =
Preferences::GetInt("toolkit.asyncshutdown.crash_timeout",
FALLBACK_ASYNCSHUTDOWN_CRASH_AFTER_MS);
// Ignore negative values
if (crashAfterMS <= 0) {
crashAfterMS = FALLBACK_ASYNCSHUTDOWN_CRASH_AFTER_MS;
}
// Add a little padding, to ensure that we do not crash before
// AsyncShutdown.
crashAfterMS += ADDITIONAL_WAIT_BEFORE_CRASH_MS;
if (crashAfterMS > INT32_MAX - ADDITIONAL_WAIT_BEFORE_CRASH_MS) {
// Defend against overflow
crashAfterMS = INT32_MAX;
} else {
crashAfterMS += ADDITIONAL_WAIT_BEFORE_CRASH_MS;
}
UniquePtr<Options> options(new Options());
options->crashAfterMS = crashAfterMS;
options->crashAfterTicks = crashAfterMS / TICK_DURATION;
// Allocate and start the thread.
// By design, it will never finish, nor be deallocated.
DebugOnly<PRThread*> thread = PR_CreateThread(
PR_SYSTEM_THREAD, /* This thread will not prevent the process from terminating */
Run,
options.release(),
PR_PRIORITY_LOW,
PR_GLOBAL_THREAD /* Make sure that the thread is never cooperatively scheduled */,
PR_UNJOINABLE_THREAD,
0 /* Use default stack size */
);
DebugOnly<PRThread*> watchdogThread = CreateSystemThread(RunWatchdog,
options.release());
MOZ_ASSERT(watchdogThread);
}
MOZ_ASSERT(thread);
mInitialized = true;
// Prepare, allocate and start the writer thread. By design, it will never
// finish, nor be deallocated. In case of error, we degrade
// gracefully to not writing Telemetry data.
void
nsTerminator::StartWriter()
{
if (!Telemetry::CanRecord()) {
return;
}
nsCOMPtr<nsIFile> profLD;
nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
getter_AddRefs(profLD));
if (NS_FAILED(rv)) {
return;
}
rv = profLD->Append(NS_LITERAL_STRING("ShutdownDuration.json"));
if (NS_FAILED(rv)) {
return;
}
nsAutoString path;
rv = profLD->GetPath(path);
if (NS_FAILED(rv)) {
return;
}
gWriteReady = PR_NewMonitor();
MOZ_LSAN_INTENTIONALLY_LEAK_OBJECT(gWriteReady); // We will never deallocate this object
PRThread* writerThread = CreateSystemThread(RunWriter,
ToNewUTF8String(path));
if (!writerThread) {
return;
}
}
NS_IMETHODIMP
@ -185,19 +439,101 @@ nsTerminator::Observe(nsISupports *, const char *aTopic, const char16_t *)
Start();
}
// Inform the thread that we have advanced by one phase.
gProgress.exchange(true);
#if defined(MOZ_CRASHREPORTER)
// In case of crash, we wish to know where in shutdown we are
unused << CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ShutdownProgress"),
nsAutoCString(aTopic));
#endif // defined(MOZ_CRASH_REPORTER)
UpdateHeartbeat(aTopic);
UpdateTelemetry();
UpdateCrashReport(aTopic);
// Perform a little cleanup
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
MOZ_RELEASE_ASSERT(os);
(void)os->RemoveObserver(this, aTopic);
return NS_OK;
}
void
nsTerminator::UpdateHeartbeat(const char* aTopic)
{
// Reset the clock, find out how long the current phase has lasted.
uint32_t ticks = gHeartbeat.exchange(0);
if (mCurrentStep > 0) {
sShutdownSteps[mCurrentStep].mTicks = ticks;
}
// Find out where we now are in the current shutdown.
// Don't assume that shutdown takes place in the expected order.
int nextStep = -1;
for (size_t i = 0; i < ArrayLength(sShutdownSteps); ++i) {
if (strcmp(sShutdownSteps[i].mTopic, aTopic) == 0) {
nextStep = i;
break;
}
}
MOZ_ASSERT(nextStep != -1);
mCurrentStep = nextStep;
}
void
nsTerminator::UpdateTelemetry()
{
if (!Telemetry::CanRecord() || !gWriteReady) {
return;
}
//
// We need Telemetry data on the effective duration of each step,
// to be able to tune the time-to-crash of each of both the
// Terminator and AsyncShutdown. However, at this stage, it is too
// late to record such data into Telemetry, so we write it to disk
// and read it upon the next startup.
//
// Build JSON.
UniquePtr<nsCString> telemetryData(new nsCString());
telemetryData->AppendLiteral("{");
size_t fields = 0;
for (size_t i = 0; i < ArrayLength(sShutdownSteps); ++i) {
if (sShutdownSteps[i].mTicks < 0) {
// Ignore this field.
continue;
}
if (fields++ > 0) {
telemetryData->Append(", ");
}
telemetryData->AppendLiteral("\"");
telemetryData->Append(sShutdownSteps[i].mTopic);
telemetryData->AppendLiteral("\": ");
telemetryData->AppendInt(sShutdownSteps[i].mTicks);
}
telemetryData->AppendLiteral("}");
if (fields == 0) {
// Nothing to write
return;
}
//
// Send data to the worker thread.
//
delete gWriteData.exchange(telemetryData.release()); // Clear any data that hasn't been written yet
// In case the worker thread was sleeping, wake it up.
PR_EnterMonitor(gWriteReady);
PR_Notify(gWriteReady);
PR_ExitMonitor(gWriteReady);
}
void
nsTerminator::UpdateCrashReport(const char* aTopic)
{
#if defined(MOZ_CRASHREPORTER)
// In case of crash, we wish to know where in shutdown we are
nsAutoCString report(aTopic);
unused << CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ShutdownProgress"),
report);
#endif // defined(MOZ_CRASH_REPORTER)
}
} // namespace mozilla

View File

@ -22,10 +22,17 @@ public:
private:
nsresult SelfInit();
void Start();
void StartWatchdog();
void StartWriter();
void UpdateHeartbeat(const char* aTopic);
void UpdateTelemetry();
void UpdateCrashReport(const char* aTopic);
~nsTerminator() {}
bool mInitialized;
int32_t mCurrentStep;
};
}

View File

@ -0,0 +1,105 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
/* 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";
/**
* Read the data saved by nsTerminator during shutdown and feed it to the
* relevant telemetry histograms.
*/
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
const Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "OS",
"resource://gre/modules/osfile.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/Promise.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "setTimeout",
"resource://gre/modules/Timer.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
function nsTerminatorTelemetry() {}
let HISTOGRAMS = {
"quit-application": "SHUTDOWN_PHASE_DURATION_TICKS_QUIT_APPLICATION",
"profile-change-teardown": "SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_CHANGE_TEARDOWN",
"profile-before-change": "SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_BEFORE_CHANGE",
"xpcom-will-shutdown": "SHUTDOWN_PHASE_DURATION_TICKS_XPCOM_WILL_SHUTDOWN",
};
nsTerminatorTelemetry.prototype = {
classID: Components.ID("{3f78ada1-cba2-442a-82dd-d5fb300ddea7}"),
_xpcom_factory: XPCOMUtils.generateSingletonFactory(nsTerminatorTelemetry),
//////////////////////////////////////////////////////////////////////////////
//// nsISupports
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
//////////////////////////////////////////////////////////////////////////////
//// nsIObserver
observe: function DS_observe(aSubject, aTopic, aData)
{
Task.spawn(function*() {
//
// This data is hardly critical, reading it can wait for a few seconds.
//
yield new Promise(resolve => setTimeout(resolve, 3000));
let PATH = OS.Path.join(OS.Constants.Path.localProfileDir,
"ShutdownDuration.json");
let raw;
try {
raw = yield OS.File.read(PATH, { encoding: "utf-8" });
} catch (ex if ex.becauseNoSuchFile) {
return;
}
// Let other errors be reported by Promise's error-reporting.
// Clean up
OS.File.remove(PATH);
OS.File.remove(PATH + ".tmp");
let data = JSON.parse(raw);
for (let k of Object.keys(data)) {
let id = HISTOGRAMS[k];
try {
let histogram = Services.telemetry.getHistogramById(id);
if (!histogram) {
throw new Error("Unknown histogram " + id);
}
histogram.add(Number.parseInt(data[k]));
} catch (ex) {
// Make sure that the error is reported and causes test failures,
// but otherwise, ignore it.
Promise.reject(ex);
continue;
}
}
// Inform observers that we are done.
Services.obs.notifyObservers(null,
"shutdown-terminator-telemetry-updated",
"");
});
},
};
////////////////////////////////////////////////////////////////////////////////
//// Module
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([nsTerminatorTelemetry]);

View File

@ -1,2 +1,5 @@
category profile-after-change nsTerminator @mozilla.org/toolkit/shutdown-terminator;1
component {3f78ada1-cba2-442a-82dd-d5fb300ddea7} nsTerminatorTelemetry.js
contract @mozilla.org/toolkit/shutdown-terminator-telemetry;1 {3f78ada1-cba2-442a-82dd-d5fb300ddea7}
category profile-after-change nsTerminatorTelemetry @mozilla.org/toolkit/shutdown-terminator-telemetry;1

View File

@ -0,0 +1,108 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the Shutdown Terminator records durations correctly
const Cu = Components.utils;
const Cc = Components.classes;
const Ci = Components.interfaces;
Cu.import("resource://gre/modules/Services.jsm", this);
Cu.import("resource://gre/modules/osfile.jsm", this);
Cu.import("resource://gre/modules/Timer.jsm", this);
Cu.import("resource://gre/modules/Task.jsm", this);
let {Path, File, Constants} = OS;
let PATH;
let PATH_TMP;
let terminator;
add_task(function* init() {
do_get_profile();
PATH = Path.join(Constants.Path.localProfileDir, "ShutdownDuration.json");
PATH_TMP = PATH + ".tmp";
// Initialize the terminator
// (normally, this is done through the manifest file, but xpcshell
// doesn't take them into account).
do_print("Initializing the Terminator");
terminator = Cc["@mozilla.org/toolkit/shutdown-terminator;1"].
createInstance(Ci.nsIObserver);
terminator.observe(null, "profile-after-change", null);
});
let promiseShutdownDurationData = Task.async(function*() {
// Wait until PATH exists.
// Timeout if it is never created.
do_print("Waiting for file creation: " + PATH);
while (true) {
if ((yield OS.File.exists(PATH))) {
break;
}
do_print("The file does not exist yet. Waiting 1 second.");
yield new Promise(resolve => setTimeout(resolve, 1000));
}
do_print("The file has been created");
let raw = yield OS.File.read(PATH, { encoding: "utf-8"} );
do_print(raw);
return JSON.parse(raw);
});
add_task(function* test_record() {
let PHASE0 = "profile-change-teardown";
let PHASE1 = "profile-before-change";
let PHASE2 = "xpcom-will-shutdown";
let t0 = Date.now();
do_print("Starting shutdown");
terminator.observe(null, "profile-change-teardown", null);
do_print("Moving to next phase");
terminator.observe(null, PHASE1, null);
let data = yield promiseShutdownDurationData();
let t1 = Date.now();
Assert.ok(PHASE0 in data, "The file contains the expected key");
let duration = data[PHASE0];
Assert.equal(typeof duration, "number");
Assert.ok(duration >= 0, "Duration is a non-negative number");
Assert.ok(duration <= Math.ceil((t1 - t0) / 1000) + 1,
"Duration is reasonable");
Assert.equal(Object.keys(data).length, 1, "Data does not contain other durations");
do_print("Cleaning up and moving to next phase");
yield File.remove(PATH);
yield File.remove(PATH_TMP);
do_print("Waiting at least one tick");
let WAIT_MS = 2000;
yield new Promise(resolve => setTimeout(resolve, WAIT_MS));
terminator.observe(null, PHASE2, null);
data = yield promiseShutdownDurationData();
let t2 = Date.now();
Assert.equal(Object.keys(data).sort().join(", "),
[PHASE0, PHASE1].sort().join(", "),
"The file contains the expected keys");
Assert.equal(data[PHASE0], duration, "Duration of phase 0 hasn't changed");
let duration2 = data[PHASE1];
Assert.equal(typeof duration2, "number");
Assert.ok(duration2 >= WAIT_MS / 2000, "We have waited at least " + (WAIT_MS / 2000) + " ticks");
Assert.ok(duration2 <= Math.ceil((t2 - t1) / 1000) + 1,
"Duration is reasonable");
});
function run_test() {
run_next_test();
}

View File

@ -0,0 +1,85 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the Shutdown Terminator reloads durations correctly
const Cu = Components.utils;
const Cc = Components.classes;
const Ci = Components.interfaces;
Cu.import("resource://gre/modules/Services.jsm", this);
Cu.import("resource://gre/modules/osfile.jsm", this);
Cu.import("resource://gre/modules/Timer.jsm", this);
Cu.import("resource://gre/modules/Task.jsm", this);
let {Path, File, Constants} = OS;
let PATH;
let HISTOGRAMS = {
"quit-application": "SHUTDOWN_PHASE_DURATION_TICKS_QUIT_APPLICATION",
"profile-change-teardown": "SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_CHANGE_TEARDOWN",
"profile-before-change": "SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_BEFORE_CHANGE",
"xpcom-will-shutdown": "SHUTDOWN_PHASE_DURATION_TICKS_XPCOM_WILL_SHUTDOWN",
};
add_task(function* init() {
do_get_profile();
PATH = Path.join(Constants.Path.localProfileDir, "ShutdownDuration.json");
});
add_task(function* test_reload() {
do_print("Forging data");
let data = {};
let telemetrySnapshots = Services.telemetry.histogramSnapshots;
let i = 0;
for (let k of Object.keys(HISTOGRAMS)) {
let id = HISTOGRAMS[k];
data[k] = i++;
Assert.equal(telemetrySnapshots[id] || undefined, undefined, "Histogram " + id + " is empty");
}
yield OS.File.writeAtomic(PATH, JSON.stringify(data));
const TOPIC = "shutdown-terminator-telemetry-updated";
let wait = new Promise(resolve =>
Services.obs.addObserver(
function observer() {
do_print("Telemetry has been updated");
Services.obs.removeObserver(observer, TOPIC);
resolve();
},
TOPIC,
false));
do_print("Starting nsTerminatorTelemetry");
let tt = Cc["@mozilla.org/toolkit/shutdown-terminator-telemetry;1"].
createInstance(Ci.nsIObserver);
tt.observe(null, "profile-after-change", "");
do_print("Waiting until telemetry is updated");
// Now wait until Telemetry is updated
yield wait;
telemetrySnapshots = Services.telemetry.histogramSnapshots;
for (let k of Object.keys(HISTOGRAMS)) {
let id = HISTOGRAMS[k];
do_print("Testing histogram " + id);
let snapshot = telemetrySnapshots[id];
let count = 0;
for (let x of snapshot.counts) {
count += x;
}
Assert.equal(count, 1, "We have added one item");
}
});
function run_test() {
run_next_test();
}

View File

@ -0,0 +1,7 @@
[DEFAULT]
head=
tail=
[test_terminator_record.js]
[test_terminator_reload.js]
skip-if = (os == "android" || appname == "b2g")

View File

@ -21,6 +21,7 @@ function setup_crash() {
// Inform the terminator that shutdown has started
// Pick an arbitrary notification
terminator.observe(null, "xpcom-will-shutdown", null);
terminator.observe(null, "profile-before-change", null);
dump("Waiting (actively) for the crash\n");
while(true) {
@ -30,7 +31,8 @@ function setup_crash() {
function after_crash(mdump, extra) {
Assert.equal(extra.ShutdownProgress, "xpcom-will-shutdown");
do_print("Crash signature: " + JSON.stringify(extra, null, "\t"));
Assert.equal(extra.ShutdownProgress, "profile-before-change");
}
function run_test() {