зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to fx-team
This commit is contained in:
Коммит
9c58c649d9
|
@ -15,15 +15,15 @@
|
|||
<remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
|
||||
<default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
|
||||
<!-- Gonk specific things and forks -->
|
||||
<project name="platform_build" path="build" remote="b2g" revision="1ad48c4be51b279f7f63c1a13025b52fe087d231">
|
||||
<project name="platform_build" path="build" remote="b2g" revision="2a165bebfa19b11b697837409f9550dd2917c46c">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="84f4835833c4cb8ac7e5d9e0c94738b1cb0ef45a"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f0463704888881b8ed1619e8d4b0d851b0e0311b"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="55bcc2d7e44dc805c24b57d1e783fc26e8a2ee86"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="99a67a75855d8ca077018c819aedd90bf0447d9b"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="52720b94a4f709d5f96c23b2d63c398e7002ecb9"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
|
||||
<!-- Stock Android things -->
|
||||
|
@ -98,7 +98,7 @@
|
|||
<project name="platform/system/vold" path="system/vold" revision="919829940468066a32f403980b43f6ebfee5d314"/>
|
||||
<!-- Emulator specific things -->
|
||||
<project name="android-development" path="development" remote="b2g" revision="9abf0ab68376afae3e1c7beefa3e9cbee2fde202"/>
|
||||
<project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="aab7a70124d88092831b99f3619a6572dca05b8f"/>
|
||||
<project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="2fee3bbbfc236b883ef8507e27d88b17b203fe25"/>
|
||||
<project name="platform/external/iproute2" path="external/iproute2" revision="c66c5716d5335e450f7a7b71ccc6a604fb2f41d2"/>
|
||||
<project name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="d2685281e2e54ca14d1df304867aa82c37b27162"/>
|
||||
<project name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="627f9b20fc518937b93747a7ff1ed4f5ed46e06f"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</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="84f4835833c4cb8ac7e5d9e0c94738b1cb0ef45a"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f0463704888881b8ed1619e8d4b0d851b0e0311b"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="52c909ccead537f8f9dbf634f3e6639078a8b0bd">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="84f4835833c4cb8ac7e5d9e0c94738b1cb0ef45a"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f0463704888881b8ed1619e8d4b0d851b0e0311b"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
|
|
|
@ -15,15 +15,15 @@
|
|||
<remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
|
||||
<default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
|
||||
<!-- Gonk specific things and forks -->
|
||||
<project name="platform_build" path="build" remote="b2g" revision="1ad48c4be51b279f7f63c1a13025b52fe087d231">
|
||||
<project name="platform_build" path="build" remote="b2g" revision="2a165bebfa19b11b697837409f9550dd2917c46c">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="84f4835833c4cb8ac7e5d9e0c94738b1cb0ef45a"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f0463704888881b8ed1619e8d4b0d851b0e0311b"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="55bcc2d7e44dc805c24b57d1e783fc26e8a2ee86"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="99a67a75855d8ca077018c819aedd90bf0447d9b"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="52720b94a4f709d5f96c23b2d63c398e7002ecb9"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
|
||||
<!-- Stock Android things -->
|
||||
|
@ -98,7 +98,7 @@
|
|||
<project name="platform/system/vold" path="system/vold" revision="919829940468066a32f403980b43f6ebfee5d314"/>
|
||||
<!-- Emulator specific things -->
|
||||
<project name="android-development" path="development" remote="b2g" revision="9abf0ab68376afae3e1c7beefa3e9cbee2fde202"/>
|
||||
<project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="aab7a70124d88092831b99f3619a6572dca05b8f"/>
|
||||
<project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="2fee3bbbfc236b883ef8507e27d88b17b203fe25"/>
|
||||
<project name="platform/external/iproute2" path="external/iproute2" revision="c66c5716d5335e450f7a7b71ccc6a604fb2f41d2"/>
|
||||
<project name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="d2685281e2e54ca14d1df304867aa82c37b27162"/>
|
||||
<project name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="627f9b20fc518937b93747a7ff1ed4f5ed46e06f"/>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="84f4835833c4cb8ac7e5d9e0c94738b1cb0ef45a"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f0463704888881b8ed1619e8d4b0d851b0e0311b"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
|
||||
|
@ -119,7 +119,7 @@
|
|||
<!-- Flame specific things -->
|
||||
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="e8a318f7690092e639ba88891606f4183e846d3f"/>
|
||||
<project name="device/qcom/common" path="device/qcom/common" revision="234ed34543345f58c0d4dcb1aa012de68802b9dc"/>
|
||||
<project name="device-flame" path="device/t2m/flame" remote="b2g" revision="92f9b79e3a5ecf24cb0f66e20d5292b300f8cac9"/>
|
||||
<project name="device-flame" path="device/t2m/flame" remote="b2g" revision="08132b26f24d3c2ec9c24f63b58bd99158e9400c"/>
|
||||
<project name="kernel/msm" path="kernel" revision="b3092c54430df89636fb0670d32058bc63474017"/>
|
||||
<project name="platform/bootable/recovery" path="bootable/recovery" revision="f2914eacee9120680a41463708bb6ee8291749fc"/>
|
||||
<project name="platform/external/bluetooth/bluedroid" path="external/bluetooth/bluedroid" revision="fa892235a9bd8983f8b591129fc1a9398f64e514"/>
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
"remote": "",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "46f6f56839237440d17026f21255dac5329c681c",
|
||||
"revision": "3f54f719dc145f1544db67881a48819559ecb71b",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
<remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
|
||||
<default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
|
||||
<!-- Gonk specific things and forks -->
|
||||
<project name="platform_build" path="build" remote="b2g" revision="1ad48c4be51b279f7f63c1a13025b52fe087d231">
|
||||
<project name="platform_build" path="build" remote="b2g" revision="2a165bebfa19b11b697837409f9550dd2917c46c">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="84f4835833c4cb8ac7e5d9e0c94738b1cb0ef45a"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f0463704888881b8ed1619e8d4b0d851b0e0311b"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
|
|
@ -11,11 +11,11 @@
|
|||
<remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
|
||||
<default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
|
||||
<!-- Gonk specific things and forks -->
|
||||
<project name="platform_build" path="build" remote="b2g" revision="1ad48c4be51b279f7f63c1a13025b52fe087d231">
|
||||
<project name="platform_build" path="build" remote="b2g" revision="2a165bebfa19b11b697837409f9550dd2917c46c">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="84f4835833c4cb8ac7e5d9e0c94738b1cb0ef45a"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f0463704888881b8ed1619e8d4b0d851b0e0311b"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
|
|
@ -15,11 +15,11 @@
|
|||
<remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
|
||||
<default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
|
||||
<!-- Gonk specific things and forks -->
|
||||
<project name="platform_build" path="build" remote="b2g" revision="1ad48c4be51b279f7f63c1a13025b52fe087d231">
|
||||
<project name="platform_build" path="build" remote="b2g" revision="2a165bebfa19b11b697837409f9550dd2917c46c">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="84f4835833c4cb8ac7e5d9e0c94738b1cb0ef45a"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f0463704888881b8ed1619e8d4b0d851b0e0311b"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
<remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
|
||||
<default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
|
||||
<!-- Gonk specific things and forks -->
|
||||
<project name="platform_build" path="build" remote="b2g" revision="1ad48c4be51b279f7f63c1a13025b52fe087d231">
|
||||
<project name="platform_build" path="build" remote="b2g" revision="2a165bebfa19b11b697837409f9550dd2917c46c">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="84f4835833c4cb8ac7e5d9e0c94738b1cb0ef45a"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f0463704888881b8ed1619e8d4b0d851b0e0311b"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</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="84f4835833c4cb8ac7e5d9e0c94738b1cb0ef45a"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f0463704888881b8ed1619e8d4b0d851b0e0311b"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
<remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
|
||||
<default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
|
||||
<!-- Gonk specific things and forks -->
|
||||
<project name="platform_build" path="build" remote="b2g" revision="1ad48c4be51b279f7f63c1a13025b52fe087d231">
|
||||
<project name="platform_build" path="build" remote="b2g" revision="2a165bebfa19b11b697837409f9550dd2917c46c">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="84f4835833c4cb8ac7e5d9e0c94738b1cb0ef45a"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f0463704888881b8ed1619e8d4b0d851b0e0311b"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
|
|
@ -944,8 +944,9 @@ WebGLContext::ValidateTexImageSize(GLenum target, GLint level,
|
|||
|
||||
const GLuint maxTexImageSize = MaxTextureSizeForTarget(target) >> level;
|
||||
const bool isCubemapTarget = IsTexImageCubemapTarget(target);
|
||||
const bool isSub = IsSubFunc(func);
|
||||
|
||||
if (isCubemapTarget && width != height) {
|
||||
if (!isSub && isCubemapTarget && (width != height)) {
|
||||
/* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification
|
||||
* "When the target parameter to TexImage2D is one of the
|
||||
* six cube map two-dimensional image targets, the error
|
||||
|
|
|
@ -72,7 +72,7 @@ SVGTransformableElement::GetAttributeChangeHint(const nsIAtom* aAttribute,
|
|||
NS_ABORT_IF_FALSE(aModType == nsIDOMMutationEvent::MODIFICATION,
|
||||
"Unknown modification type.");
|
||||
// We just assume the old and new transforms are different.
|
||||
NS_UpdateHint(retval, NS_CombineHint(nsChangeHint_UpdateOverflow,
|
||||
NS_UpdateHint(retval, NS_CombineHint(nsChangeHint_UpdatePostTransformOverflow,
|
||||
nsChangeHint_UpdateTransformLayer));
|
||||
}
|
||||
}
|
||||
|
@ -135,8 +135,19 @@ SVGTransformableElement::SetAnimateMotionTransform(const gfx::Matrix* aMatrix)
|
|||
(aMatrix && mAnimateMotionTransform && *aMatrix == *mAnimateMotionTransform)) {
|
||||
return;
|
||||
}
|
||||
bool transformSet = mTransforms && mTransforms->IsExplicitlySet();
|
||||
bool prevSet = mAnimateMotionTransform || transformSet;
|
||||
mAnimateMotionTransform = aMatrix ? new gfx::Matrix(*aMatrix) : nullptr;
|
||||
DidAnimateTransformList();
|
||||
bool nowSet = mAnimateMotionTransform || transformSet;
|
||||
int32_t modType;
|
||||
if (prevSet && !nowSet) {
|
||||
modType = nsIDOMMutationEvent::REMOVAL;
|
||||
} else if(!prevSet && nowSet) {
|
||||
modType = nsIDOMMutationEvent::ADDITION;
|
||||
} else {
|
||||
modType = nsIDOMMutationEvent::MODIFICATION;
|
||||
}
|
||||
DidAnimateTransformList(modType);
|
||||
nsIFrame* frame = GetPrimaryFrame();
|
||||
if (frame) {
|
||||
// If the result of this transform and any other transforms on this frame
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "nsSMILValue.h"
|
||||
#include "SVGContentUtils.h"
|
||||
#include "SVGTransformListSMILType.h"
|
||||
#include "nsIDOMMutationEvent.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
@ -75,6 +76,7 @@ nsresult
|
|||
nsSVGAnimatedTransformList::SetAnimValue(const SVGTransformList& aValue,
|
||||
nsSVGElement *aElement)
|
||||
{
|
||||
bool prevSet = HasTransform() || aElement->GetAnimateMotionTransform();
|
||||
SVGAnimatedTransformList *domWrapper =
|
||||
SVGAnimatedTransformList::GetDOMWrapperIfExists(this);
|
||||
if (domWrapper) {
|
||||
|
@ -106,7 +108,13 @@ nsSVGAnimatedTransformList::SetAnimValue(const SVGTransformList& aValue,
|
|||
ClearAnimValue(aElement);
|
||||
return rv;
|
||||
}
|
||||
aElement->DidAnimateTransformList();
|
||||
int32_t modType;
|
||||
if(prevSet) {
|
||||
modType = nsIDOMMutationEvent::MODIFICATION;
|
||||
} else {
|
||||
modType = nsIDOMMutationEvent::ADDITION;
|
||||
}
|
||||
aElement->DidAnimateTransformList(modType);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -124,7 +132,13 @@ nsSVGAnimatedTransformList::ClearAnimValue(nsSVGElement *aElement)
|
|||
domWrapper->InternalAnimValListWillChangeLengthTo(mBaseVal.Length());
|
||||
}
|
||||
mAnimVal = nullptr;
|
||||
aElement->DidAnimateTransformList();
|
||||
int32_t modType;
|
||||
if (HasTransform() || aElement->GetAnimateMotionTransform()) {
|
||||
modType = nsIDOMMutationEvent::MODIFICATION;
|
||||
} else {
|
||||
modType = nsIDOMMutationEvent::REMOVAL;
|
||||
}
|
||||
aElement->DidAnimateTransformList(modType);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -2356,7 +2356,7 @@ nsSVGElement::DidChangeTransformList(const nsAttrValue& aEmptyOrOldValue)
|
|||
}
|
||||
|
||||
void
|
||||
nsSVGElement::DidAnimateTransformList()
|
||||
nsSVGElement::DidAnimateTransformList(int32_t aModType)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(GetTransformListAttrName(),
|
||||
"Animating non-existent transform data?");
|
||||
|
@ -2365,10 +2365,9 @@ nsSVGElement::DidAnimateTransformList()
|
|||
|
||||
if (frame) {
|
||||
nsIAtom *transformAttr = GetTransformListAttrName();
|
||||
int32_t modType = nsIDOMMutationEvent::MODIFICATION;
|
||||
frame->AttributeChanged(kNameSpaceID_None,
|
||||
transformAttr,
|
||||
modType);
|
||||
aModType);
|
||||
// When script changes the 'transform' attribute, Element::SetAttrAndNotify
|
||||
// will call nsNodeUtills::AttributeChanged, under which
|
||||
// SVGTransformableElement::GetAttributeChangeHint will be called and an
|
||||
|
@ -2377,7 +2376,7 @@ nsSVGElement::DidAnimateTransformList()
|
|||
// 'animateTransform' though (and sending out the mutation events that
|
||||
// nsNodeUtills::AttributeChanged dispatches would be inappropriate
|
||||
// anyway), so we need to post the change event ourself.
|
||||
nsChangeHint changeHint = GetAttributeChangeHint(transformAttr, modType);
|
||||
nsChangeHint changeHint = GetAttributeChangeHint(transformAttr, aModType);
|
||||
if (changeHint) {
|
||||
nsLayoutUtils::PostRestyleEvent(this, nsRestyleHint(0), changeHint);
|
||||
}
|
||||
|
|
|
@ -239,7 +239,7 @@ public:
|
|||
void DidAnimateLengthList(uint8_t aAttrEnum);
|
||||
void DidAnimatePointList();
|
||||
void DidAnimatePathSegList();
|
||||
void DidAnimateTransformList();
|
||||
void DidAnimateTransformList(int32_t aModType);
|
||||
void DidAnimateString(uint8_t aAttrEnum);
|
||||
|
||||
enum {
|
||||
|
|
|
@ -33,20 +33,23 @@
|
|||
"iframe with mozFrameType='content' in chrome document is typeContent");
|
||||
|
||||
SimpleTest.executeSoon(function () {
|
||||
// Wait for the window to be closed before finishing the test
|
||||
let ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
|
||||
.getService(Components.interfaces.nsIWindowWatcher);
|
||||
ww.registerNotification(function windowObs(subject, topic, data) {
|
||||
if (topic == "domwindowclosed") {
|
||||
ww.unregisterNotification(windowObs);
|
||||
// First focus the parent window and then close this one.
|
||||
SimpleTest.waitForFocus(function() {
|
||||
let ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
|
||||
.getService(Components.interfaces.nsIWindowWatcher);
|
||||
ww.registerNotification(function windowObs(subject, topic, data) {
|
||||
if (topic == "domwindowclosed") {
|
||||
ww.unregisterNotification(windowObs);
|
||||
|
||||
SimpleTest.waitForFocus(function() {
|
||||
SimpleTest.finish();
|
||||
}, opener);
|
||||
}
|
||||
});
|
||||
// Don't start the next test synchronously!
|
||||
SimpleTest.executeSoon(function() {
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
window.close();
|
||||
window.close();
|
||||
}, opener);
|
||||
});
|
||||
}
|
||||
]]></script>
|
||||
|
|
|
@ -353,6 +353,9 @@ parent:
|
|||
|
||||
ReplyKeyEvent(WidgetKeyboardEvent event);
|
||||
|
||||
sync RequestNativeKeyBindings(WidgetKeyboardEvent event)
|
||||
returns (MaybeNativeKeyBinding bindings);
|
||||
|
||||
child:
|
||||
/**
|
||||
* Notify the remote browser that it has been Show()n on this
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- */
|
||||
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8; -*- */
|
||||
/* vim: set sw=2 sts=2 ts=8 et tw=80 : */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
@ -2027,19 +2027,40 @@ TabChild::RecvRealTouchMoveEvent(const WidgetTouchEvent& aEvent,
|
|||
return RecvRealTouchEvent(aEvent, aGuid);
|
||||
}
|
||||
|
||||
void
|
||||
TabChild::RequestNativeKeyBindings(AutoCacheNativeKeyCommands* aAutoCache,
|
||||
WidgetKeyboardEvent* aEvent)
|
||||
{
|
||||
MaybeNativeKeyBinding maybeBindings;
|
||||
if (!SendRequestNativeKeyBindings(*aEvent, &maybeBindings)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (maybeBindings.type() == MaybeNativeKeyBinding::TNativeKeyBinding) {
|
||||
const NativeKeyBinding& bindings = maybeBindings;
|
||||
aAutoCache->Cache(bindings.singleLineCommands(),
|
||||
bindings.multiLineCommands(),
|
||||
bindings.richTextCommands());
|
||||
} else {
|
||||
aAutoCache->CacheNoCommands();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
TabChild::RecvRealKeyEvent(const WidgetKeyboardEvent& event,
|
||||
const MaybeNativeKeyBinding& aBindings)
|
||||
{
|
||||
{
|
||||
PuppetWidget* widget = static_cast<PuppetWidget*>(mWidget.get());
|
||||
AutoCacheNativeKeyCommands autoCache(widget);
|
||||
|
||||
if (event.message == NS_KEY_PRESS) {
|
||||
PuppetWidget* widget = static_cast<PuppetWidget*>(mWidget.get());
|
||||
if (aBindings.type() == MaybeNativeKeyBinding::TNativeKeyBinding) {
|
||||
const NativeKeyBinding& bindings = aBindings;
|
||||
widget->CacheNativeKeyCommands(bindings.singleLineCommands(),
|
||||
bindings.multiLineCommands(),
|
||||
bindings.richTextCommands());
|
||||
autoCache.Cache(bindings.singleLineCommands(),
|
||||
bindings.multiLineCommands(),
|
||||
bindings.richTextCommands());
|
||||
} else {
|
||||
widget->ClearNativeKeyCommands();
|
||||
autoCache.CacheNoCommands();
|
||||
}
|
||||
}
|
||||
// If content code called preventDefault() on a keydown event, then we don't
|
||||
|
|
|
@ -46,6 +46,10 @@ namespace layers {
|
|||
class ActiveElementManager;
|
||||
}
|
||||
|
||||
namespace widget {
|
||||
struct AutoCacheNativeKeyCommands;
|
||||
}
|
||||
|
||||
namespace dom {
|
||||
|
||||
class TabChild;
|
||||
|
@ -393,6 +397,8 @@ public:
|
|||
|
||||
void NotifyPainted();
|
||||
|
||||
void RequestNativeKeyBindings(mozilla::widget::AutoCacheNativeKeyCommands* aAutoCache,
|
||||
WidgetKeyboardEvent* aEvent);
|
||||
|
||||
/** Return a boolean indicating if the page has called preventDefault on
|
||||
* the event.
|
||||
|
|
|
@ -791,6 +791,41 @@ DoCommandCallback(mozilla::Command aCommand, void* aData)
|
|||
static_cast<InfallibleTArray<mozilla::CommandInt>*>(aData)->AppendElement(aCommand);
|
||||
}
|
||||
|
||||
bool
|
||||
TabParent::RecvRequestNativeKeyBindings(const WidgetKeyboardEvent& aEvent,
|
||||
MaybeNativeKeyBinding* aBindings)
|
||||
{
|
||||
AutoInfallibleTArray<mozilla::CommandInt, 4> singleLine;
|
||||
AutoInfallibleTArray<mozilla::CommandInt, 4> multiLine;
|
||||
AutoInfallibleTArray<mozilla::CommandInt, 4> richText;
|
||||
|
||||
*aBindings = mozilla::void_t();
|
||||
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget();
|
||||
if (!widget) {
|
||||
return true;
|
||||
}
|
||||
|
||||
WidgetKeyboardEvent localEvent(aEvent);
|
||||
|
||||
if (NS_FAILED(widget->AttachNativeKeyEvent(localEvent))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
widget->ExecuteNativeKeyBinding(nsIWidget::NativeKeyBindingsForSingleLineEditor,
|
||||
localEvent, DoCommandCallback, &singleLine);
|
||||
widget->ExecuteNativeKeyBinding(nsIWidget::NativeKeyBindingsForMultiLineEditor,
|
||||
localEvent, DoCommandCallback, &multiLine);
|
||||
widget->ExecuteNativeKeyBinding(nsIWidget::NativeKeyBindingsForRichTextEditor,
|
||||
localEvent, DoCommandCallback, &richText);
|
||||
|
||||
if (!singleLine.IsEmpty() || !multiLine.IsEmpty() || !richText.IsEmpty()) {
|
||||
*aBindings = NativeKeyBinding(singleLine, multiLine, richText);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TabParent::SendRealKeyEvent(WidgetKeyboardEvent& event)
|
||||
{
|
||||
if (mIsDestroyed) {
|
||||
|
|
|
@ -222,6 +222,9 @@ public:
|
|||
void MapEventCoordinatesForChildProcess(const LayoutDeviceIntPoint& aOffset,
|
||||
mozilla::WidgetEvent* aEvent);
|
||||
|
||||
virtual bool RecvRequestNativeKeyBindings(const mozilla::WidgetKeyboardEvent& aEvent,
|
||||
MaybeNativeKeyBinding* aBindings) MOZ_OVERRIDE;
|
||||
|
||||
void SendMouseEvent(const nsAString& aType, float aX, float aY,
|
||||
int32_t aButton, int32_t aClickCount,
|
||||
int32_t aModifiers, bool aIgnoreRootScrollFrame);
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
Mozilla Graphics Overview {#graphicsoverview}
|
||||
=================
|
||||
## Work in progress. Possibly incorrect or incomplete.
|
||||
|
||||
Overview
|
||||
--------
|
||||
The graphics systems is responsible for rendering (painting, drawing) the frame tree (rendering tree) elements as created by the layout system. Each leaf in the tree has content, either bounded by a rectangle (or perhaps another shape, in the case of SVG.)
|
||||
|
||||
The simple approach for producing the result would thus involve traversing the frame tree, in a correct order, drawing each frame into the resulting buffer and displaying (printing non-withstanding) that buffer when the traversal is done. It is worth spending some time on the "correct order" note above. If there are no overlapping frames, this is fairly simple - any order will do, as long as there is no background. If there is background, we just have to worry about drawing that first. Since we do not control the content, chances are the page is more complicated. There are overlapping frames, likely with transparency, so we need to make sure the elements are draw "back to front", in layers, so to speak. Layers are an important concept, and we will revisit them shortly, as they are central to fixing a major issue with the above simple approach.
|
||||
|
||||
While the above simple approach will work, the performance will suffer. Each time anything changes in any of the frames, the complete process needs to be repeated, everything needs to be redrawn. Further, there is very little space to take advantage of the modern graphics (GPU) hardware, or multi-core computers. If you recall from the previous sections, the frame tree is only accessible from the UI thread, so while we're doing all this work, the UI is basically blocked.
|
||||
|
||||
### (Retained) Layers
|
||||
|
||||
Layers framework was introduced to address the above performance issues, by having a part of the design address each item. At the high level:
|
||||
|
||||
1. We create a layer tree. The leaf elements of the tree contain all frames (possibly multiple frames per leaf).
|
||||
2. We render each layer tree element and cache (retain) the result.
|
||||
3. We composite (combine) all the leaf elements into the final result.
|
||||
|
||||
Let's examine each of these steps, in reverse order.
|
||||
|
||||
### Compositing
|
||||
We use the term composite as it implies that the order is important. If the elements being composited overlap, whether there is transparency involved or not, the order in which they are combined will effect the result.
|
||||
Compositing is where we can use some of the power of the modern graphics hardware. It is optimal for doing this job. In the scenarios where only the position of individual frames changes, without the content inside them changing, we see why caching each layer would be advantageous - we only need to repeat the final compositing step, completely skipping the layer tree creation and the rendering of each leaf, thus speeding up the process considerably.
|
||||
|
||||
Another benefit is equally apparent in the context of the stated deficiencies of the simple approach. We can use the available graphics hardware accelerated APIs to do the compositing step. Direct3D, OpenGL can be used on different platforms and are well suited to accelerate this step.
|
||||
|
||||
Finally, we can now envision performing the compositing step on a separate thread, unblocking the UI thread for other work, and doing more work in parallel. More on this below.
|
||||
|
||||
It is important to note that the number of operations in this step is proportional to the number of layer tree (leaf) elements, so there is additional work and complexity involved, when the layer tree is large.
|
||||
|
||||
#### Render and retain layer elements
|
||||
As we saw, the compositing step benefits from caching the intermediate result. This does result in the extra memory usage, so needs to be considered during the layer tree creation. Beyond the caching, we can accelerate the rendering of each element by (indirectly) using the available platform APIs (e.g., Direct2D, CoreGraphics, even some of the 3D APIs like OpenGL or Direct3D) as available. This is actually done through a platform independent API (see Moz2D) below, but is important to realize it does get accelerated appropriately.
|
||||
|
||||
#### Creating the layer tree
|
||||
We need to create a layer tree (from the frames tree), which will give us the correct result while striking the right balance between a layer per frame element and a single layer for the complete frames tree. As was mentioned above, there is an overhead in traversing the whole tree and caching each of the elements, balanced by the performance improvements. Some of the performance improvements are only noticed when something changes (e.g., one element is moving, we only need to redo the compositing step).
|
||||
|
||||
### Refresh Driver
|
||||
|
||||
### Layers
|
||||
|
||||
#### Rendering each layer
|
||||
|
||||
### Tiling vs. Buffer Rotation vs. Full paint
|
||||
|
||||
#### Compositing for the final result
|
||||
|
||||
### Graphics API
|
||||
|
||||
#### Moz2D
|
||||
* The Moz2D graphics API, part of the Azure project, is a cross-platform interface onto the various graphics backends that Gecko uses for rendering such as Direct2D (1.0 and 1.1), Skia, Cairo, Quartz, and NV Path. Adding a new graphics platform to Gecko is accomplished by adding a backend to Moz2D.
|
||||
\see [Moz2D documentation on wiki](https://wiki.mozilla.org/Platform/GFX/Moz2D)
|
||||
|
||||
#### Compositing
|
||||
|
||||
#### Image Decoding
|
||||
|
||||
#### Image Animation
|
||||
|
||||
### Funny words
|
||||
There are a lot of code words that we use to refer to projects, libraries, areas of the code. Here's an attempt to cover some of those:
|
||||
* Azure - See Moz2D in the Graphics API section above.
|
||||
* Backend - See Moz2D in the Graphics API section above.
|
||||
* Cairo - http://www.cairographics.org/. Cairo is a 2D graphics library with support for multiple output devices. Currently supported output targets include the X Window System (via both Xlib and XCB), Quartz, Win32, image buffers, PostScript, PDF, and SVG file output.
|
||||
* Moz2D - See Moz2D in the Graphics API section above.
|
||||
* Thebes - Graphics API that preceded Moz2D.
|
||||
* Reflow
|
||||
* Display list
|
||||
|
||||
### [Historical Documents](http://www.youtube.com/watch?v=lLZQz26-kms)
|
||||
A number of posts and blogs that will give you more details or more background, or reasoning that led to different solutions and approaches.
|
||||
|
||||
* 2010-01 [Layers: Cross Platform Acceleration] (http://www.basschouten.com/blog1.php/layers-cross-platform-acceleration)
|
||||
* 2010-04 [Layers] (http://robert.ocallahan.org/2010/04/layers_01.html)
|
||||
* 2010-07 [Retained Layers](http://robert.ocallahan.org/2010/07/retained-layers_16.html)
|
||||
* 2011-04 [Introduction](https://blog.mozilla.org/joe/2011/04/26/introducing-the-azure-project/ Moz2D)
|
||||
* 2011-07 [Layers](http://chrislord.net/index.php/2011/07/25/shadow-layers-and-learning-by-failing/ Shadow)
|
||||
* 2011-09 [Graphics API Design](http://robert.ocallahan.org/2011/09/graphics-api-design.html)
|
||||
* 2012-04 [Moz2D Canvas on OSX](http://muizelaar.blogspot.ca/2012/04/azure-canvas-on-os-x.html)
|
||||
* 2012-05 [Mask Layers](http://featherweightmusings.blogspot.co.uk/2012/05/mask-layers_26.html)
|
||||
* 2013-07 [Graphics related](http://www.basschouten.com/blog1.php)
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
Mozilla Graphics {#mainpage}
|
||||
======================
|
||||
|
||||
## Work in progress. Possibly incorrect or incomplete.
|
||||
|
||||
|
||||
Introduction
|
||||
-------
|
||||
This collection of linked pages contains a combination of Doxygen
|
||||
extracted source code documentation and design documents for the
|
||||
Mozilla graphics architecture. The design documents live in gfx/docs directory.
|
||||
|
||||
This [wiki page](https://wiki.mozilla.org/Platform/GFX) contains
|
||||
information about graphics and the graphics team at MoCo.
|
||||
|
||||
Continue here for a [very high level introductory overview](@ref graphicsoverview)
|
||||
if you don't know where to start.
|
||||
|
||||
Useful pointers for creating documentation
|
||||
------
|
||||
[The mechanics of creating these files](https://wiki.mozilla.org/Platform/GFX/DesignDocumentationGuidelines)
|
|
@ -849,7 +849,10 @@ void imgFrame::ApplyDirtToSurfaces()
|
|||
void imgFrame::SetDiscardable()
|
||||
{
|
||||
MOZ_ASSERT(mLockCount, "Expected to be locked when SetDiscardable is called");
|
||||
// Disabled elsewhere due to the cost of calling GetSourceSurfaceForSurface.
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
mDiscardable = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
int32_t imgFrame::GetRawTimeout() const
|
||||
|
|
|
@ -472,9 +472,19 @@ nsresult nsExtensibleStringBundle::GetSimpleEnumeration(nsISimpleEnumerator ** a
|
|||
|
||||
#define MAX_CACHED_BUNDLES 16
|
||||
|
||||
struct bundleCacheEntry_t : public LinkedListElement<bundleCacheEntry_t> {
|
||||
struct bundleCacheEntry_t MOZ_FINAL : public LinkedListElement<bundleCacheEntry_t> {
|
||||
nsAutoPtr<nsCStringKey> mHashKey;
|
||||
nsCOMPtr<nsIStringBundle> mBundle;
|
||||
|
||||
bundleCacheEntry_t()
|
||||
{
|
||||
MOZ_COUNT_CTOR(bundleCacheEntry_t);
|
||||
}
|
||||
|
||||
~bundleCacheEntry_t()
|
||||
{
|
||||
MOZ_COUNT_DTOR(bundleCacheEntry_t);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -11,6 +11,13 @@
|
|||
|
||||
#include "assembler/assembler/MacroAssemblerX86Common.h"
|
||||
|
||||
#if WTF_COMPILER_MSVC
|
||||
#if WTF_CPU_X86_64
|
||||
/* for __cpuid */
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
using namespace JSC;
|
||||
MacroAssemblerX86Common::SSECheckState MacroAssemblerX86Common::s_sseCheckState = NotCheckedSSE;
|
||||
|
||||
|
@ -20,5 +27,112 @@ bool MacroAssemblerX86Common::s_SSE3Disabled = false;
|
|||
bool MacroAssemblerX86Common::s_SSE4Disabled = false;
|
||||
#endif
|
||||
|
||||
void MacroAssemblerX86Common::setSSECheckState()
|
||||
{
|
||||
// Default the flags value to zero; if the compiler is
|
||||
// not MSVC or GCC we will read this as SSE2 not present.
|
||||
int flags_edx = 0;
|
||||
int flags_ecx = 0;
|
||||
#if WTF_COMPILER_MSVC
|
||||
#if WTF_CPU_X86_64
|
||||
int cpuinfo[4];
|
||||
|
||||
__cpuid(cpuinfo, 1);
|
||||
flags_ecx = cpuinfo[2];
|
||||
flags_edx = cpuinfo[3];
|
||||
#else
|
||||
_asm {
|
||||
mov eax, 1 // cpuid function 1 gives us the standard feature set
|
||||
cpuid;
|
||||
mov flags_ecx, ecx;
|
||||
mov flags_edx, edx;
|
||||
}
|
||||
#endif
|
||||
#elif WTF_COMPILER_GCC
|
||||
#if WTF_CPU_X86_64
|
||||
asm (
|
||||
"movl $0x1, %%eax;"
|
||||
"cpuid;"
|
||||
: "=c" (flags_ecx), "=d" (flags_edx)
|
||||
:
|
||||
: "%eax", "%ebx"
|
||||
);
|
||||
#else
|
||||
// On 32-bit x86, we must preserve ebx; the compiler needs it for PIC mode.
|
||||
asm (
|
||||
"movl $0x1, %%eax;"
|
||||
"pushl %%ebx;"
|
||||
"cpuid;"
|
||||
"popl %%ebx;"
|
||||
: "=c" (flags_ecx), "=d" (flags_edx)
|
||||
:
|
||||
: "%eax"
|
||||
);
|
||||
#endif
|
||||
#elif WTF_COMPILER_SUNCC
|
||||
#if WTF_CPU_X86_64
|
||||
asm (
|
||||
"movl $0x1, %%eax;"
|
||||
"pushq %%rbx;"
|
||||
"cpuid;"
|
||||
"popq %%rbx;"
|
||||
"movl %%ecx, (%rsi);"
|
||||
"movl %%edx, (%rdi);"
|
||||
:
|
||||
: "S" (&flags_ecx), "D" (&flags_edx)
|
||||
: "%eax", "%ecx", "%edx"
|
||||
);
|
||||
#else
|
||||
asm (
|
||||
"movl $0x1, %eax;"
|
||||
"pushl %ebx;"
|
||||
"cpuid;"
|
||||
"popl %ebx;"
|
||||
"movl %ecx, (%esi);"
|
||||
"movl %edx, (%edi);"
|
||||
:
|
||||
: "S" (&flags_ecx), "D" (&flags_edx)
|
||||
: "%eax", "%ecx", "%edx"
|
||||
);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
if (s_floatingPointDisabled) {
|
||||
// Disable SSE2.
|
||||
s_sseCheckState = HasSSE;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const int SSEFeatureBit = 1 << 25;
|
||||
static const int SSE2FeatureBit = 1 << 26;
|
||||
static const int SSE3FeatureBit = 1 << 0;
|
||||
static const int SSSE3FeatureBit = 1 << 9;
|
||||
static const int SSE41FeatureBit = 1 << 19;
|
||||
static const int SSE42FeatureBit = 1 << 20;
|
||||
if (flags_ecx & SSE42FeatureBit)
|
||||
s_sseCheckState = HasSSE4_2;
|
||||
else if (flags_ecx & SSE41FeatureBit)
|
||||
s_sseCheckState = HasSSE4_1;
|
||||
else if (flags_ecx & SSSE3FeatureBit)
|
||||
s_sseCheckState = HasSSSE3;
|
||||
else if (flags_ecx & SSE3FeatureBit)
|
||||
s_sseCheckState = HasSSE3;
|
||||
else if (flags_edx & SSE2FeatureBit)
|
||||
s_sseCheckState = HasSSE2;
|
||||
else if (flags_edx & SSEFeatureBit)
|
||||
s_sseCheckState = HasSSE;
|
||||
else
|
||||
s_sseCheckState = NoSSE;
|
||||
|
||||
#ifdef DEBUG
|
||||
if (s_sseCheckState >= HasSSE4_1 && s_SSE4Disabled)
|
||||
s_sseCheckState = HasSSE3;
|
||||
if (s_sseCheckState >= HasSSE3 && s_SSE3Disabled)
|
||||
s_sseCheckState = HasSSE2;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* WTF_CPU_X86 || WTF_CPU_X86_64 */
|
||||
|
||||
|
|
|
@ -37,13 +37,6 @@
|
|||
#include "assembler/assembler/X86Assembler.h"
|
||||
#include "assembler/assembler/AbstractMacroAssembler.h"
|
||||
|
||||
#if WTF_COMPILER_MSVC
|
||||
#if WTF_CPU_X86_64
|
||||
/* for __cpuid */
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace JSC {
|
||||
|
||||
class MacroAssemblerX86Common : public AbstractMacroAssembler<X86Assembler> {
|
||||
|
@ -1300,140 +1293,15 @@ private:
|
|||
|
||||
static SSECheckState s_sseCheckState;
|
||||
|
||||
static void setSSECheckState()
|
||||
{
|
||||
// Default the flags value to zero; if the compiler is
|
||||
// not MSVC or GCC we will read this as SSE2 not present.
|
||||
volatile int flags_edx = 0;
|
||||
volatile int flags_ecx = 0;
|
||||
#if WTF_COMPILER_MSVC
|
||||
#if WTF_CPU_X86_64
|
||||
int cpuinfo[4];
|
||||
|
||||
__cpuid(cpuinfo, 1);
|
||||
flags_ecx = cpuinfo[2];
|
||||
flags_edx = cpuinfo[3];
|
||||
#else
|
||||
_asm {
|
||||
mov eax, 1 // cpuid function 1 gives us the standard feature set
|
||||
cpuid;
|
||||
mov flags_ecx, ecx;
|
||||
mov flags_edx, edx;
|
||||
}
|
||||
#endif
|
||||
#elif WTF_COMPILER_GCC
|
||||
#if WTF_CPU_X86_64
|
||||
asm (
|
||||
"movl $0x1, %%eax;"
|
||||
"pushq %%rbx;"
|
||||
"cpuid;"
|
||||
"popq %%rbx;"
|
||||
"movl %%ecx, %0;"
|
||||
"movl %%edx, %1;"
|
||||
: "=g" (flags_ecx), "=g" (flags_edx)
|
||||
:
|
||||
: "%eax", "%ecx", "%edx"
|
||||
);
|
||||
#else
|
||||
asm (
|
||||
"movl $0x1, %%eax;"
|
||||
"pushl %%ebx;"
|
||||
"cpuid;"
|
||||
"popl %%ebx;"
|
||||
"movl %%ecx, %0;"
|
||||
"movl %%edx, %1;"
|
||||
: "=g" (flags_ecx), "=g" (flags_edx)
|
||||
:
|
||||
: "%eax", "%ecx", "%edx"
|
||||
);
|
||||
#endif
|
||||
#elif WTF_COMPILER_SUNCC
|
||||
#if WTF_CPU_X86_64
|
||||
asm (
|
||||
"movl $0x1, %%eax;"
|
||||
"pushq %%rbx;"
|
||||
"cpuid;"
|
||||
"popq %%rbx;"
|
||||
"movl %%ecx, (%rsi);"
|
||||
"movl %%edx, (%rdi);"
|
||||
:
|
||||
: "S" (&flags_ecx), "D" (&flags_edx)
|
||||
: "%eax", "%ecx", "%edx"
|
||||
);
|
||||
#else
|
||||
asm (
|
||||
"movl $0x1, %eax;"
|
||||
"pushl %ebx;"
|
||||
"cpuid;"
|
||||
"popl %ebx;"
|
||||
"movl %ecx, (%esi);"
|
||||
"movl %edx, (%edi);"
|
||||
:
|
||||
: "S" (&flags_ecx), "D" (&flags_edx)
|
||||
: "%eax", "%ecx", "%edx"
|
||||
);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
if (s_floatingPointDisabled) {
|
||||
// Disable SSE2.
|
||||
s_sseCheckState = HasSSE;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const int SSEFeatureBit = 1 << 25;
|
||||
static const int SSE2FeatureBit = 1 << 26;
|
||||
static const int SSE3FeatureBit = 1 << 0;
|
||||
static const int SSSE3FeatureBit = 1 << 9;
|
||||
static const int SSE41FeatureBit = 1 << 19;
|
||||
static const int SSE42FeatureBit = 1 << 20;
|
||||
if (flags_ecx & SSE42FeatureBit)
|
||||
s_sseCheckState = HasSSE4_2;
|
||||
else if (flags_ecx & SSE41FeatureBit)
|
||||
s_sseCheckState = HasSSE4_1;
|
||||
else if (flags_ecx & SSSE3FeatureBit)
|
||||
s_sseCheckState = HasSSSE3;
|
||||
else if (flags_ecx & SSE3FeatureBit)
|
||||
s_sseCheckState = HasSSE3;
|
||||
else if (flags_edx & SSE2FeatureBit)
|
||||
s_sseCheckState = HasSSE2;
|
||||
else if (flags_edx & SSEFeatureBit)
|
||||
s_sseCheckState = HasSSE;
|
||||
else
|
||||
s_sseCheckState = NoSSE;
|
||||
|
||||
#ifdef DEBUG
|
||||
if (s_sseCheckState >= HasSSE4_1 && s_SSE4Disabled)
|
||||
s_sseCheckState = HasSSE3;
|
||||
if (s_sseCheckState >= HasSSE3 && s_SSE3Disabled)
|
||||
s_sseCheckState = HasSSE2;
|
||||
#endif
|
||||
}
|
||||
static void setSSECheckState();
|
||||
|
||||
public:
|
||||
#if WTF_CPU_X86
|
||||
#if WTF_OS_MAC_OS_X
|
||||
|
||||
// All X86 Macs are guaranteed to support at least SSE2
|
||||
static bool isSSEPresent()
|
||||
{
|
||||
#if defined(__SSE__) && !defined(DEBUG)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool isSSE2Present()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (s_floatingPointDisabled)
|
||||
return false;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
#else // OS(MAC_OS_X)
|
||||
|
||||
static bool isSSEPresent()
|
||||
{
|
||||
#else
|
||||
if (s_sseCheckState == NotCheckedSSE) {
|
||||
setSSECheckState();
|
||||
}
|
||||
|
@ -1441,10 +1309,14 @@ private:
|
|||
ASSERT(s_sseCheckState != NotCheckedSSE);
|
||||
|
||||
return s_sseCheckState >= HasSSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool isSSE2Present()
|
||||
{
|
||||
#if defined(__SSE2__) && !defined(DEBUG)
|
||||
return true;
|
||||
#else
|
||||
if (s_sseCheckState == NotCheckedSSE) {
|
||||
setSSECheckState();
|
||||
}
|
||||
|
@ -1452,9 +1324,9 @@ private:
|
|||
ASSERT(s_sseCheckState != NotCheckedSSE);
|
||||
|
||||
return s_sseCheckState >= HasSSE2;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // PLATFORM(MAC)
|
||||
#elif !defined(NDEBUG) // CPU(X86)
|
||||
|
||||
// On x86-64 we should never be checking for SSE2 in a non-debug build,
|
||||
|
@ -1467,6 +1339,9 @@ private:
|
|||
#endif
|
||||
static bool isSSE3Present()
|
||||
{
|
||||
#if defined(__SSE3__) && !defined(DEBUG)
|
||||
return true;
|
||||
#else
|
||||
if (s_sseCheckState == NotCheckedSSE) {
|
||||
setSSECheckState();
|
||||
}
|
||||
|
@ -1474,10 +1349,14 @@ private:
|
|||
ASSERT(s_sseCheckState != NotCheckedSSE);
|
||||
|
||||
return s_sseCheckState >= HasSSE3;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool isSSSE3Present()
|
||||
{
|
||||
#if defined(__SSSE3__) && !defined(DEBUG)
|
||||
return true;
|
||||
#else
|
||||
if (s_sseCheckState == NotCheckedSSE) {
|
||||
setSSECheckState();
|
||||
}
|
||||
|
@ -1485,10 +1364,14 @@ private:
|
|||
ASSERT(s_sseCheckState != NotCheckedSSE);
|
||||
|
||||
return s_sseCheckState >= HasSSSE3;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool isSSE41Present()
|
||||
{
|
||||
#if defined(__SSE4_1__) && !defined(DEBUG)
|
||||
return true;
|
||||
#else
|
||||
if (s_sseCheckState == NotCheckedSSE) {
|
||||
setSSECheckState();
|
||||
}
|
||||
|
@ -1496,10 +1379,14 @@ private:
|
|||
ASSERT(s_sseCheckState != NotCheckedSSE);
|
||||
|
||||
return s_sseCheckState >= HasSSE4_1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool isSSE42Present()
|
||||
{
|
||||
#if defined(__SSE4_2__) && !defined(DEBUG)
|
||||
return true;
|
||||
#else
|
||||
if (s_sseCheckState == NotCheckedSSE) {
|
||||
setSSECheckState();
|
||||
}
|
||||
|
@ -1507,8 +1394,10 @@ private:
|
|||
ASSERT(s_sseCheckState != NotCheckedSSE);
|
||||
|
||||
return s_sseCheckState >= HasSSE4_2;
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
#ifdef DEBUG
|
||||
static bool s_floatingPointDisabled;
|
||||
static bool s_SSE3Disabled;
|
||||
|
|
|
@ -89,6 +89,14 @@ GetBuildConfiguration(JSContext *cx, unsigned argc, jsval *vp)
|
|||
if (!JS_SetProperty(cx, info, "x64", value))
|
||||
return false;
|
||||
|
||||
#ifdef JS_ARM_SIMULATOR
|
||||
value = BooleanValue(true);
|
||||
#else
|
||||
value = BooleanValue(false);
|
||||
#endif
|
||||
if (!JS_SetProperty(cx, info, "arm-simulator", value))
|
||||
return false;
|
||||
|
||||
#ifdef MOZ_ASAN
|
||||
value = BooleanValue(true);
|
||||
#else
|
||||
|
|
|
@ -972,7 +972,7 @@ AssertValidColor(const void *thing, uint32_t color)
|
|||
{
|
||||
#ifdef DEBUG
|
||||
ArenaHeader *aheader = reinterpret_cast<const Cell *>(thing)->arenaHeader();
|
||||
JS_ASSERT_IF(color, color < aheader->getThingSize() / CellSize);
|
||||
JS_ASSERT(color < aheader->getThingSize() / CellSize);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -1015,6 +1015,7 @@ bool
|
|||
Cell::isMarked(uint32_t color /* = BLACK */) const
|
||||
{
|
||||
JS_ASSERT(isTenured());
|
||||
JS_ASSERT(arenaHeader()->allocated());
|
||||
AssertValidColor(this, color);
|
||||
return chunk()->bitmap.isMarked(this, color);
|
||||
}
|
||||
|
|
|
@ -356,19 +356,24 @@ IsAboutToBeFinalized(T **thingp)
|
|||
JS_ASSERT(thingp);
|
||||
JS_ASSERT(*thingp);
|
||||
|
||||
T *thing = *thingp;
|
||||
JSRuntime *rt = thing->runtimeFromAnyThread();
|
||||
|
||||
/* Permanent atoms are never finalized by non-owning runtimes. */
|
||||
if (ThingIsPermanentAtom(*thingp) &&
|
||||
!TlsPerThreadData.get()->associatedWith((*thingp)->runtimeFromAnyThread()))
|
||||
{
|
||||
if (ThingIsPermanentAtom(thing) && !TlsPerThreadData.get()->associatedWith(rt))
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
Nursery &nursery = (*thingp)->runtimeFromMainThread()->gcNursery;
|
||||
if (nursery.isInside(*thingp))
|
||||
return !nursery.getForwardedPointer(thingp);
|
||||
Nursery &nursery = rt->gcNursery;
|
||||
JS_ASSERT_IF(!rt->isHeapMinorCollecting(), !nursery.isInside(thing));
|
||||
if (rt->isHeapMinorCollecting()) {
|
||||
if (nursery.isInside(thing))
|
||||
return !nursery.getForwardedPointer(thingp);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
if (!(*thingp)->tenuredZone()->isGCSweeping())
|
||||
|
||||
if (!thing->tenuredZone()->isGCSweeping())
|
||||
return false;
|
||||
|
||||
/*
|
||||
|
@ -378,10 +383,9 @@ IsAboutToBeFinalized(T **thingp)
|
|||
* compartment group and during minor gc. Rather than do the extra check,
|
||||
* we just assert that it's not necessary.
|
||||
*/
|
||||
JS_ASSERT_IF(!(*thingp)->runtimeFromAnyThread()->isHeapMinorCollecting(),
|
||||
!(*thingp)->arenaHeader()->allocatedDuringIncremental);
|
||||
JS_ASSERT_IF(!rt->isHeapMinorCollecting(), !thing->arenaHeader()->allocatedDuringIncremental);
|
||||
|
||||
return !(*thingp)->isMarked();
|
||||
return !thing->isMarked();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
var w = new WeakMap();
|
||||
var g = newGlobal();
|
||||
var k = g.eval('for (var i=0; i<100; i++) new Object(); var q = new Object(); q');
|
||||
w.set(k, {});
|
||||
k = null;
|
||||
|
||||
gc();
|
||||
g.eval('q = null');
|
||||
gc(g);
|
||||
gc();
|
|
@ -781,14 +781,16 @@ class AssemblerX86Shared
|
|||
masm.int3();
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static bool HasSSE2() {
|
||||
return JSC::MacroAssembler::getSSEState() >= JSC::MacroAssembler::HasSSE2;
|
||||
return JSC::MacroAssembler::isSSE2Present();
|
||||
}
|
||||
#endif
|
||||
static bool HasSSE3() {
|
||||
return JSC::MacroAssembler::getSSEState() >= JSC::MacroAssembler::HasSSE3;
|
||||
return JSC::MacroAssembler::isSSE3Present();
|
||||
}
|
||||
static bool HasSSE41() {
|
||||
return JSC::MacroAssembler::getSSEState() >= JSC::MacroAssembler::HasSSE4_1;
|
||||
return JSC::MacroAssembler::isSSE41Present();
|
||||
}
|
||||
|
||||
// The below cmpl methods switch the lhs and rhs when it invokes the
|
||||
|
|
|
@ -1069,7 +1069,7 @@ CodeGeneratorShared::computeDivisionConstants(int d) {
|
|||
// M * n if this is the case (cf. item (a) above).
|
||||
ReciprocalMulConstants rmc;
|
||||
rmc.multiplier = int32_t((int64_t(1) << (shift+32))/d + 1);
|
||||
rmc.shift_amount = shift;
|
||||
rmc.shiftAmount = shift;
|
||||
|
||||
return rmc;
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ struct PatchableBackedgeInfo
|
|||
|
||||
struct ReciprocalMulConstants {
|
||||
int32_t multiplier;
|
||||
int32_t shift_amount;
|
||||
int32_t shiftAmount;
|
||||
};
|
||||
|
||||
class CodeGeneratorShared : public LInstructionVisitor
|
||||
|
|
|
@ -928,6 +928,7 @@ CodeGeneratorX86Shared::visitDivOrModConstantI(LDivOrModConstantI *ins) {
|
|||
// This emits the division answer into edx or the modulus answer into eax.
|
||||
JS_ASSERT(output == eax || output == edx);
|
||||
JS_ASSERT(lhs != eax && lhs != edx);
|
||||
bool isDiv = (output == edx);
|
||||
|
||||
// The absolute value of the denominator isn't a power of 2 (see LDivPowTwoI
|
||||
// and LModPowTwoI).
|
||||
|
@ -942,12 +943,11 @@ CodeGeneratorX86Shared::visitDivOrModConstantI(LDivOrModConstantI *ins) {
|
|||
// is non-negative or (rmc.multiplier * n) + (2^32 * n) otherwise. This is the
|
||||
// desired division result if n is non-negative, and is one less than the result
|
||||
// otherwise.
|
||||
masm.movl(lhs, eax);
|
||||
masm.movl(Imm32(rmc.multiplier), edx);
|
||||
masm.imull(edx);
|
||||
masm.movl(Imm32(rmc.multiplier), eax);
|
||||
masm.imull(lhs);
|
||||
if (rmc.multiplier < 0)
|
||||
masm.addl(lhs, edx);
|
||||
masm.sarl(Imm32(rmc.shift_amount), edx);
|
||||
masm.sarl(Imm32(rmc.shiftAmount), edx);
|
||||
|
||||
// We'll subtract -1 instead of adding 1, because (n < 0 ? -1 : 0) can be
|
||||
// computed with just a sign-extending shift of 31 bits.
|
||||
|
@ -957,17 +957,17 @@ CodeGeneratorX86Shared::visitDivOrModConstantI(LDivOrModConstantI *ins) {
|
|||
masm.subl(eax, edx);
|
||||
}
|
||||
|
||||
// After this, edx contains the correct division result.
|
||||
// After this, edx contains the correct truncated division result.
|
||||
if (d < 0)
|
||||
masm.negl(edx);
|
||||
|
||||
if (output == eax) {
|
||||
if (!isDiv) {
|
||||
masm.imull(Imm32(-d), edx, eax);
|
||||
masm.addl(lhs, eax);
|
||||
}
|
||||
|
||||
if (!ins->mir()->isTruncated()) {
|
||||
if (output == edx) {
|
||||
if (isDiv) {
|
||||
// This is a division op. Multiply the obtained value by d to check if
|
||||
// the correct answer is an integer. This cannot overflow, since |d| > 1.
|
||||
masm.imull(Imm32(d), edx, eax);
|
||||
|
|
|
@ -938,6 +938,7 @@ void
|
|||
nsDisplayListBuilder::MarkFramesForDisplayList(nsIFrame* aDirtyFrame,
|
||||
const nsFrameList& aFrames,
|
||||
const nsRect& aDirtyRect) {
|
||||
mFramesMarkedForDisplay.SetCapacity(mFramesMarkedForDisplay.Length() + aFrames.GetLength());
|
||||
for (nsFrameList::Enumerator e(aFrames); !e.AtEnd(); e.Next()) {
|
||||
mFramesMarkedForDisplay.AppendElement(e.get());
|
||||
MarkOutOfFlowFrameForDisplay(aDirtyFrame, e.get(), aDirtyRect);
|
||||
|
|
|
@ -8905,6 +8905,7 @@ PresShell::DelayedKeyEvent::DelayedKeyEvent(WidgetKeyboardEvent* aEvent) :
|
|||
aEvent->message,
|
||||
aEvent->widget);
|
||||
keyEvent->AssignKeyEventData(*aEvent, false);
|
||||
keyEvent->mFlags.mIsSynthesizedForTests = aEvent->mFlags.mIsSynthesizedForTests;
|
||||
mEvent = keyEvent;
|
||||
}
|
||||
|
||||
|
|
|
@ -12554,8 +12554,13 @@ CSSParserImpl::ParseFunction(nsCSSKeyword aFunction,
|
|||
|
||||
/* Read in a list of values as an array, failing if we can't or if
|
||||
* it's out of bounds.
|
||||
*
|
||||
* We reserve 16 entries in the foundValues array in order to avoid
|
||||
* having to resize the array dynamically when parsing some well-formed
|
||||
* functions. The number 16 is coming from the number of arguments that
|
||||
* matrix3d() accepts.
|
||||
*/
|
||||
InfallibleTArray<nsCSSValue> foundValues;
|
||||
AutoInfallibleTArray<nsCSSValue, 16> foundValues;
|
||||
if (!ParseFunctionInternals(aAllowedTypes, aAllowedTypesAll, aMinElems,
|
||||
aMaxElems, foundValues)) {
|
||||
return false;
|
||||
|
|
|
@ -771,7 +771,7 @@ var gFlexboxTestcases =
|
|||
// max-size violated (the second one) and restart the algorithm. This time,
|
||||
// all the available space (200px - 50px = 150px) goes to the not-yet-frozen
|
||||
// first item, and that puts it above its min-size, so all is well.
|
||||
{
|
||||
{
|
||||
items:
|
||||
[
|
||||
{
|
||||
|
|
|
@ -48,7 +48,9 @@
|
|||
#include "mtransport_test_utils.h"
|
||||
#include "gtest_ringbuffer_dumper.h"
|
||||
MtransportTestUtils *test_utils;
|
||||
nsCOMPtr<nsIThread> gThread;
|
||||
nsCOMPtr<nsIThread> gMainThread;
|
||||
nsCOMPtr<nsIThread> gGtestThread;
|
||||
bool gTestsComplete = false;
|
||||
|
||||
#ifndef USE_FAKE_MEDIA_STREAMS
|
||||
#error USE_FAKE_MEDIA_STREAMS undefined
|
||||
|
@ -213,19 +215,6 @@ enum mediaPipelineFlags
|
|||
};
|
||||
|
||||
|
||||
static bool SetupGlobalThread() {
|
||||
if (!gThread) {
|
||||
nsIThread *thread;
|
||||
|
||||
nsresult rv = NS_NewNamedThread("pseudo-main",&thread);
|
||||
if (NS_FAILED(rv))
|
||||
return false;
|
||||
|
||||
gThread = thread;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
class TestObserver : public AFakePCObserver
|
||||
{
|
||||
public:
|
||||
|
@ -703,7 +692,7 @@ class SignalingAgent {
|
|||
|
||||
|
||||
~SignalingAgent() {
|
||||
mozilla::SyncRunnable::DispatchToThread(gThread,
|
||||
mozilla::SyncRunnable::DispatchToThread(gMainThread,
|
||||
WrapRunnable(this, &SignalingAgent::Close));
|
||||
}
|
||||
|
||||
|
@ -1282,7 +1271,6 @@ class SignalingEnvironment : public ::testing::Environment {
|
|||
class SignalingAgentTest : public ::testing::Test {
|
||||
public:
|
||||
static void SetUpTestCase() {
|
||||
ASSERT_TRUE(SetupGlobalThread());
|
||||
}
|
||||
|
||||
void TearDown() {
|
||||
|
@ -1301,7 +1289,7 @@ class SignalingAgentTest : public ::testing::Test {
|
|||
ScopedDeletePtr<SignalingAgent> agent(
|
||||
new SignalingAgent("agent", stun_addr, stun_port));
|
||||
|
||||
agent->Init(gThread);
|
||||
agent->Init(gMainThread);
|
||||
|
||||
if (wait_for_gather) {
|
||||
if (!agent->WaitForGatherAllowFail())
|
||||
|
@ -1345,7 +1333,6 @@ public:
|
|||
stun_port_(stun_port) {}
|
||||
|
||||
static void SetUpTestCase() {
|
||||
ASSERT_TRUE(SetupGlobalThread());
|
||||
}
|
||||
|
||||
void EnsureInit() {
|
||||
|
@ -1356,8 +1343,8 @@ public:
|
|||
a1_ = new SignalingAgent(callerName, stun_addr_, stun_port_);
|
||||
a2_ = new SignalingAgent(calleeName, stun_addr_, stun_port_);
|
||||
|
||||
a1_->Init(gThread);
|
||||
a2_->Init(gThread);
|
||||
a1_->Init(gMainThread);
|
||||
a2_->Init(gMainThread);
|
||||
|
||||
if (wait_for_gather_) {
|
||||
WaitForGather();
|
||||
|
@ -1370,7 +1357,6 @@ public:
|
|||
}
|
||||
|
||||
static void TearDownTestCase() {
|
||||
gThread = nullptr;
|
||||
}
|
||||
|
||||
void CreateOffer(sipcc::MediaConstraints& constraints,
|
||||
|
@ -1636,12 +1622,51 @@ public:
|
|||
uint16_t stun_port_;
|
||||
};
|
||||
|
||||
static void SetIntPrefOnMainThread(nsCOMPtr<nsIPrefBranch> prefs,
|
||||
const char *pref_name,
|
||||
int new_value) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
prefs->SetIntPref(pref_name, new_value);
|
||||
}
|
||||
|
||||
static void SetMaxFsFr(nsCOMPtr<nsIPrefBranch> prefs,
|
||||
int max_fs,
|
||||
int max_fr) {
|
||||
gMainThread->Dispatch(
|
||||
WrapRunnableNM(SetIntPrefOnMainThread,
|
||||
prefs,
|
||||
"media.navigator.video.max_fs",
|
||||
max_fs),
|
||||
NS_DISPATCH_SYNC);
|
||||
|
||||
gMainThread->Dispatch(
|
||||
WrapRunnableNM(SetIntPrefOnMainThread,
|
||||
prefs,
|
||||
"media.navigator.video.max_fr",
|
||||
max_fr),
|
||||
NS_DISPATCH_SYNC);
|
||||
}
|
||||
|
||||
class FsFrPrefClearer {
|
||||
public:
|
||||
FsFrPrefClearer(nsCOMPtr<nsIPrefBranch> prefs): mPrefs(prefs) {}
|
||||
~FsFrPrefClearer() {
|
||||
mPrefs->ClearUserPref("media.navigator.video.max_fs");
|
||||
mPrefs->ClearUserPref("media.navigator.video.max_fr");
|
||||
gMainThread->Dispatch(
|
||||
WrapRunnableNM(FsFrPrefClearer::ClearUserPrefOnMainThread,
|
||||
mPrefs,
|
||||
"media.navigator.video.max_fs"),
|
||||
NS_DISPATCH_SYNC);
|
||||
gMainThread->Dispatch(
|
||||
WrapRunnableNM(FsFrPrefClearer::ClearUserPrefOnMainThread,
|
||||
mPrefs,
|
||||
"media.navigator.video.max_fr"),
|
||||
NS_DISPATCH_SYNC);
|
||||
}
|
||||
|
||||
static void ClearUserPrefOnMainThread(nsCOMPtr<nsIPrefBranch> prefs,
|
||||
const char *pref_name) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
prefs->ClearUserPref(pref_name);
|
||||
}
|
||||
private:
|
||||
nsCOMPtr<nsIPrefBranch> mPrefs;
|
||||
|
@ -3458,8 +3483,7 @@ TEST_F(SignalingTest, MaxFsFrInOffer)
|
|||
ASSERT_TRUE(prefs);
|
||||
FsFrPrefClearer prefClearer(prefs);
|
||||
|
||||
prefs->SetIntPref("media.navigator.video.max_fs", 300);
|
||||
prefs->SetIntPref("media.navigator.video.max_fr", 30);
|
||||
SetMaxFsFr(prefs, 300, 30);
|
||||
|
||||
a1_->CreateOffer(constraints, OFFER_AV, SHOULD_CHECK_AV);
|
||||
|
||||
|
@ -3479,8 +3503,7 @@ TEST_F(SignalingTest, MaxFsFrInAnswer)
|
|||
FsFrPrefClearer prefClearer(prefs);
|
||||
|
||||
// We don't want max_fs and max_fr prefs impact SDP at this moment
|
||||
prefs->SetIntPref("media.navigator.video.max_fs", 0);
|
||||
prefs->SetIntPref("media.navigator.video.max_fr", 0);
|
||||
SetMaxFsFr(prefs, 0, 0);
|
||||
|
||||
a1_->CreateOffer(constraints, OFFER_AV, SHOULD_CHECK_AV);
|
||||
|
||||
|
@ -3489,8 +3512,7 @@ TEST_F(SignalingTest, MaxFsFrInAnswer)
|
|||
|
||||
a2_->SetRemote(TestObserver::OFFER, a1_->offer());
|
||||
|
||||
prefs->SetIntPref("media.navigator.video.max_fs", 600);
|
||||
prefs->SetIntPref("media.navigator.video.max_fr", 60);
|
||||
SetMaxFsFr(prefs, 600, 60);
|
||||
|
||||
a2_->CreateAnswer(constraints, a1_->offer(), OFFER_AV | ANSWER_AV);
|
||||
|
||||
|
@ -3510,8 +3532,7 @@ TEST_F(SignalingTest, MaxFsFrCalleeCodec)
|
|||
FsFrPrefClearer prefClearer(prefs);
|
||||
|
||||
// We don't want max_fs and max_fr prefs impact SDP at this moment
|
||||
prefs->SetIntPref("media.navigator.video.max_fs", 0);
|
||||
prefs->SetIntPref("media.navigator.video.max_fr", 0);
|
||||
SetMaxFsFr(prefs, 0, 0);
|
||||
|
||||
a1_->CreateOffer(constraints, OFFER_AV, SHOULD_CHECK_AV);
|
||||
|
||||
|
@ -3567,8 +3588,7 @@ TEST_F(SignalingTest, MaxFsFrCallerCodec)
|
|||
FsFrPrefClearer prefClearer(prefs);
|
||||
|
||||
// We don't want max_fs and max_fr prefs impact SDP at this moment
|
||||
prefs->SetIntPref("media.navigator.video.max_fs", 0);
|
||||
prefs->SetIntPref("media.navigator.video.max_fr", 0);
|
||||
SetMaxFsFr(prefs, 0, 0);
|
||||
|
||||
a1_->CreateOffer(constraints, OFFER_AV, SHOULD_CHECK_AV);
|
||||
a1_->SetLocal(TestObserver::OFFER, a1_->offer());
|
||||
|
@ -3642,6 +3662,39 @@ static std::string get_environment(const char *name) {
|
|||
return value;
|
||||
}
|
||||
|
||||
// This exists to send as an event to trigger shutdown.
|
||||
static void tests_complete() {
|
||||
gTestsComplete = true;
|
||||
}
|
||||
|
||||
// The GTest thread runs this instead of the main thread so it can
|
||||
// do things like ASSERT_TRUE_WAIT which you could not do on the main thread.
|
||||
static int gtest_main(int argc, char **argv) {
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
for(int i=0; i<argc; i++) {
|
||||
if (!strcmp(argv[i],"-t")) {
|
||||
kDefaultTimeout = 20000;
|
||||
}
|
||||
}
|
||||
|
||||
::testing::AddGlobalTestEnvironment(new test::SignalingEnvironment);
|
||||
int result = RUN_ALL_TESTS();
|
||||
|
||||
test_utils->sts_target()->Dispatch(
|
||||
WrapRunnableNM(&TestStunServer::ShutdownInstance), NS_DISPATCH_SYNC);
|
||||
|
||||
// Set the global shutdown flag and tickle the main thread
|
||||
// The main thread did not go through Init() so calling Shutdown()
|
||||
// on it will not work.
|
||||
gMainThread->Dispatch(WrapRunnableNM(tests_complete), NS_DISPATCH_SYNC);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
// This test can cause intermittent oranges on the builders
|
||||
|
@ -3667,14 +3720,6 @@ int main(int argc, char **argv) {
|
|||
NSS_NoDB_Init(nullptr);
|
||||
NSS_SetDomesticPolicy();
|
||||
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
for(int i=0; i<argc; i++) {
|
||||
if (!strcmp(argv[i],"-t")) {
|
||||
kDefaultTimeout = 20000;
|
||||
}
|
||||
}
|
||||
|
||||
::testing::TestEventListeners& listeners =
|
||||
::testing::UnitTest::GetInstance()->listeners();
|
||||
// Adds a listener to the end. Google Test takes the ownership.
|
||||
|
@ -3682,15 +3727,28 @@ int main(int argc, char **argv) {
|
|||
test_utils->sts_target()->Dispatch(
|
||||
WrapRunnableNM(&TestStunServer::GetInstance), NS_DISPATCH_SYNC);
|
||||
|
||||
::testing::AddGlobalTestEnvironment(new test::SignalingEnvironment);
|
||||
int result = RUN_ALL_TESTS();
|
||||
// Set the main thread global which is this thread.
|
||||
nsIThread *thread;
|
||||
NS_GetMainThread(&thread);
|
||||
gMainThread = thread;
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
test_utils->sts_target()->Dispatch(
|
||||
WrapRunnableNM(&TestStunServer::ShutdownInstance), NS_DISPATCH_SYNC);
|
||||
// Now create the GTest thread and run all of the tests on it
|
||||
// When it is complete it will set gTestsComplete
|
||||
NS_NewNamedThread("gtest_thread", &thread);
|
||||
gGtestThread = thread;
|
||||
|
||||
int result;
|
||||
gGtestThread->Dispatch(
|
||||
WrapRunnableNMRet(gtest_main, argc, argv, &result), NS_DISPATCH_NORMAL);
|
||||
|
||||
// Here we handle the event queue for dispatches to the main thread
|
||||
// When the GTest thread is complete it will send one more dispatch
|
||||
// with gTestsComplete == true.
|
||||
while (!gTestsComplete && NS_ProcessNextEvent());
|
||||
|
||||
gGtestThread->Shutdown();
|
||||
|
||||
// Because we don't initialize on the main thread, we can't register for
|
||||
// XPCOM shutdown callbacks (where the context is usually shut down) --
|
||||
// so we need to explictly destroy the context.
|
||||
sipcc::PeerConnectionCtx::Destroy();
|
||||
delete test_utils;
|
||||
|
||||
|
|
|
@ -136,17 +136,37 @@ typedef std::vector<std::pair<pthread_key_t, void *>,
|
|||
LibcAllocator<std::pair<pthread_key_t, void *> > >
|
||||
TLSInfoList;
|
||||
|
||||
/**
|
||||
* Return the system's page size
|
||||
*/
|
||||
static size_t getPageSize(void) {
|
||||
#ifdef HAVE_GETPAGESIZE
|
||||
return getpagesize();
|
||||
#elif defined(_SC_PAGESIZE)
|
||||
return sysconf(_SC_PAGESIZE);
|
||||
#elif defined(PAGE_SIZE)
|
||||
return PAGE_SIZE;
|
||||
#else
|
||||
#warning "Hard-coding page size to 4096 bytes"
|
||||
return 4096
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Align the pointer to the next page boundary unless it's already aligned
|
||||
*/
|
||||
static uintptr_t ceilToPage(uintptr_t aPtr) {
|
||||
size_t pageSize = getPageSize();
|
||||
|
||||
return ((aPtr + pageSize - 1) / pageSize) * pageSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* The stack size is chosen carefully so the frozen threads doesn't consume too
|
||||
* much memory in the Nuwa process. The threads shouldn't run deep recursive
|
||||
* methods or do large allocations on the stack to avoid stack overflow.
|
||||
*/
|
||||
#ifndef NUWA_STACK_SIZE
|
||||
#ifndef PAGE_SIZE
|
||||
#warning "Hard-coding page size to 4096 byte"
|
||||
#define PAGE_SIZE 4096ul
|
||||
#endif
|
||||
#define PAGE_ALIGN_MASK (~(PAGE_SIZE-1))
|
||||
#define NUWA_STACK_SIZE (1024 * 128)
|
||||
#endif
|
||||
|
||||
|
@ -495,17 +515,13 @@ thread_info_new(void) {
|
|||
tinfo->recreatedThreadID = 0;
|
||||
tinfo->recreatedNativeThreadID = 0;
|
||||
tinfo->reacquireMutex = nullptr;
|
||||
tinfo->stk = malloc(NUWA_STACK_SIZE + PAGE_SIZE);
|
||||
tinfo->stk = malloc(NUWA_STACK_SIZE + getPageSize());
|
||||
|
||||
// We use a smaller stack size. Add protection to stack overflow: mprotect()
|
||||
// stack top (the page at the lowest address) so we crash instead of corrupt
|
||||
// other content that is malloc()'d.
|
||||
unsigned long long pageGuard = ((unsigned long long)tinfo->stk);
|
||||
pageGuard &= PAGE_ALIGN_MASK;
|
||||
if (pageGuard != (unsigned long long) tinfo->stk) {
|
||||
pageGuard += PAGE_SIZE; // Round up to be page-aligned.
|
||||
}
|
||||
mprotect((void*)pageGuard, PAGE_SIZE, PROT_READ);
|
||||
uintptr_t pageGuard = ceilToPage((uintptr_t)tinfo->stk);
|
||||
mprotect((void*)pageGuard, getPageSize(), PROT_READ);
|
||||
|
||||
pthread_attr_init(&tinfo->threadAttr);
|
||||
|
||||
|
|
|
@ -942,15 +942,9 @@ TabActor.prototype = {
|
|||
* True if the window.console object is native, or false otherwise.
|
||||
*/
|
||||
hasNativeConsoleAPI: function BTA_hasNativeConsoleAPI(aWindow) {
|
||||
let isNative = false;
|
||||
try {
|
||||
// We are very explicitly examining the "console" property of
|
||||
// the non-Xrayed object here.
|
||||
let console = aWindow.wrappedJSObject.console;
|
||||
isNative = console instanceof aWindow.Console;
|
||||
}
|
||||
catch (ex) { }
|
||||
return isNative;
|
||||
// Do not expose WebConsoleActor function directly as it is always
|
||||
// loaded after the BrowserTabActor
|
||||
return WebConsoleActor.prototype.hasNativeConsoleAPI(aWindow);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -6,34 +6,28 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
let {Cc, Ci, Cu} = require("chrome");
|
||||
let Cc = Components.classes;
|
||||
let Ci = Components.interfaces;
|
||||
let Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
let { DebuggerServer, ActorPool } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
|
||||
// Symbols from script.js
|
||||
let { ThreadActor, EnvironmentActor, ObjectActor, LongStringActor } = DebuggerServer;
|
||||
|
||||
Cu.import("resource://gre/modules/jsdebugger.jsm");
|
||||
addDebuggerToGlobal(this);
|
||||
let devtools = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
XPCOMUtils.defineLazyGetter(this, "NetworkMonitor", () => {
|
||||
return require("devtools/toolkit/webconsole/network-monitor")
|
||||
return devtools.require("devtools/toolkit/webconsole/network-monitor")
|
||||
.NetworkMonitor;
|
||||
});
|
||||
XPCOMUtils.defineLazyGetter(this, "NetworkMonitorChild", () => {
|
||||
return require("devtools/toolkit/webconsole/network-monitor")
|
||||
return devtools.require("devtools/toolkit/webconsole/network-monitor")
|
||||
.NetworkMonitorChild;
|
||||
});
|
||||
XPCOMUtils.defineLazyGetter(this, "ConsoleProgressListener", () => {
|
||||
return require("devtools/toolkit/webconsole/network-monitor")
|
||||
return devtools.require("devtools/toolkit/webconsole/network-monitor")
|
||||
.ConsoleProgressListener;
|
||||
});
|
||||
XPCOMUtils.defineLazyGetter(this, "events", () => {
|
||||
return require("sdk/event/core");
|
||||
});
|
||||
|
||||
for (let name of ["WebConsoleUtils", "ConsoleServiceListener",
|
||||
"ConsoleAPIListener", "JSTermHelpers", "JSPropertyProvider",
|
||||
|
@ -43,7 +37,7 @@ for (let name of ["WebConsoleUtils", "ConsoleServiceListener",
|
|||
if (prop == "WebConsoleUtils") {
|
||||
prop = "Utils";
|
||||
}
|
||||
return require("devtools/toolkit/webconsole/utils")[prop];
|
||||
return devtools.require("devtools/toolkit/webconsole/utils")[prop];
|
||||
}.bind(null, name),
|
||||
configurable: true,
|
||||
enumerable: true
|
||||
|
@ -1834,12 +1828,6 @@ NetworkEventActor.prototype.requestTypes =
|
|||
"getEventTimings": NetworkEventActor.prototype.onGetEventTimings,
|
||||
};
|
||||
|
||||
exports.register = function(handle) {
|
||||
handle.addGlobalActor(WebConsoleActor, "consoleActor");
|
||||
handle.addTabActor(WebConsoleActor, "consoleActor");
|
||||
};
|
||||
DebuggerServer.addTabActor(WebConsoleActor, "consoleActor");
|
||||
DebuggerServer.addGlobalActor(WebConsoleActor, "consoleActor");
|
||||
|
||||
exports.unregister = function(handle) {
|
||||
handle.removeGlobalActor(WebConsoleActor, "consoleActor");
|
||||
handle.removeTabActor(WebConsoleActor, "consoleActor");
|
||||
};
|
||||
|
|
|
@ -360,7 +360,7 @@ var DebuggerServer = {
|
|||
// In case of apps being loaded in parent process, DebuggerServer is already
|
||||
// initialized and browser actors are already loaded,
|
||||
// but childtab.js hasn't been loaded yet.
|
||||
if (!DebuggerServer.tabActorFactories.hasOwnProperty("consoleActor")) {
|
||||
if (!("WebConsoleActor" in this)) {
|
||||
this.addTabActors();
|
||||
}
|
||||
// But webbrowser.js and childtab.js aren't loaded from shell.js.
|
||||
|
@ -377,7 +377,7 @@ var DebuggerServer = {
|
|||
*/
|
||||
addTabActors: function() {
|
||||
this.addActors("resource://gre/modules/devtools/server/actors/script.js");
|
||||
this.registerModule("devtools/server/actors/webconsole");
|
||||
this.addActors("resource://gre/modules/devtools/server/actors/webconsole.js");
|
||||
this.registerModule("devtools/server/actors/inspector");
|
||||
this.registerModule("devtools/server/actors/call-watcher");
|
||||
this.registerModule("devtools/server/actors/canvas");
|
||||
|
|
|
@ -117,8 +117,7 @@ function test_profile(aClient, aProfiler)
|
|||
let location = stack.name + " (" + stack.filename + ":" + funcLine + ")";
|
||||
// At least one sample is expected to have been in the busy wait above.
|
||||
do_check_true(aResponse.profile.threads[0].samples.some(function(sample) {
|
||||
return sample.name == "(root)" &&
|
||||
typeof sample.frames == "object" &&
|
||||
return typeof sample.frames == "object" &&
|
||||
sample.frames.length != 0 &&
|
||||
sample.frames.some(function(f) {
|
||||
return (f.line == stack.lineNumber) &&
|
||||
|
|
|
@ -26,10 +26,6 @@
|
|||
#include "UnwinderThread2.h"
|
||||
#include "TableTicker.h"
|
||||
|
||||
// JSON
|
||||
#include "JSObjectBuilder.h"
|
||||
#include "nsIJSRuntimeService.h"
|
||||
|
||||
// Meta
|
||||
#include "nsXPCOM.h"
|
||||
#include "nsXPCOMCID.h"
|
||||
|
|
|
@ -151,6 +151,9 @@ static inline char* profiler_get_profile() { return nullptr; }
|
|||
// Get the profile encoded as a JSON object.
|
||||
static inline JSObject* profiler_get_profile_jsobject(JSContext* aCx) { return nullptr; }
|
||||
|
||||
// Get the profile and write it into a file
|
||||
static inline void profiler_save_profile_to_file(char* aFilename) { }
|
||||
|
||||
// Get the features supported by the profiler that are accepted by profiler_init.
|
||||
// Returns a null terminated char* array.
|
||||
static inline char** profiler_get_features() { return nullptr; }
|
||||
|
|
|
@ -56,6 +56,8 @@ char* mozilla_sampler_get_profile();
|
|||
|
||||
JSObject *mozilla_sampler_get_profile_data(JSContext *aCx);
|
||||
|
||||
void mozilla_sampler_save_profile_to_file(const char* aFilename);
|
||||
|
||||
const char** mozilla_sampler_get_features();
|
||||
|
||||
void mozilla_sampler_init(void* stackTop);
|
||||
|
|
|
@ -143,6 +143,12 @@ JSObject* profiler_get_profile_jsobject(JSContext* aCx)
|
|||
return mozilla_sampler_get_profile_data(aCx);
|
||||
}
|
||||
|
||||
static inline
|
||||
void profiler_save_profile_to_file(const char* aFilename)
|
||||
{
|
||||
return mozilla_sampler_save_profile_to_file(aFilename);
|
||||
}
|
||||
|
||||
static inline
|
||||
const char** profiler_get_features()
|
||||
{
|
||||
|
|
|
@ -1,318 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "JSCustomObjectBuilder.h"
|
||||
|
||||
#include "mozilla/ArrayUtils.h" // for ArrayLength
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsUTF8Utils.h"
|
||||
|
||||
#if _MSC_VER
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
// These are owned and deleted by JSCustomObject
|
||||
struct PropertyValue {
|
||||
virtual ~PropertyValue() {}
|
||||
virtual void SendToStream(std::ostream& stream) = 0;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct finalizer_impl
|
||||
{
|
||||
static void run(T) {}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct finalizer_impl<T*>
|
||||
{
|
||||
static void run(T* p) {
|
||||
delete p;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct finalizer_impl<char *>
|
||||
{
|
||||
static void run(char* p) {
|
||||
free(p);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class TemplatePropertyValue : public PropertyValue {
|
||||
public:
|
||||
TemplatePropertyValue(T aValue)
|
||||
: mValue(aValue)
|
||||
{}
|
||||
|
||||
~TemplatePropertyValue() {
|
||||
finalizer_impl<T>::run(mValue);
|
||||
}
|
||||
|
||||
virtual void SendToStream(std::ostream& stream);
|
||||
private:
|
||||
T mValue;
|
||||
};
|
||||
|
||||
// Escape a UTF8 string to a stream. When an illegal encoding
|
||||
// is found it will insert "INVALID" and the function will return.
|
||||
void EscapeToStream(std::ostream& stream, const char* str) {
|
||||
stream << "\"";
|
||||
|
||||
size_t len = strlen(str);
|
||||
const char* end = &str[len];
|
||||
while (str < end) {
|
||||
bool err;
|
||||
const char* utf8CharStart = str;
|
||||
uint32_t ucs4Char = UTF8CharEnumerator::NextChar(&str, end, &err);
|
||||
|
||||
if (err) {
|
||||
// Encoding error
|
||||
stream << "INVALID\"";
|
||||
return;
|
||||
}
|
||||
|
||||
// See http://www.ietf.org/rfc/rfc4627.txt?number=4627
|
||||
// characters that must be escaped: quotation mark,
|
||||
// reverse solidus, and the control characters
|
||||
// (U+0000 through U+001F).
|
||||
if (ucs4Char == '\"') {
|
||||
stream << "\\\"";
|
||||
} else if (ucs4Char == '\\') {
|
||||
stream << "\\\\";
|
||||
} else if (ucs4Char > 0xFF) {
|
||||
char16_t chr[2];
|
||||
ConvertUTF8toUTF16 encoder(chr);
|
||||
encoder.write(utf8CharStart, uint32_t(str-utf8CharStart));
|
||||
char escChar[13];
|
||||
snprintf(escChar, mozilla::ArrayLength(escChar), "\\u%04X\\u%04X", chr[0], chr[1]);
|
||||
stream << escChar;
|
||||
} else if (ucs4Char < 0x1F || ucs4Char > 0xFF) {
|
||||
char escChar[7];
|
||||
snprintf(escChar, mozilla::ArrayLength(escChar), "\\u%04X", ucs4Char);
|
||||
stream << escChar;
|
||||
} else {
|
||||
stream << char(ucs4Char);
|
||||
}
|
||||
}
|
||||
stream << "\"";
|
||||
}
|
||||
|
||||
class JSCustomObject {
|
||||
public:
|
||||
JSCustomObject() {}
|
||||
~JSCustomObject();
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& stream, JSCustomObject* entry);
|
||||
|
||||
template<class T>
|
||||
void AddProperty(const char* aName, T aValue) {
|
||||
mProperties.Put(nsDependentCString(aName), new TemplatePropertyValue<T>(aValue));
|
||||
}
|
||||
|
||||
nsDataHashtable<nsCStringHashKey, PropertyValue*> mProperties;
|
||||
};
|
||||
|
||||
class JSCustomArray {
|
||||
public:
|
||||
nsTArray<PropertyValue*> mValues;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& stream, JSCustomArray* entry);
|
||||
|
||||
template<class T>
|
||||
void AppendElement(T aValue) {
|
||||
mValues.AppendElement(new TemplatePropertyValue<T>(aValue));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct SendToStreamImpl
|
||||
{
|
||||
static void run(std::ostream& stream, const T& t) {
|
||||
stream << t;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct SendToStreamImpl<T*>
|
||||
{
|
||||
static void run(std::ostream& stream, T* t) {
|
||||
stream << *t;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SendToStreamImpl<char *>
|
||||
{
|
||||
static void run(std::ostream& stream, char* p) {
|
||||
EscapeToStream(stream, p);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SendToStreamImpl<double>
|
||||
{
|
||||
static void run(std::ostream& stream, double p) {
|
||||
// 13 for ms, 16 of microseconds, plus an extra 2
|
||||
stream.precision(18);
|
||||
stream << p;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SendToStreamImpl<JSCustomObject*>
|
||||
{
|
||||
static void run(std::ostream& stream, JSCustomObject* p) {
|
||||
stream << p;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SendToStreamImpl<JSCustomArray*>
|
||||
{
|
||||
static void run(std::ostream& stream, JSCustomArray* p) {
|
||||
stream << p;
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> void
|
||||
TemplatePropertyValue<T>::SendToStream(std::ostream& stream)
|
||||
{
|
||||
SendToStreamImpl<T>::run(stream, mValue);
|
||||
}
|
||||
|
||||
struct JSONStreamClosure {
|
||||
std::ostream& mStream;
|
||||
bool mNeedsComma;
|
||||
};
|
||||
|
||||
PLDHashOperator HashTableOutput(const nsACString& aKey, PropertyValue* aValue, void* stream)
|
||||
{
|
||||
JSONStreamClosure& streamClosure = *(JSONStreamClosure*)stream;
|
||||
if (streamClosure.mNeedsComma) {
|
||||
streamClosure.mStream << ",";
|
||||
}
|
||||
streamClosure.mNeedsComma = true;
|
||||
EscapeToStream(streamClosure.mStream, (const char*)aKey.BeginReading());
|
||||
streamClosure.mStream << ":";
|
||||
aValue->SendToStream(streamClosure.mStream);
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& stream, JSCustomObject* entry)
|
||||
{
|
||||
JSONStreamClosure streamClosure = {stream, false};
|
||||
stream << "{";
|
||||
entry->mProperties.EnumerateRead(HashTableOutput, &streamClosure);
|
||||
stream << "}";
|
||||
return stream;
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& stream, JSCustomArray* entry)
|
||||
{
|
||||
bool needsComma = false;
|
||||
stream << "[";
|
||||
for (uint32_t i = 0; i < entry->mValues.Length(); i++) {
|
||||
if (needsComma) {
|
||||
stream << ",";
|
||||
}
|
||||
entry->mValues[i]->SendToStream(stream);
|
||||
needsComma = true;
|
||||
}
|
||||
stream << "]";
|
||||
return stream;
|
||||
}
|
||||
|
||||
PLDHashOperator HashTableFree(const nsACString& aKey, PropertyValue* aValue, void* stream)
|
||||
{
|
||||
delete aValue;
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
JSCustomObject::~JSCustomObject()
|
||||
{
|
||||
mProperties.EnumerateRead(HashTableFree, nullptr);
|
||||
}
|
||||
|
||||
JSCustomObjectBuilder::JSCustomObjectBuilder()
|
||||
{}
|
||||
|
||||
void
|
||||
JSCustomObjectBuilder::DeleteObject(JSCustomObject* aObject)
|
||||
{
|
||||
delete aObject;
|
||||
}
|
||||
|
||||
void
|
||||
JSCustomObjectBuilder::Serialize(JSCustomObject* aObject, std::ostream& stream)
|
||||
{
|
||||
stream << aObject;
|
||||
}
|
||||
|
||||
void
|
||||
JSCustomObjectBuilder::DefineProperty(JSCustomObject *aObject, const char *name, JSCustomObject *aValue)
|
||||
{
|
||||
aObject->AddProperty(name, aValue);
|
||||
}
|
||||
|
||||
void
|
||||
JSCustomObjectBuilder::DefineProperty(JSCustomObject *aObject, const char *name, JSCustomArray *aValue)
|
||||
{
|
||||
aObject->AddProperty(name, aValue);
|
||||
}
|
||||
|
||||
void
|
||||
JSCustomObjectBuilder::DefineProperty(JSCustomObject *aObject, const char *name, int aValue)
|
||||
{
|
||||
aObject->AddProperty(name, aValue);
|
||||
}
|
||||
|
||||
void
|
||||
JSCustomObjectBuilder::DefineProperty(JSCustomObject *aObject, const char *name, double aValue)
|
||||
{
|
||||
aObject->AddProperty(name, aValue);
|
||||
}
|
||||
|
||||
void
|
||||
JSCustomObjectBuilder::DefineProperty(JSCustomObject *aObject, const char *name, const char *aValue)
|
||||
{
|
||||
// aValue copy will be freed by the property desctructor (template specialization)
|
||||
aObject->AddProperty(name, strdup(aValue));
|
||||
}
|
||||
|
||||
void
|
||||
JSCustomObjectBuilder::ArrayPush(JSCustomArray *aArray, int aValue)
|
||||
{
|
||||
aArray->AppendElement(aValue);
|
||||
}
|
||||
|
||||
void
|
||||
JSCustomObjectBuilder::ArrayPush(JSCustomArray *aArray, const char *aValue)
|
||||
{
|
||||
// aValue copy will be freed by the property desctructor (template specialization)
|
||||
aArray->AppendElement(strdup(aValue));
|
||||
}
|
||||
|
||||
void
|
||||
JSCustomObjectBuilder::ArrayPush(JSCustomArray *aArray, JSCustomObject *aObject)
|
||||
{
|
||||
aArray->AppendElement(aObject);
|
||||
}
|
||||
|
||||
JSCustomArray*
|
||||
JSCustomObjectBuilder::CreateArray() {
|
||||
return new JSCustomArray();
|
||||
}
|
||||
|
||||
JSCustomObject*
|
||||
JSCustomObjectBuilder::CreateObject() {
|
||||
return new JSCustomObject();
|
||||
}
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef JSCUSTOMOBJECTBUILDER_H
|
||||
#define JSCUSTOMOBJECTBUILDER_H
|
||||
|
||||
#include <ostream>
|
||||
#include <stdlib.h>
|
||||
#include "js/RootingAPI.h"
|
||||
|
||||
class JSCustomObject;
|
||||
class JSCustomArray;
|
||||
|
||||
class JSCustomObjectBuilder
|
||||
{
|
||||
public:
|
||||
typedef JSCustomObject* Object;
|
||||
typedef JSCustomArray* Array;
|
||||
typedef JSCustomObject* ObjectHandle;
|
||||
typedef JSCustomArray* ArrayHandle;
|
||||
typedef js::FakeRooted<JSCustomObject*> RootedObject;
|
||||
typedef js::FakeRooted<JSCustomArray*> RootedArray;
|
||||
|
||||
// We need to ensure that this object lives on the stack so that GC sees it properly
|
||||
JSCustomObjectBuilder();
|
||||
|
||||
void Serialize(JSCustomObject* aObject, std::ostream& stream);
|
||||
|
||||
void DefineProperty(JSCustomObject *aObject, const char *name, JSCustomObject *aValue);
|
||||
void DefineProperty(JSCustomObject *aObject, const char *name, JSCustomArray *aValue);
|
||||
void DefineProperty(JSCustomObject *aObject, const char *name, int value);
|
||||
void DefineProperty(JSCustomObject *aObject, const char *name, double value);
|
||||
void DefineProperty(JSCustomObject *aObject, const char *name, const char *value, size_t valueLength);
|
||||
void DefineProperty(JSCustomObject *aObject, const char *name, const char *value);
|
||||
void ArrayPush(JSCustomArray *aArray, int value);
|
||||
void ArrayPush(JSCustomArray *aArray, const char *value);
|
||||
void ArrayPush(JSCustomArray *aArray, JSCustomObject *aObject);
|
||||
JSCustomArray *CreateArray();
|
||||
JSCustomObject *CreateObject();
|
||||
|
||||
// Delete this object and all of its descendant
|
||||
void DeleteObject(JSCustomObject* aObject);
|
||||
|
||||
JSContext *context() const { return nullptr; }
|
||||
|
||||
private:
|
||||
// This class can't be copied
|
||||
JSCustomObjectBuilder(const JSCustomObjectBuilder&);
|
||||
JSCustomObjectBuilder& operator=(const JSCustomObjectBuilder&);
|
||||
|
||||
void* operator new(size_t);
|
||||
void* operator new[](size_t);
|
||||
void operator delete(void*) {
|
||||
// Since JSCustomObjectBuilder has a virtual destructor the compiler
|
||||
// has to provide a destructor in the object file that will call
|
||||
// operate delete in case there is a derived class since its
|
||||
// destructor wont know how to free this instance.
|
||||
abort();
|
||||
}
|
||||
void operator delete[](void*);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,145 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "nsString.h"
|
||||
#include "JSObjectBuilder.h"
|
||||
|
||||
JSObjectBuilder::JSObjectBuilder(JSContext *aCx) : mCx(aCx), mOk(true)
|
||||
{}
|
||||
|
||||
void
|
||||
JSObjectBuilder::DefineProperty(JS::HandleObject aObject, const char *name, JS::HandleObject aValue)
|
||||
{
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
mOk = JS_DefineProperty(mCx, aObject, name, aValue, JSPROP_ENUMERATE);
|
||||
}
|
||||
|
||||
void
|
||||
JSObjectBuilder::DefineProperty(JS::HandleObject aObject, const char *name, int value)
|
||||
{
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
mOk = JS_DefineProperty(mCx, aObject, name, value, JSPROP_ENUMERATE);
|
||||
}
|
||||
|
||||
void
|
||||
JSObjectBuilder::DefineProperty(JS::HandleObject aObject, const char *name, double value)
|
||||
{
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
mOk = JS_DefineProperty(mCx, aObject, name, value, JSPROP_ENUMERATE);
|
||||
}
|
||||
|
||||
void
|
||||
JSObjectBuilder::DefineProperty(JS::HandleObject aObject, const char *name, nsAString &value)
|
||||
{
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
const nsString &flat = PromiseFlatString(value);
|
||||
JS::RootedString string(mCx, JS_NewUCStringCopyN(mCx, static_cast<const jschar*>(flat.get()), flat.Length()));
|
||||
if (!string)
|
||||
mOk = false;
|
||||
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
mOk = JS_DefineProperty(mCx, aObject, name, string, JSPROP_ENUMERATE);
|
||||
}
|
||||
|
||||
void
|
||||
JSObjectBuilder::DefineProperty(JS::HandleObject aObject, const char *name, const char *value, size_t valueLength)
|
||||
{
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
JS::RootedString string(mCx, JS_InternStringN(mCx, value, valueLength));
|
||||
if (!string) {
|
||||
mOk = false;
|
||||
return;
|
||||
}
|
||||
|
||||
mOk = JS_DefineProperty(mCx, aObject, name, string, JSPROP_ENUMERATE); }
|
||||
|
||||
void
|
||||
JSObjectBuilder::DefineProperty(JS::HandleObject aObject, const char *name, const char *value)
|
||||
{
|
||||
DefineProperty(aObject, name, value, strlen(value));
|
||||
}
|
||||
|
||||
void
|
||||
JSObjectBuilder::ArrayPush(JS::HandleObject aArray, int value)
|
||||
{
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
uint32_t length;
|
||||
mOk = JS_GetArrayLength(mCx, aArray, &length);
|
||||
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
mOk = JS_SetElement(mCx, aArray, length, value);
|
||||
}
|
||||
|
||||
void
|
||||
JSObjectBuilder::ArrayPush(JS::HandleObject aArray, const char *value)
|
||||
{
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
JS::RootedString string(mCx, JS_NewStringCopyN(mCx, value, strlen(value)));
|
||||
if (!string) {
|
||||
mOk = false;
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t length;
|
||||
mOk = JS_GetArrayLength(mCx, aArray, &length);
|
||||
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
mOk = JS_SetElement(mCx, aArray, length, string);
|
||||
}
|
||||
|
||||
void
|
||||
JSObjectBuilder::ArrayPush(JS::HandleObject aArray, JS::HandleObject aObject)
|
||||
{
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
uint32_t length;
|
||||
mOk = JS_GetArrayLength(mCx, aArray, &length);
|
||||
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
mOk = JS_SetElement(mCx, aArray, length, aObject);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
JSObjectBuilder::CreateArray() {
|
||||
JSObject *array = JS_NewArrayObject(mCx, 0);
|
||||
if (!array)
|
||||
mOk = false;
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
JSObjectBuilder::CreateObject() {
|
||||
JSObject *obj = JS_NewObject(mCx, nullptr, JS::NullPtr(), JS::NullPtr());
|
||||
if (!obj)
|
||||
mOk = false;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef JSOBJECTBUILDER_H
|
||||
#define JSOBJECTBUILDER_H
|
||||
|
||||
#include "js/TypeDecls.h"
|
||||
#include "js/RootingAPI.h"
|
||||
|
||||
class JSCustomArray;
|
||||
class JSCustomObject;
|
||||
class JSCustomObjectBuilder;
|
||||
class nsAString;
|
||||
|
||||
/* this is handy wrapper around JSAPI to make it more pleasant to use.
|
||||
* We collect the JSAPI errors and so that callers don't need to */
|
||||
class JSObjectBuilder
|
||||
{
|
||||
public:
|
||||
typedef JS::Handle<JSObject*> ObjectHandle;
|
||||
typedef JS::Handle<JSObject*> ArrayHandle;
|
||||
typedef JS::Rooted<JSObject*> RootedObject;
|
||||
typedef JS::Rooted<JSObject*> RootedArray;
|
||||
typedef JSObject* Object;
|
||||
typedef JSObject* Array;
|
||||
|
||||
// We need to ensure that this object lives on the stack so that GC sees it properly
|
||||
explicit JSObjectBuilder(JSContext *aCx);
|
||||
~JSObjectBuilder() {}
|
||||
|
||||
void DefineProperty(JS::HandleObject aObject, const char *name, JS::HandleObject aValue);
|
||||
void DefineProperty(JS::HandleObject aObject, const char *name, int value);
|
||||
void DefineProperty(JS::HandleObject aObject, const char *name, double value);
|
||||
void DefineProperty(JS::HandleObject aObject, const char *name, nsAString &value);
|
||||
void DefineProperty(JS::HandleObject aObject, const char *name, const char *value, size_t valueLength);
|
||||
void DefineProperty(JS::HandleObject aObject, const char *name, const char *value);
|
||||
void ArrayPush(JS::HandleObject aArray, int value);
|
||||
void ArrayPush(JS::HandleObject aArray, const char *value);
|
||||
void ArrayPush(JS::HandleObject aArray, JS::HandleObject aObject);
|
||||
JSObject *CreateArray();
|
||||
JSObject *CreateObject();
|
||||
|
||||
JSContext *context() const { return mCx; }
|
||||
|
||||
private:
|
||||
JSObjectBuilder(const JSObjectBuilder&);
|
||||
JSObjectBuilder& operator=(const JSObjectBuilder&);
|
||||
|
||||
void* operator new(size_t);
|
||||
void* operator new[](size_t);
|
||||
void operator delete(void*) {
|
||||
// Since JSObjectBuilder has a virtual destructor the compiler
|
||||
// has to provide a destructor in the object file that will call
|
||||
// operate delete in case there is a derived class since its
|
||||
// destructor wont know how to free this instance.
|
||||
abort();
|
||||
}
|
||||
void operator delete[](void*);
|
||||
|
||||
JSContext *mCx;
|
||||
int mOk;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,180 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "JSStreamWriter.h"
|
||||
|
||||
#include "mozilla/ArrayUtils.h" // for ArrayLength
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsUTF8Utils.h"
|
||||
|
||||
#if _MSC_VER
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
#define ARRAY (void*)1
|
||||
#define OBJECT (void*)2
|
||||
|
||||
// Escape a UTF8 string to a stream. When an illegal encoding
|
||||
// is found it will insert "INVALID" and the function will return.
|
||||
static void EscapeToStream(std::ostream& stream, const char* str) {
|
||||
stream << "\"";
|
||||
|
||||
size_t len = strlen(str);
|
||||
const char* end = &str[len];
|
||||
while (str < end) {
|
||||
bool err;
|
||||
const char* utf8CharStart = str;
|
||||
uint32_t ucs4Char = UTF8CharEnumerator::NextChar(&str, end, &err);
|
||||
|
||||
if (err) {
|
||||
// Encoding error
|
||||
stream << "INVALID\"";
|
||||
return;
|
||||
}
|
||||
|
||||
// See http://www.ietf.org/rfc/rfc4627.txt?number=4627
|
||||
// characters that must be escaped: quotation mark,
|
||||
// reverse solidus, and the control characters
|
||||
// (U+0000 through U+001F).
|
||||
if (ucs4Char == '\"') {
|
||||
stream << "\\\"";
|
||||
} else if (ucs4Char == '\\') {
|
||||
stream << "\\\\";
|
||||
} else if (ucs4Char > 0xFF) {
|
||||
char16_t chr[2];
|
||||
ConvertUTF8toUTF16 encoder(chr);
|
||||
encoder.write(utf8CharStart, uint32_t(str-utf8CharStart));
|
||||
char escChar[13];
|
||||
snprintf(escChar, mozilla::ArrayLength(escChar), "\\u%04X\\u%04X", chr[0], chr[1]);
|
||||
stream << escChar;
|
||||
} else if (ucs4Char < 0x1F || ucs4Char > 0xFF) {
|
||||
char escChar[7];
|
||||
snprintf(escChar, mozilla::ArrayLength(escChar), "\\u%04X", ucs4Char);
|
||||
stream << escChar;
|
||||
} else {
|
||||
stream << char(ucs4Char);
|
||||
}
|
||||
}
|
||||
stream << "\"";
|
||||
}
|
||||
|
||||
JSStreamWriter::JSStreamWriter(std::ostream& aStream)
|
||||
: mStream(aStream)
|
||||
, mNeedsComma(false)
|
||||
, mNeedsName(false)
|
||||
{ }
|
||||
|
||||
JSStreamWriter::~JSStreamWriter()
|
||||
{
|
||||
MOZ_ASSERT(mStack.GetSize() == 0);
|
||||
}
|
||||
|
||||
void
|
||||
JSStreamWriter::BeginObject()
|
||||
{
|
||||
MOZ_ASSERT(!mNeedsName);
|
||||
if (mNeedsComma && mStack.Peek() == ARRAY) {
|
||||
mStream << ",";
|
||||
}
|
||||
mStream << "{";
|
||||
mNeedsComma = false;
|
||||
mNeedsName = true;
|
||||
mStack.Push(OBJECT);
|
||||
}
|
||||
|
||||
void
|
||||
JSStreamWriter::EndObject()
|
||||
{
|
||||
MOZ_ASSERT(mStack.Peek() == OBJECT);
|
||||
mStream << "}";
|
||||
mNeedsComma = true;
|
||||
mNeedsName = false;
|
||||
mStack.Pop();
|
||||
if (mStack.GetSize() > 0 && mStack.Peek() == OBJECT) {
|
||||
mNeedsName = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
JSStreamWriter::BeginArray()
|
||||
{
|
||||
MOZ_ASSERT(!mNeedsName);
|
||||
if (mNeedsComma && mStack.Peek() == ARRAY) {
|
||||
mStream << ",";
|
||||
}
|
||||
mStream << "[";
|
||||
mNeedsComma = false;
|
||||
mStack.Push(ARRAY);
|
||||
}
|
||||
|
||||
void
|
||||
JSStreamWriter::EndArray()
|
||||
{
|
||||
MOZ_ASSERT(!mNeedsName);
|
||||
MOZ_ASSERT(mStack.Peek() == ARRAY);
|
||||
mStream << "]";
|
||||
mNeedsComma = true;
|
||||
mStack.Pop();
|
||||
if (mStack.GetSize() > 0 && mStack.Peek() == OBJECT) {
|
||||
mNeedsName = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
JSStreamWriter::Name(const char *aName)
|
||||
{
|
||||
MOZ_ASSERT(mNeedsName);
|
||||
if (mNeedsComma && mStack.Peek() == OBJECT) {
|
||||
mStream << ",";
|
||||
}
|
||||
EscapeToStream(mStream, aName);
|
||||
mStream << ":";
|
||||
mNeedsName = false;
|
||||
}
|
||||
|
||||
void
|
||||
JSStreamWriter::Value(int aValue)
|
||||
{
|
||||
MOZ_ASSERT(!mNeedsName);
|
||||
if (mNeedsComma && mStack.Peek() == ARRAY) {
|
||||
mStream << ",";
|
||||
}
|
||||
mStream << aValue;
|
||||
mNeedsComma = true;
|
||||
if (mStack.Peek() == OBJECT) {
|
||||
mNeedsName = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
JSStreamWriter::Value(double aValue)
|
||||
{
|
||||
MOZ_ASSERT(!mNeedsName);
|
||||
if (mNeedsComma && mStack.Peek() == ARRAY) {
|
||||
mStream << ",";
|
||||
}
|
||||
mStream.precision(18);
|
||||
mStream << aValue;
|
||||
mNeedsComma = true;
|
||||
if (mStack.Peek() == OBJECT) {
|
||||
mNeedsName = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
JSStreamWriter::Value(const char *aValue)
|
||||
{
|
||||
MOZ_ASSERT(!mNeedsName);
|
||||
if (mNeedsComma && mStack.Peek() == ARRAY) {
|
||||
mStream << ",";
|
||||
}
|
||||
EscapeToStream(mStream, aValue);
|
||||
mNeedsComma = true;
|
||||
if (mStack.Peek() == OBJECT) {
|
||||
mNeedsName = true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef JSSTREAMWRITER_H
|
||||
#define JSSTREAMWRITER_H
|
||||
|
||||
#include <ostream>
|
||||
#include <stdlib.h>
|
||||
#include "nsDeque.h"
|
||||
|
||||
class JSStreamWriter
|
||||
{
|
||||
public:
|
||||
JSStreamWriter(std::ostream& aStream);
|
||||
~JSStreamWriter();
|
||||
|
||||
void BeginObject();
|
||||
void EndObject();
|
||||
void BeginArray();
|
||||
void EndArray();
|
||||
void Name(const char *name);
|
||||
void Value(int value);
|
||||
void Value(double value);
|
||||
void Value(const char *value, size_t valueLength);
|
||||
void Value(const char *value);
|
||||
template <typename T>
|
||||
void NameValue(const char *aName, T aValue)
|
||||
{
|
||||
Name(aName);
|
||||
Value(aValue);
|
||||
}
|
||||
|
||||
private:
|
||||
std::ostream& mStream;
|
||||
bool mNeedsComma;
|
||||
bool mNeedsName;
|
||||
|
||||
nsDeque mStack;
|
||||
|
||||
// This class can't be copied
|
||||
JSStreamWriter(const JSStreamWriter&);
|
||||
JSStreamWriter& operator=(const JSStreamWriter&);
|
||||
|
||||
void* operator new(size_t);
|
||||
void* operator new[](size_t);
|
||||
void operator delete(void*) {
|
||||
// Since JSStreamWriter has a virtual destructor the compiler
|
||||
// has to provide a destructor in the object file that will call
|
||||
// operate delete in case there is a derived class since its
|
||||
// destructor won't know how to free this instance.
|
||||
abort();
|
||||
}
|
||||
void operator delete[](void*);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -4,13 +4,14 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include "platform.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "jsapi.h"
|
||||
|
||||
// JSON
|
||||
#include "JSObjectBuilder.h"
|
||||
#include "JSCustomObjectBuilder.h"
|
||||
#include "JSStreamWriter.h"
|
||||
|
||||
// Self
|
||||
#include "ProfileEntry.h"
|
||||
|
@ -308,138 +309,159 @@ void ThreadProfile::IterateTags(IterateTagsCallback aCallback)
|
|||
|
||||
void ThreadProfile::ToStreamAsJSON(std::ostream& stream)
|
||||
{
|
||||
JSCustomObjectBuilder b;
|
||||
JSCustomObject *profile = b.CreateObject();
|
||||
BuildJSObject(b, profile);
|
||||
b.Serialize(profile, stream);
|
||||
b.DeleteObject(profile);
|
||||
JSStreamWriter b(stream);
|
||||
StreamJSObject(b);
|
||||
}
|
||||
|
||||
void ThreadProfile::StreamJSObject(JSStreamWriter& b)
|
||||
{
|
||||
b.BeginObject();
|
||||
// Thread meta data
|
||||
if (XRE_GetProcessType() == GeckoProcessType_Plugin) {
|
||||
// TODO Add the proper plugin name
|
||||
b.NameValue("name", "Plugin");
|
||||
} else {
|
||||
b.NameValue("name", mName);
|
||||
}
|
||||
b.NameValue("tid", static_cast<int>(mThreadId));
|
||||
|
||||
b.Name("samples");
|
||||
b.BeginArray();
|
||||
|
||||
bool sample = false;
|
||||
int readPos = mReadPos;
|
||||
while (readPos != mLastFlushPos) {
|
||||
// Number of tag consumed
|
||||
ProfileEntry entry = mEntries[readPos];
|
||||
|
||||
switch (entry.mTagName) {
|
||||
case 'r':
|
||||
{
|
||||
if (sample) {
|
||||
b.NameValue("responsiveness", entry.mTagFloat);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
{
|
||||
if (sample) {
|
||||
b.NameValue("power", entry.mTagFloat);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
{
|
||||
if (sample) {
|
||||
b.NameValue("frameNumber", entry.mTagLine);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
{
|
||||
if (sample) {
|
||||
b.NameValue("time", entry.mTagFloat);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
{
|
||||
// end the previous sample if there was one
|
||||
if (sample) {
|
||||
b.EndObject();
|
||||
}
|
||||
// begin the next sample
|
||||
b.BeginObject();
|
||||
|
||||
sample = true;
|
||||
|
||||
// Seek forward through the entire sample, looking for frames
|
||||
// this is an easier approach to reason about than adding more
|
||||
// control variables and cases to the loop that goes through the buffer once
|
||||
b.Name("frames");
|
||||
b.BeginArray();
|
||||
|
||||
b.BeginObject();
|
||||
b.NameValue("location", "(root)");
|
||||
b.EndObject();
|
||||
|
||||
int framePos = (readPos + 1) % mEntrySize;
|
||||
ProfileEntry frame = mEntries[framePos];
|
||||
while (framePos != mLastFlushPos && frame.mTagName != 's') {
|
||||
int incBy = 1;
|
||||
frame = mEntries[framePos];
|
||||
// Read ahead to the next tag, if it's a 'd' tag process it now
|
||||
const char* tagStringData = frame.mTagData;
|
||||
int readAheadPos = (framePos + 1) % mEntrySize;
|
||||
char tagBuff[DYNAMIC_MAX_STRING];
|
||||
// Make sure the string is always null terminated if it fills up
|
||||
// DYNAMIC_MAX_STRING-2
|
||||
tagBuff[DYNAMIC_MAX_STRING-1] = '\0';
|
||||
|
||||
if (readAheadPos != mLastFlushPos && mEntries[readAheadPos].mTagName == 'd') {
|
||||
tagStringData = processDynamicTag(framePos, &incBy, tagBuff);
|
||||
}
|
||||
|
||||
// Write one frame. It can have either
|
||||
// 1. only location - 'l' containing a memory address
|
||||
// 2. location and line number - 'c' followed by 'd's and an optional 'n'
|
||||
if (frame.mTagName == 'l') {
|
||||
b.BeginObject();
|
||||
// Bug 753041
|
||||
// We need a double cast here to tell GCC that we don't want to sign
|
||||
// extend 32-bit addresses starting with 0xFXXXXXX.
|
||||
unsigned long long pc = (unsigned long long)(uintptr_t)frame.mTagPtr;
|
||||
snprintf(tagBuff, DYNAMIC_MAX_STRING, "%#llx", pc);
|
||||
b.NameValue("location", tagBuff);
|
||||
b.EndObject();
|
||||
} else if (frame.mTagName == 'c') {
|
||||
b.BeginObject();
|
||||
b.NameValue("location", tagStringData);
|
||||
readAheadPos = (framePos + incBy) % mEntrySize;
|
||||
if (readAheadPos != mLastFlushPos &&
|
||||
mEntries[readAheadPos].mTagName == 'n') {
|
||||
b.NameValue("line", mEntries[readAheadPos].mTagLine);
|
||||
incBy++;
|
||||
}
|
||||
b.EndObject();
|
||||
}
|
||||
framePos = (framePos + incBy) % mEntrySize;
|
||||
}
|
||||
b.EndArray();
|
||||
}
|
||||
break;
|
||||
}
|
||||
readPos = (readPos + 1) % mEntrySize;
|
||||
}
|
||||
if (sample) {
|
||||
b.EndObject();
|
||||
}
|
||||
b.EndArray();
|
||||
|
||||
b.Name("markers");
|
||||
b.BeginArray();
|
||||
readPos = mReadPos;
|
||||
while (readPos != mLastFlushPos) {
|
||||
ProfileEntry entry = mEntries[readPos];
|
||||
if (entry.mTagName == 'm') {
|
||||
entry.getMarker()->StreamJSObject(b);
|
||||
}
|
||||
readPos = (readPos + 1) % mEntrySize;
|
||||
}
|
||||
b.EndArray();
|
||||
b.EndObject();
|
||||
}
|
||||
|
||||
JSObject* ThreadProfile::ToJSObject(JSContext *aCx)
|
||||
{
|
||||
JSObjectBuilder b(aCx);
|
||||
JS::RootedObject profile(aCx, b.CreateObject());
|
||||
BuildJSObject(b, profile);
|
||||
return profile;
|
||||
JS::RootedValue val(aCx);
|
||||
std::stringstream ss;
|
||||
JSStreamWriter b(ss);
|
||||
StreamJSObject(b);
|
||||
NS_ConvertUTF8toUTF16 js_string(nsDependentCString(ss.str().c_str()));
|
||||
JS_ParseJSON(aCx, static_cast<const jschar*>(js_string.get()), js_string.Length(), &val);
|
||||
return &val.toObject();
|
||||
}
|
||||
|
||||
template <typename Builder>
|
||||
void ThreadProfile::BuildJSObject(Builder& b,
|
||||
typename Builder::ObjectHandle profile)
|
||||
{
|
||||
// Thread meta data
|
||||
if (XRE_GetProcessType() == GeckoProcessType_Plugin) {
|
||||
// TODO Add the proper plugin name
|
||||
b.DefineProperty(profile, "name", "Plugin");
|
||||
} else {
|
||||
b.DefineProperty(profile, "name", mName);
|
||||
}
|
||||
|
||||
b.DefineProperty(profile, "tid", static_cast<int>(mThreadId));
|
||||
|
||||
typename Builder::RootedArray samples(b.context(), b.CreateArray());
|
||||
b.DefineProperty(profile, "samples", samples);
|
||||
|
||||
typename Builder::RootedArray markers(b.context(), b.CreateArray());
|
||||
b.DefineProperty(profile, "markers", markers);
|
||||
|
||||
typename Builder::RootedObject sample(b.context());
|
||||
typename Builder::RootedArray frames(b.context());
|
||||
|
||||
int readPos = mReadPos;
|
||||
while (readPos != mLastFlushPos) {
|
||||
// Number of tag consumed
|
||||
int incBy = 1;
|
||||
ProfileEntry entry = mEntries[readPos];
|
||||
|
||||
// Read ahead to the next tag, if it's a 'd' tag process it now
|
||||
const char* tagStringData = entry.mTagData;
|
||||
int readAheadPos = (readPos + 1) % mEntrySize;
|
||||
char tagBuff[DYNAMIC_MAX_STRING];
|
||||
// Make sure the string is always null terminated if it fills up
|
||||
// DYNAMIC_MAX_STRING-2
|
||||
tagBuff[DYNAMIC_MAX_STRING-1] = '\0';
|
||||
|
||||
if (readAheadPos != mLastFlushPos && mEntries[readAheadPos].mTagName == 'd') {
|
||||
tagStringData = processDynamicTag(readPos, &incBy, tagBuff);
|
||||
}
|
||||
|
||||
switch (entry.mTagName) {
|
||||
case 'm':
|
||||
{
|
||||
entry.getMarker()->BuildJSObject(b, markers);
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
{
|
||||
if (sample) {
|
||||
b.DefineProperty(sample, "responsiveness", entry.mTagFloat);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
{
|
||||
if (sample) {
|
||||
b.DefineProperty(sample, "power", entry.mTagFloat);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
{
|
||||
if (sample) {
|
||||
b.DefineProperty(sample, "frameNumber", entry.mTagLine);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
{
|
||||
if (sample) {
|
||||
b.DefineProperty(sample, "time", entry.mTagFloat);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
sample = b.CreateObject();
|
||||
b.DefineProperty(sample, "name", tagStringData);
|
||||
frames = b.CreateArray();
|
||||
b.DefineProperty(sample, "frames", frames);
|
||||
b.ArrayPush(samples, sample);
|
||||
// Fall though to create a label for the 's' tag
|
||||
case 'c':
|
||||
case 'l':
|
||||
{
|
||||
if (sample) {
|
||||
typename Builder::RootedObject frame(b.context(), b.CreateObject());
|
||||
if (entry.mTagName == 'l') {
|
||||
// Bug 753041
|
||||
// We need a double cast here to tell GCC that we don't want to sign
|
||||
// extend 32-bit addresses starting with 0xFXXXXXX.
|
||||
unsigned long long pc = (unsigned long long)(uintptr_t)entry.mTagPtr;
|
||||
snprintf(tagBuff, DYNAMIC_MAX_STRING, "%#llx", pc);
|
||||
b.DefineProperty(frame, "location", tagBuff);
|
||||
} else {
|
||||
b.DefineProperty(frame, "location", tagStringData);
|
||||
readAheadPos = (readPos + incBy) % mEntrySize;
|
||||
if (readAheadPos != mLastFlushPos &&
|
||||
mEntries[readAheadPos].mTagName == 'n') {
|
||||
b.DefineProperty(frame, "line",
|
||||
mEntries[readAheadPos].mTagLine);
|
||||
incBy++;
|
||||
}
|
||||
}
|
||||
b.ArrayPush(frames, frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
readPos = (readPos + incBy) % mEntrySize;
|
||||
}
|
||||
}
|
||||
|
||||
template void ThreadProfile::BuildJSObject<JSObjectBuilder>(JSObjectBuilder& b,
|
||||
JS::HandleObject profile);
|
||||
template void ThreadProfile::BuildJSObject<JSCustomObjectBuilder>(JSCustomObjectBuilder& b,
|
||||
JSCustomObject *profile);
|
||||
|
||||
PseudoStack* ThreadProfile::GetPseudoStack()
|
||||
{
|
||||
return mPseudoStack;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <ostream>
|
||||
#include "GeckoProfiler.h"
|
||||
#include "platform.h"
|
||||
#include "JSStreamWriter.h"
|
||||
#include "ProfilerBacktrace.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
|
||||
|
@ -82,7 +83,7 @@ public:
|
|||
JSObject *ToJSObject(JSContext *aCx);
|
||||
PseudoStack* GetPseudoStack();
|
||||
mozilla::Mutex* GetMutex();
|
||||
template <typename Builder> void BuildJSObject(Builder& b, typename Builder::ObjectHandle profile);
|
||||
void StreamJSObject(JSStreamWriter& b);
|
||||
void BeginUnwind();
|
||||
virtual void EndUnwind();
|
||||
virtual SyncProfile* AsSyncProfile() { return nullptr; }
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "JSCustomObjectBuilder.h"
|
||||
#include "JSObjectBuilder.h"
|
||||
#include "JSStreamWriter.h"
|
||||
#include "ProfilerBacktrace.h"
|
||||
#include "SyncProfile.h"
|
||||
|
||||
|
||||
ProfilerBacktrace::ProfilerBacktrace(SyncProfile* aProfile)
|
||||
: mProfile(aProfile)
|
||||
{
|
||||
|
@ -22,18 +22,9 @@ ProfilerBacktrace::~ProfilerBacktrace()
|
|||
}
|
||||
}
|
||||
|
||||
template<typename Builder> void
|
||||
ProfilerBacktrace::BuildJSObject(Builder& aObjBuilder,
|
||||
typename Builder::ObjectHandle aScope)
|
||||
void
|
||||
ProfilerBacktrace::StreamJSObject(JSStreamWriter& b)
|
||||
{
|
||||
mozilla::MutexAutoLock lock(*mProfile->GetMutex());
|
||||
mProfile->BuildJSObject(aObjBuilder, aScope);
|
||||
mProfile->StreamJSObject(b);
|
||||
}
|
||||
|
||||
template void
|
||||
ProfilerBacktrace::BuildJSObject<JSCustomObjectBuilder>(
|
||||
JSCustomObjectBuilder& aObjBuilder,
|
||||
JSCustomObjectBuilder::ObjectHandle aScope);
|
||||
template void
|
||||
ProfilerBacktrace::BuildJSObject<JSObjectBuilder>(JSObjectBuilder& aObjBuilder,
|
||||
JSObjectBuilder::ObjectHandle aScope);
|
||||
|
|
|
@ -15,8 +15,7 @@ public:
|
|||
ProfilerBacktrace(SyncProfile* aProfile);
|
||||
~ProfilerBacktrace();
|
||||
|
||||
template<typename Builder> void
|
||||
BuildJSObject(Builder& aObjBuilder, typename Builder::ObjectHandle aScope);
|
||||
void StreamJSObject(JSStreamWriter& b);
|
||||
|
||||
private:
|
||||
ProfilerBacktrace(const ProfilerBacktrace&);
|
||||
|
|
|
@ -26,89 +26,62 @@ ProfilerMarkerPayload::~ProfilerMarkerPayload()
|
|||
profiler_free_backtrace(mStack);
|
||||
}
|
||||
|
||||
template<typename Builder> void
|
||||
ProfilerMarkerPayload::prepareCommonProps(const char* aMarkerType,
|
||||
Builder& aBuilder,
|
||||
typename Builder::ObjectHandle aObject)
|
||||
void
|
||||
ProfilerMarkerPayload::streamCommonProps(const char* aMarkerType,
|
||||
JSStreamWriter& b)
|
||||
{
|
||||
MOZ_ASSERT(aMarkerType);
|
||||
aBuilder.DefineProperty(aObject, "type", aMarkerType);
|
||||
b.NameValue("type", aMarkerType);
|
||||
if (!mStartTime.IsNull()) {
|
||||
aBuilder.DefineProperty(aObject, "startTime", profiler_time(mStartTime));
|
||||
b.NameValue("startTime", profiler_time(mStartTime));
|
||||
}
|
||||
if (!mEndTime.IsNull()) {
|
||||
aBuilder.DefineProperty(aObject, "endTime", profiler_time(mEndTime));
|
||||
b.NameValue("endTime", profiler_time(mEndTime));
|
||||
}
|
||||
if (mStack) {
|
||||
typename Builder::RootedObject stack(aBuilder.context(),
|
||||
aBuilder.CreateObject());
|
||||
aBuilder.DefineProperty(aObject, "stack", stack);
|
||||
mStack->BuildJSObject(aBuilder, stack);
|
||||
b.Name("stack");
|
||||
mStack->StreamJSObject(b);
|
||||
}
|
||||
}
|
||||
|
||||
template void
|
||||
ProfilerMarkerPayload::prepareCommonProps<JSCustomObjectBuilder>(
|
||||
const char* aMarkerType,
|
||||
JSCustomObjectBuilder& b,
|
||||
JSCustomObjectBuilder::ObjectHandle aObject);
|
||||
template void
|
||||
ProfilerMarkerPayload::prepareCommonProps<JSObjectBuilder>(
|
||||
const char* aMarkerType,
|
||||
JSObjectBuilder& b,
|
||||
JSObjectBuilder::ObjectHandle aObject);
|
||||
|
||||
ProfilerMarkerTracing::ProfilerMarkerTracing(const char* aCategory, TracingMetadata aMetaData)
|
||||
: mCategory(aCategory)
|
||||
, mMetaData(aMetaData)
|
||||
{}
|
||||
|
||||
template<typename Builder>
|
||||
typename Builder::Object
|
||||
ProfilerMarkerTracing::preparePayloadImp(Builder& b)
|
||||
void
|
||||
ProfilerMarkerTracing::streamPayloadImp(JSStreamWriter& b)
|
||||
{
|
||||
typename Builder::RootedObject data(b.context(), b.CreateObject());
|
||||
prepareCommonProps("tracing", b, data);
|
||||
b.BeginObject();
|
||||
streamCommonProps("tracing", b);
|
||||
|
||||
if (GetCategory()) {
|
||||
b.DefineProperty(data, "category", GetCategory());
|
||||
}
|
||||
if (GetMetaData() != TRACING_DEFAULT) {
|
||||
if (GetMetaData() == TRACING_INTERVAL_START) {
|
||||
b.DefineProperty(data, "interval", "start");
|
||||
} else if (GetMetaData() == TRACING_INTERVAL_END) {
|
||||
b.DefineProperty(data, "interval", "end");
|
||||
if (GetCategory()) {
|
||||
b.NameValue("category", GetCategory());
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
if (GetMetaData() != TRACING_DEFAULT) {
|
||||
if (GetMetaData() == TRACING_INTERVAL_START) {
|
||||
b.NameValue("interval", "start");
|
||||
} else if (GetMetaData() == TRACING_INTERVAL_END) {
|
||||
b.NameValue("interval", "end");
|
||||
}
|
||||
}
|
||||
b.EndObject();
|
||||
}
|
||||
|
||||
template JSCustomObjectBuilder::Object
|
||||
ProfilerMarkerTracing::preparePayloadImp<JSCustomObjectBuilder>(JSCustomObjectBuilder& b);
|
||||
template JSObjectBuilder::Object
|
||||
ProfilerMarkerTracing::preparePayloadImp<JSObjectBuilder>(JSObjectBuilder& b);
|
||||
|
||||
ProfilerMarkerImagePayload::ProfilerMarkerImagePayload(gfxASurface *aImg)
|
||||
: mImg(aImg)
|
||||
{}
|
||||
|
||||
template<typename Builder>
|
||||
typename Builder::Object
|
||||
ProfilerMarkerImagePayload::preparePayloadImp(Builder& b)
|
||||
void
|
||||
ProfilerMarkerImagePayload::streamPayloadImp(JSStreamWriter& b)
|
||||
{
|
||||
typename Builder::RootedObject data(b.context(), b.CreateObject());
|
||||
prepareCommonProps("innerHTML", b, data);
|
||||
// TODO: Finish me
|
||||
//b.DefineProperty(data, "innerHTML", "<img src=''/>");
|
||||
return data;
|
||||
b.BeginObject();
|
||||
streamCommonProps("innerHTML", b);
|
||||
// TODO: Finish me
|
||||
//b.NameValue("innerHTML", "<img src=''/>");
|
||||
b.EndObject();
|
||||
}
|
||||
|
||||
template JSCustomObjectBuilder::Object
|
||||
ProfilerMarkerImagePayload::preparePayloadImp<JSCustomObjectBuilder>(JSCustomObjectBuilder& b);
|
||||
template JSObjectBuilder::Object
|
||||
ProfilerMarkerImagePayload::preparePayloadImp<JSObjectBuilder>(JSObjectBuilder& b);
|
||||
|
||||
IOMarkerPayload::IOMarkerPayload(const char* aSource,
|
||||
const char* aFilename,
|
||||
const mozilla::TimeStamp& aStartTime,
|
||||
|
@ -125,23 +98,18 @@ IOMarkerPayload::~IOMarkerPayload(){
|
|||
free(mFilename);
|
||||
}
|
||||
|
||||
template<typename Builder> typename Builder::Object
|
||||
IOMarkerPayload::preparePayloadImp(Builder& b)
|
||||
void
|
||||
IOMarkerPayload::streamPayloadImp(JSStreamWriter& b)
|
||||
{
|
||||
typename Builder::RootedObject data(b.context(), b.CreateObject());
|
||||
prepareCommonProps("io", b, data);
|
||||
b.DefineProperty(data, "source", mSource);
|
||||
if (mFilename != nullptr) {
|
||||
b.DefineProperty(data, "filename", mFilename);
|
||||
}
|
||||
|
||||
return data;
|
||||
b.BeginObject();
|
||||
streamCommonProps("io", b);
|
||||
b.NameValue("source", mSource);
|
||||
if (mFilename != nullptr) {
|
||||
b.NameValue("filename", mFilename);
|
||||
}
|
||||
b.EndObject();
|
||||
}
|
||||
|
||||
template JSCustomObjectBuilder::Object
|
||||
IOMarkerPayload::preparePayloadImp<JSCustomObjectBuilder>(JSCustomObjectBuilder& b);
|
||||
template JSObjectBuilder::Object
|
||||
IOMarkerPayload::preparePayloadImp<JSObjectBuilder>(JSObjectBuilder& b);
|
||||
|
||||
void
|
||||
ProfilerJSEventMarker(const char *event)
|
||||
|
|
|
@ -6,8 +6,7 @@
|
|||
#ifndef PROFILER_MARKERS_H
|
||||
#define PROFILER_MARKERS_H
|
||||
|
||||
#include "JSCustomObjectBuilder.h"
|
||||
#include "JSObjectBuilder.h"
|
||||
#include "JSStreamWriter.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
|
@ -42,31 +41,21 @@ public:
|
|||
/**
|
||||
* Called from the main thread
|
||||
*/
|
||||
template<typename Builder>
|
||||
typename Builder::Object PreparePayload(Builder& b)
|
||||
{
|
||||
return preparePayload(b);
|
||||
void StreamPayload(JSStreamWriter& b) {
|
||||
return streamPayload(b);
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Called from the main thread
|
||||
*/
|
||||
template<typename Builder>
|
||||
void prepareCommonProps(const char* aMarkerType, Builder& aBuilder,
|
||||
typename Builder::ObjectHandle aObject);
|
||||
void streamCommonProps(const char* aMarkerType, JSStreamWriter& b);
|
||||
|
||||
/**
|
||||
* Called from the main thread
|
||||
*/
|
||||
virtual JSCustomObjectBuilder::Object
|
||||
preparePayload(JSCustomObjectBuilder& b) = 0;
|
||||
|
||||
/**
|
||||
* Called from the main thread
|
||||
*/
|
||||
virtual JSObjectBuilder::Object
|
||||
preparePayload(JSObjectBuilder& b) = 0;
|
||||
virtual void
|
||||
streamPayload(JSStreamWriter& b) = 0;
|
||||
|
||||
private:
|
||||
mozilla::TimeStamp mStartTime;
|
||||
|
@ -83,14 +72,11 @@ public:
|
|||
TracingMetadata GetMetaData() const { return mMetaData; }
|
||||
|
||||
protected:
|
||||
virtual JSCustomObjectBuilder::Object
|
||||
preparePayload(JSCustomObjectBuilder& b) { return preparePayloadImp(b); }
|
||||
virtual JSObjectBuilder::Object
|
||||
preparePayload(JSObjectBuilder& b) { return preparePayloadImp(b); }
|
||||
virtual void
|
||||
streamPayload(JSStreamWriter& b) { return streamPayloadImp(b); }
|
||||
|
||||
private:
|
||||
template<typename Builder>
|
||||
typename Builder::Object preparePayloadImp(Builder& b);
|
||||
void streamPayloadImp(JSStreamWriter& b);
|
||||
|
||||
private:
|
||||
const char *mCategory;
|
||||
|
@ -105,14 +91,11 @@ public:
|
|||
ProfilerMarkerImagePayload(gfxASurface *aImg);
|
||||
|
||||
protected:
|
||||
virtual JSCustomObjectBuilder::Object
|
||||
preparePayload(JSCustomObjectBuilder& b) { return preparePayloadImp(b); }
|
||||
virtual JSObjectBuilder::Object
|
||||
preparePayload(JSObjectBuilder& b) { return preparePayloadImp(b); }
|
||||
virtual void
|
||||
streamPayload(JSStreamWriter& b) { return streamPayloadImp(b); }
|
||||
|
||||
private:
|
||||
template<typename Builder>
|
||||
typename Builder::Object preparePayloadImp(Builder& b);
|
||||
void streamPayloadImp(JSStreamWriter& b);
|
||||
|
||||
nsRefPtr<gfxASurface> mImg;
|
||||
};
|
||||
|
@ -126,14 +109,11 @@ public:
|
|||
~IOMarkerPayload();
|
||||
|
||||
protected:
|
||||
virtual JSCustomObjectBuilder::Object
|
||||
preparePayload(JSCustomObjectBuilder& b) { return preparePayloadImp(b); }
|
||||
virtual JSObjectBuilder::Object
|
||||
preparePayload(JSObjectBuilder& b) { return preparePayloadImp(b); }
|
||||
virtual void
|
||||
streamPayload(JSStreamWriter& b) { return streamPayloadImp(b); }
|
||||
|
||||
private:
|
||||
template<typename Builder>
|
||||
typename Builder::Object preparePayloadImp(Builder& b);
|
||||
void streamPayloadImp(JSStreamWriter& b);
|
||||
|
||||
const char* mSource;
|
||||
char* mFilename;
|
||||
|
|
|
@ -110,7 +110,7 @@ public:
|
|||
class ProfilerMarkerPayload;
|
||||
template<typename T>
|
||||
class ProfilerLinkedList;
|
||||
class JSAObjectBuilder;
|
||||
class JSStreamWriter;
|
||||
class JSCustomArray;
|
||||
class ThreadProfile;
|
||||
class ProfilerMarker {
|
||||
|
@ -126,8 +126,8 @@ public:
|
|||
return mMarkerName;
|
||||
}
|
||||
|
||||
template<typename Builder> void
|
||||
BuildJSObject(Builder& b, typename Builder::ArrayHandle markers) const;
|
||||
void
|
||||
StreamJSObject(JSStreamWriter& b) const;
|
||||
|
||||
void SetGeneration(int aGenID);
|
||||
|
||||
|
|
|
@ -6,15 +6,6 @@
|
|||
#include "SaveProfileTask.h"
|
||||
#include "GeckoProfiler.h"
|
||||
|
||||
static bool
|
||||
WriteCallback(const jschar *buf, uint32_t len, void *data)
|
||||
{
|
||||
std::ofstream& stream = *static_cast<std::ofstream*>(data);
|
||||
nsAutoCString profile = NS_ConvertUTF16toUTF8(buf, len);
|
||||
stream << profile.Data();
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult
|
||||
SaveProfileTask::Run() {
|
||||
// Get file path
|
||||
|
@ -39,48 +30,7 @@ SaveProfileTask::Run() {
|
|||
return rv;
|
||||
#endif
|
||||
|
||||
// Create a JSContext to run a JSObjectBuilder :(
|
||||
// Based on XPCShellEnvironment
|
||||
JSRuntime *rt;
|
||||
JSContext *cx;
|
||||
nsCOMPtr<nsIJSRuntimeService> rtsvc
|
||||
= do_GetService("@mozilla.org/js/xpc/RuntimeService;1");
|
||||
if (!rtsvc || NS_FAILED(rtsvc->GetRuntime(&rt)) || !rt) {
|
||||
LOG("failed to get RuntimeService");
|
||||
return NS_ERROR_FAILURE;;
|
||||
}
|
||||
|
||||
cx = JS_NewContext(rt, 8192);
|
||||
if (!cx) {
|
||||
LOG("Failed to get context");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
{
|
||||
JSAutoRequest ar(cx);
|
||||
static const JSClass c = {
|
||||
"global", JSCLASS_GLOBAL_FLAGS,
|
||||
JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
|
||||
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub,
|
||||
nullptr, nullptr, nullptr, nullptr,
|
||||
JS_GlobalObjectTraceHook
|
||||
};
|
||||
JSObject *obj = JS_NewGlobalObject(cx, &c, nullptr, JS::FireOnNewGlobalHook);
|
||||
|
||||
std::ofstream stream;
|
||||
stream.open(tmpPath.get());
|
||||
if (stream.is_open()) {
|
||||
JSAutoCompartment autoComp(cx, obj);
|
||||
JSObject* profileObj = profiler_get_profile_jsobject(cx);
|
||||
JS::Rooted<JS::Value> val(cx, OBJECT_TO_JSVAL(profileObj));
|
||||
JS_Stringify(cx, &val, JS::NullPtr(), JS::NullHandleValue, WriteCallback, &stream);
|
||||
stream.close();
|
||||
LOGF("Saved to %s", tmpPath.get());
|
||||
} else {
|
||||
LOG("Fail to open profile log file.");
|
||||
}
|
||||
}
|
||||
JS_DestroyContext(cx);
|
||||
profiler_save_profile_to_file(tmpPath.get());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -15,9 +15,6 @@
|
|||
#include "nsIJSRuntimeService.h"
|
||||
#include "nsIProfileSaveEvent.h"
|
||||
|
||||
#include <ostream>
|
||||
#include <fstream>
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include <windows.h>
|
||||
#define getpid GetCurrentProcessId
|
||||
|
|
|
@ -21,8 +21,7 @@
|
|||
#include "nsXULAppAPI.h"
|
||||
|
||||
// JSON
|
||||
#include "JSObjectBuilder.h"
|
||||
#include "JSCustomObjectBuilder.h"
|
||||
#include "JSStreamWriter.h"
|
||||
|
||||
// Meta
|
||||
#include "nsXPCOM.h"
|
||||
|
@ -105,203 +104,209 @@ void TableTicker::HandleSaveRequest()
|
|||
NS_DispatchToMainThread(runnable);
|
||||
}
|
||||
|
||||
template <typename Builder>
|
||||
typename Builder::Object TableTicker::GetMetaJSCustomObject(Builder& b)
|
||||
void TableTicker::StreamMetaJSCustomObject(JSStreamWriter& b)
|
||||
{
|
||||
typename Builder::RootedObject meta(b.context(), b.CreateObject());
|
||||
b.BeginObject();
|
||||
|
||||
b.DefineProperty(meta, "version", 2);
|
||||
b.DefineProperty(meta, "interval", interval());
|
||||
b.DefineProperty(meta, "stackwalk", mUseStackWalk);
|
||||
b.DefineProperty(meta, "jank", mJankOnly);
|
||||
b.DefineProperty(meta, "processType", XRE_GetProcessType());
|
||||
b.NameValue("version", 2);
|
||||
b.NameValue("interval", interval());
|
||||
b.NameValue("stackwalk", mUseStackWalk);
|
||||
b.NameValue("jank", mJankOnly);
|
||||
b.NameValue("processType", XRE_GetProcessType());
|
||||
|
||||
TimeDuration delta = TimeStamp::Now() - sStartTime;
|
||||
b.DefineProperty(meta, "startTime", static_cast<float>(PR_Now()/1000.0 - delta.ToMilliseconds()));
|
||||
TimeDuration delta = TimeStamp::Now() - sStartTime;
|
||||
b.NameValue("startTime", static_cast<float>(PR_Now()/1000.0 - delta.ToMilliseconds()));
|
||||
|
||||
nsresult res;
|
||||
nsCOMPtr<nsIHttpProtocolHandler> http = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &res);
|
||||
if (!NS_FAILED(res)) {
|
||||
nsAutoCString string;
|
||||
nsresult res;
|
||||
nsCOMPtr<nsIHttpProtocolHandler> http = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &res);
|
||||
if (!NS_FAILED(res)) {
|
||||
nsAutoCString string;
|
||||
|
||||
res = http->GetPlatform(string);
|
||||
if (!NS_FAILED(res))
|
||||
b.DefineProperty(meta, "platform", string.Data());
|
||||
res = http->GetPlatform(string);
|
||||
if (!NS_FAILED(res))
|
||||
b.NameValue("platform", string.Data());
|
||||
|
||||
res = http->GetOscpu(string);
|
||||
if (!NS_FAILED(res))
|
||||
b.DefineProperty(meta, "oscpu", string.Data());
|
||||
res = http->GetOscpu(string);
|
||||
if (!NS_FAILED(res))
|
||||
b.NameValue("oscpu", string.Data());
|
||||
|
||||
res = http->GetMisc(string);
|
||||
if (!NS_FAILED(res))
|
||||
b.DefineProperty(meta, "misc", string.Data());
|
||||
}
|
||||
res = http->GetMisc(string);
|
||||
if (!NS_FAILED(res))
|
||||
b.NameValue("misc", string.Data());
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
|
||||
if (runtime) {
|
||||
nsAutoCString string;
|
||||
nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
|
||||
if (runtime) {
|
||||
nsAutoCString string;
|
||||
|
||||
res = runtime->GetXPCOMABI(string);
|
||||
if (!NS_FAILED(res))
|
||||
b.DefineProperty(meta, "abi", string.Data());
|
||||
res = runtime->GetXPCOMABI(string);
|
||||
if (!NS_FAILED(res))
|
||||
b.NameValue("abi", string.Data());
|
||||
|
||||
res = runtime->GetWidgetToolkit(string);
|
||||
if (!NS_FAILED(res))
|
||||
b.DefineProperty(meta, "toolkit", string.Data());
|
||||
}
|
||||
res = runtime->GetWidgetToolkit(string);
|
||||
if (!NS_FAILED(res))
|
||||
b.NameValue("toolkit", string.Data());
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIXULAppInfo> appInfo = do_GetService("@mozilla.org/xre/app-info;1");
|
||||
if (appInfo) {
|
||||
nsAutoCString string;
|
||||
nsCOMPtr<nsIXULAppInfo> appInfo = do_GetService("@mozilla.org/xre/app-info;1");
|
||||
if (appInfo) {
|
||||
nsAutoCString string;
|
||||
|
||||
res = appInfo->GetName(string);
|
||||
if (!NS_FAILED(res))
|
||||
b.DefineProperty(meta, "product", string.Data());
|
||||
}
|
||||
res = appInfo->GetName(string);
|
||||
if (!NS_FAILED(res))
|
||||
b.NameValue("product", string.Data());
|
||||
}
|
||||
|
||||
return meta;
|
||||
b.EndObject();
|
||||
}
|
||||
|
||||
void TableTicker::ToStreamAsJSON(std::ostream& stream)
|
||||
{
|
||||
JSCustomObjectBuilder b;
|
||||
JSCustomObject* profile = b.CreateObject();
|
||||
BuildJSObject(b, profile);
|
||||
b.Serialize(profile, stream);
|
||||
b.DeleteObject(profile);
|
||||
JSStreamWriter b(stream);
|
||||
StreamJSObject(b);
|
||||
}
|
||||
|
||||
JSObject* TableTicker::ToJSObject(JSContext *aCx)
|
||||
{
|
||||
JSObjectBuilder b(aCx);
|
||||
JS::RootedObject profile(aCx, b.CreateObject());
|
||||
BuildJSObject(b, profile);
|
||||
return profile;
|
||||
JS::RootedValue val(aCx);
|
||||
std::stringstream ss;
|
||||
JSStreamWriter b(ss);
|
||||
StreamJSObject(b);
|
||||
NS_ConvertUTF8toUTF16 js_string(nsDependentCString(ss.str().c_str()));
|
||||
JS_ParseJSON(aCx, static_cast<const jschar*>(js_string.get()), js_string.Length(), &val);
|
||||
return &val.toObject();
|
||||
}
|
||||
|
||||
template <typename Builder>
|
||||
struct SubprocessClosure {
|
||||
SubprocessClosure(Builder *aBuilder, typename Builder::ArrayHandle aThreads)
|
||||
: mBuilder(aBuilder), mThreads(aThreads)
|
||||
SubprocessClosure(JSStreamWriter *aWriter)
|
||||
: mWriter(aWriter)
|
||||
{}
|
||||
|
||||
Builder* mBuilder;
|
||||
typename Builder::ArrayHandle mThreads;
|
||||
JSStreamWriter* mWriter;
|
||||
};
|
||||
|
||||
template <typename Builder>
|
||||
void SubProcessCallback(const char* aProfile, void* aClosure)
|
||||
{
|
||||
// Called by the observer to get their profile data included
|
||||
// as a sub profile
|
||||
SubprocessClosure<Builder>* closure = (SubprocessClosure<Builder>*)aClosure;
|
||||
SubprocessClosure* closure = (SubprocessClosure*)aClosure;
|
||||
|
||||
closure->mBuilder->ArrayPush(closure->mThreads, aProfile);
|
||||
// Add the string profile into the profile
|
||||
closure->mWriter->Value(aProfile);
|
||||
}
|
||||
|
||||
|
||||
#if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
|
||||
template <typename Builder>
|
||||
static
|
||||
typename Builder::Object BuildJavaThreadJSObject(Builder& b)
|
||||
void BuildJavaThreadJSObject(JSStreamWriter& b)
|
||||
{
|
||||
typename Builder::RootedObject javaThread(b.context(), b.CreateObject());
|
||||
b.DefineProperty(javaThread, "name", "Java Main Thread");
|
||||
b.BeginObject();
|
||||
|
||||
typename Builder::RootedArray samples(b.context(), b.CreateArray());
|
||||
b.DefineProperty(javaThread, "samples", samples);
|
||||
b.NameValue("name", "Java Main Thread");
|
||||
|
||||
int sampleId = 0;
|
||||
while (true) {
|
||||
int frameId = 0;
|
||||
typename Builder::RootedObject sample(b.context());
|
||||
typename Builder::RootedArray frames(b.context());
|
||||
while (true) {
|
||||
nsCString result;
|
||||
bool hasFrame = AndroidBridge::Bridge()->GetFrameNameJavaProfiling(0, sampleId, frameId, result);
|
||||
if (!hasFrame) {
|
||||
if (frames) {
|
||||
b.DefineProperty(sample, "frames", frames);
|
||||
b.Name("samples");
|
||||
b.BeginArray();
|
||||
|
||||
// for each sample
|
||||
for (int sampleId = 0; true; sampleId++) {
|
||||
bool firstRun = true;
|
||||
// for each frame
|
||||
for (int frameId = 0; true; frameId++) {
|
||||
nsCString result;
|
||||
bool hasFrame = AndroidBridge::Bridge()->GetFrameNameJavaProfiling(0, sampleId, frameId, result);
|
||||
// when we run out of frames, we stop looping
|
||||
if (!hasFrame) {
|
||||
// if we found at least one frame, we have objects to close
|
||||
if (!firstRun) {
|
||||
b.EndArray();
|
||||
b.EndObject();
|
||||
}
|
||||
break;
|
||||
}
|
||||
// the first time around, open the sample object and frames array
|
||||
if (firstRun) {
|
||||
firstRun = false;
|
||||
|
||||
double sampleTime =
|
||||
mozilla::widget::android::GeckoJavaSampler::GetSampleTimeJavaProfiling(0, sampleId);
|
||||
|
||||
b.BeginObject();
|
||||
b.NameValue("time", sampleTime);
|
||||
|
||||
b.Name("frames");
|
||||
b.BeginArray();
|
||||
}
|
||||
// add a frame to the sample
|
||||
b.BeginObject();
|
||||
b.NameValue("location", result.BeginReading());
|
||||
b.EndObject();
|
||||
}
|
||||
// if we found no frames for this sample, we are done
|
||||
if (firstRun) {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!sample) {
|
||||
sample = b.CreateObject();
|
||||
frames = b.CreateArray();
|
||||
b.DefineProperty(sample, "frames", frames);
|
||||
b.ArrayPush(samples, sample);
|
||||
|
||||
double sampleTime =
|
||||
mozilla::widget::android::GeckoJavaSampler::GetSampleTimeJavaProfiling(0, sampleId);
|
||||
b.DefineProperty(sample, "time", sampleTime);
|
||||
}
|
||||
typename Builder::RootedObject frame(b.context(), b.CreateObject());
|
||||
b.DefineProperty(frame, "location", result.BeginReading());
|
||||
b.ArrayPush(frames, frame);
|
||||
frameId++;
|
||||
}
|
||||
if (frameId == 0) {
|
||||
break;
|
||||
}
|
||||
sampleId++;
|
||||
}
|
||||
b.EndArray();
|
||||
|
||||
return javaThread;
|
||||
b.EndObject();
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename Builder>
|
||||
void TableTicker::BuildJSObject(Builder& b, typename Builder::ObjectHandle profile)
|
||||
void TableTicker::StreamJSObject(JSStreamWriter& b)
|
||||
{
|
||||
// Put shared library info
|
||||
b.DefineProperty(profile, "libs", GetSharedLibraryInfoString().c_str());
|
||||
b.BeginObject();
|
||||
// Put shared library info
|
||||
b.NameValue("libs", GetSharedLibraryInfoString().c_str());
|
||||
|
||||
// Put meta data
|
||||
typename Builder::RootedObject meta(b.context(), GetMetaJSCustomObject(b));
|
||||
b.DefineProperty(profile, "meta", meta);
|
||||
// Put meta data
|
||||
b.Name("meta");
|
||||
StreamMetaJSCustomObject(b);
|
||||
|
||||
// Lists the samples for each ThreadProfile
|
||||
typename Builder::RootedArray threads(b.context(), b.CreateArray());
|
||||
b.DefineProperty(profile, "threads", threads);
|
||||
// Lists the samples for each ThreadProfile
|
||||
b.Name("threads");
|
||||
b.BeginArray();
|
||||
|
||||
SetPaused(true);
|
||||
SetPaused(true);
|
||||
|
||||
{
|
||||
mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex);
|
||||
{
|
||||
mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex);
|
||||
|
||||
for (size_t i = 0; i < sRegisteredThreads->size(); i++) {
|
||||
// Thread not being profiled, skip it
|
||||
if (!sRegisteredThreads->at(i)->Profile())
|
||||
continue;
|
||||
for (size_t i = 0; i < sRegisteredThreads->size(); i++) {
|
||||
// Thread not being profiled, skip it
|
||||
if (!sRegisteredThreads->at(i)->Profile())
|
||||
continue;
|
||||
|
||||
MutexAutoLock lock(*sRegisteredThreads->at(i)->Profile()->GetMutex());
|
||||
MutexAutoLock lock(*sRegisteredThreads->at(i)->Profile()->GetMutex());
|
||||
|
||||
typename Builder::RootedObject threadSamples(b.context(), b.CreateObject());
|
||||
sRegisteredThreads->at(i)->Profile()->BuildJSObject(b, threadSamples);
|
||||
b.ArrayPush(threads, threadSamples);
|
||||
}
|
||||
}
|
||||
sRegisteredThreads->at(i)->Profile()->StreamJSObject(b);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
|
||||
if (ProfileJava()) {
|
||||
mozilla::widget::android::GeckoJavaSampler::PauseJavaProfiling();
|
||||
// Send a event asking any subprocesses (plugins) to
|
||||
// give us their information
|
||||
SubprocessClosure closure(&b);
|
||||
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
|
||||
if (os) {
|
||||
nsRefPtr<ProfileSaveEvent> pse = new ProfileSaveEvent(SubProcessCallback, &closure);
|
||||
os->NotifyObservers(pse, "profiler-subprocess", nullptr);
|
||||
}
|
||||
|
||||
typename Builder::RootedObject javaThread(b.context(), BuildJavaThreadJSObject(b));
|
||||
b.ArrayPush(threads, javaThread);
|
||||
#if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
|
||||
if (ProfileJava()) {
|
||||
mozilla::widget::android::GeckoJavaSampler::PauseJavaProfiling();
|
||||
|
||||
mozilla::widget::android::GeckoJavaSampler::UnpauseJavaProfiling();
|
||||
}
|
||||
#endif
|
||||
BuildJavaThreadJSObject(b);
|
||||
|
||||
SetPaused(false);
|
||||
mozilla::widget::android::GeckoJavaSampler::UnpauseJavaProfiling();
|
||||
}
|
||||
#endif
|
||||
|
||||
SetPaused(false);
|
||||
b.EndArray();
|
||||
|
||||
b.EndObject();
|
||||
|
||||
// Send a event asking any subprocesses (plugins) to
|
||||
// give us their information
|
||||
SubprocessClosure<Builder> closure(&b, threads);
|
||||
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
|
||||
if (os) {
|
||||
nsRefPtr<ProfileSaveEvent> pse = new ProfileSaveEvent(SubProcessCallback<Builder>, &closure);
|
||||
os->NotifyObservers(pse, "profiler-subprocess", nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
// END SaveProfileTask et al
|
||||
|
|
|
@ -176,8 +176,7 @@ class TableTicker: public Sampler {
|
|||
|
||||
void ToStreamAsJSON(std::ostream& stream);
|
||||
virtual JSObject *ToJSObject(JSContext *aCx);
|
||||
template <typename Builder> typename Builder::Object GetMetaJSCustomObject(Builder& b);
|
||||
|
||||
void StreamMetaJSCustomObject(JSStreamWriter& b);
|
||||
bool HasUnwinderThread() const { return mUnwinderThread; }
|
||||
bool ProfileJS() const { return mProfileJS; }
|
||||
bool ProfileJava() const { return mProfileJava; }
|
||||
|
@ -196,7 +195,7 @@ protected:
|
|||
// Not implemented on platforms which do not support backtracing
|
||||
void doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample);
|
||||
|
||||
template <typename Builder> void BuildJSObject(Builder& b, typename Builder::ObjectHandle profile);
|
||||
void StreamJSObject(JSStreamWriter& b);
|
||||
|
||||
// This represent the application's main thread (SAMPLER_INIT)
|
||||
ThreadProfile* mPrimaryThreadProfile;
|
||||
|
|
|
@ -24,8 +24,7 @@ if CONFIG['MOZ_ENABLE_PROFILER_SPS']:
|
|||
]
|
||||
UNIFIED_SOURCES += [
|
||||
'BreakpadSampler.cpp',
|
||||
'JSCustomObjectBuilder.cpp',
|
||||
'JSObjectBuilder.cpp',
|
||||
'JSStreamWriter.cpp',
|
||||
'nsProfiler.cpp',
|
||||
'nsProfilerFactory.cpp',
|
||||
'platform.cpp',
|
||||
|
|
|
@ -204,10 +204,10 @@ nsProfiler::GetSharedLibraryInformation(nsAString& aOutString)
|
|||
NS_IMETHODIMP nsProfiler::GetProfileData(JSContext* aCx,
|
||||
JS::MutableHandle<JS::Value> aResult)
|
||||
{
|
||||
JSObject *obj = profiler_get_profile_jsobject(aCx);
|
||||
if (!obj)
|
||||
JS::RootedObject obj(aCx, profiler_get_profile_jsobject(aCx));
|
||||
if (!obj) {
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
}
|
||||
aResult.setObject(*obj);
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -132,29 +132,20 @@ ProfilerMarker::GetTime() {
|
|||
return mTime;
|
||||
}
|
||||
|
||||
template<typename Builder> void
|
||||
ProfilerMarker::BuildJSObject(Builder& b, typename Builder::ArrayHandle markers) const {
|
||||
typename Builder::RootedObject marker(b.context(), b.CreateObject());
|
||||
b.DefineProperty(marker, "name", GetMarkerName());
|
||||
// TODO: Store the callsite for this marker if available:
|
||||
// if have location data
|
||||
// b.DefineProperty(marker, "location", ...);
|
||||
if (mPayload) {
|
||||
typename Builder::RootedObject markerData(b.context(),
|
||||
mPayload->PreparePayload(b));
|
||||
b.DefineProperty(marker, "data", markerData);
|
||||
}
|
||||
b.DefineProperty(marker, "time", mTime);
|
||||
b.ArrayPush(markers, marker);
|
||||
void ProfilerMarker::StreamJSObject(JSStreamWriter& b) const {
|
||||
b.BeginObject();
|
||||
b.NameValue("name", GetMarkerName());
|
||||
// TODO: Store the callsite for this marker if available:
|
||||
// if have location data
|
||||
// b.NameValue(marker, "location", ...);
|
||||
if (mPayload) {
|
||||
b.Name("data");
|
||||
mPayload->StreamPayload(b);
|
||||
}
|
||||
b.NameValue("time", mTime);
|
||||
b.EndObject();
|
||||
}
|
||||
|
||||
template void
|
||||
ProfilerMarker::BuildJSObject<JSCustomObjectBuilder>(JSCustomObjectBuilder& b,
|
||||
JSCustomObjectBuilder::ArrayHandle markers) const;
|
||||
template void
|
||||
ProfilerMarker::BuildJSObject<JSObjectBuilder>(JSObjectBuilder& b,
|
||||
JSObjectBuilder::ArrayHandle markers) const;
|
||||
|
||||
PendingMarkers::~PendingMarkers() {
|
||||
clearMarkers();
|
||||
if (mSignalLock != false) {
|
||||
|
@ -573,6 +564,24 @@ JSObject *mozilla_sampler_get_profile_data(JSContext *aCx)
|
|||
return t->ToJSObject(aCx);
|
||||
}
|
||||
|
||||
void mozilla_sampler_save_profile_to_file(const char* aFilename)
|
||||
{
|
||||
TableTicker *t = tlsTicker.get();
|
||||
if (!t) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::ofstream stream;
|
||||
stream.open(aFilename);
|
||||
if (stream.is_open()) {
|
||||
t->ToStreamAsJSON(stream);
|
||||
stream.close();
|
||||
LOGF("Saved to %s", aFilename);
|
||||
} else {
|
||||
LOG("Fail to open profile log file.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const char** mozilla_sampler_get_features()
|
||||
{
|
||||
|
|
|
@ -0,0 +1,183 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <sstream>
|
||||
#include "JSStreamWriter.h"
|
||||
|
||||
TEST(JSStreamWriter, NoOutput) {
|
||||
std::stringstream ss;
|
||||
JSStreamWriter b(ss);
|
||||
ASSERT_TRUE(ss.str().compare("") == 0);
|
||||
}
|
||||
|
||||
TEST(JSStreamWriter, EmptyObject) {
|
||||
std::stringstream ss;
|
||||
JSStreamWriter b(ss);
|
||||
b.BeginObject();
|
||||
b.EndObject();
|
||||
ASSERT_TRUE(ss.str().compare("{}") == 0);
|
||||
}
|
||||
|
||||
TEST(JSStreamWriter, OnePropertyObject) {
|
||||
std::stringstream ss;
|
||||
JSStreamWriter b(ss);
|
||||
b.BeginObject();
|
||||
b.Name("a");
|
||||
b.Value(1);
|
||||
b.EndObject();
|
||||
ASSERT_TRUE(ss.str().compare("{\"a\":1}") == 0);
|
||||
}
|
||||
|
||||
TEST(JSStreamWriter, MultiPropertyObject) {
|
||||
std::stringstream ss;
|
||||
JSStreamWriter b(ss);
|
||||
b.BeginObject();
|
||||
b.Name("a");
|
||||
b.Value(1);
|
||||
b.Name("b");
|
||||
b.Value(2);
|
||||
b.EndObject();
|
||||
ASSERT_TRUE(ss.str().compare("{\"a\":1,\"b\":2}") == 0);
|
||||
}
|
||||
|
||||
TEST(JSStreamWriter, OnePropertyArray) {
|
||||
std::stringstream ss;
|
||||
JSStreamWriter b(ss);
|
||||
b.BeginArray();
|
||||
b.Value(1);
|
||||
b.EndArray();
|
||||
ASSERT_TRUE(ss.str().compare("[1]") == 0);
|
||||
}
|
||||
|
||||
TEST(JSStreamWriter, MultiPropertyArray) {
|
||||
std::stringstream ss;
|
||||
JSStreamWriter b(ss);
|
||||
b.BeginArray();
|
||||
b.Value(1);
|
||||
b.Value(2);
|
||||
b.EndArray();
|
||||
ASSERT_TRUE(ss.str().compare("[1,2]") == 0);
|
||||
}
|
||||
|
||||
TEST(JSStreamWriter, NestedObject) {
|
||||
std::stringstream ss;
|
||||
JSStreamWriter b(ss);
|
||||
b.BeginObject();
|
||||
b.Name("a");
|
||||
b.BeginObject();
|
||||
b.Name("b");
|
||||
b.Value(1);
|
||||
b.EndObject();
|
||||
b.EndObject();
|
||||
ASSERT_TRUE(ss.str().compare("{\"a\":{\"b\":1}}") == 0);
|
||||
}
|
||||
|
||||
TEST(JSStreamWriter, NestedObjectInArray) {
|
||||
std::stringstream ss;
|
||||
JSStreamWriter b(ss);
|
||||
b.BeginArray();
|
||||
b.BeginObject();
|
||||
b.Name("a");
|
||||
b.Value(1);
|
||||
b.EndObject();
|
||||
b.EndArray();
|
||||
ASSERT_TRUE(ss.str().compare("[{\"a\":1}]") == 0);
|
||||
}
|
||||
|
||||
TEST(JSStreamWriter, NestedArrayInObject) {
|
||||
std::stringstream ss;
|
||||
JSStreamWriter b(ss);
|
||||
b.BeginObject();
|
||||
b.Name("a");
|
||||
b.BeginArray();
|
||||
b.Value(1);
|
||||
b.EndArray();
|
||||
b.EndObject();
|
||||
ASSERT_TRUE(ss.str().compare("{\"a\":[1]}") == 0);
|
||||
}
|
||||
|
||||
TEST(JSStreamWriter, StingEscaping) {
|
||||
std::stringstream ss;
|
||||
JSStreamWriter b(ss);
|
||||
b.Value("a\"a");
|
||||
ASSERT_TRUE(ss.str().compare("\"a\\\"a\"") == 0);
|
||||
|
||||
std::stringstream ss2;
|
||||
JSStreamWriter b2(ss2);
|
||||
b2.Value("a\na");
|
||||
ASSERT_TRUE(ss2.str().compare("\"a\\u000Aa\"") == 0);
|
||||
}
|
||||
|
||||
TEST(JSStreamWriter, ArrayOfOjects) {
|
||||
std::stringstream ss;
|
||||
JSStreamWriter b(ss);
|
||||
b.BeginArray();
|
||||
b.BeginObject();
|
||||
b.EndObject();
|
||||
|
||||
b.BeginObject();
|
||||
b.EndObject();
|
||||
b.EndArray();
|
||||
ASSERT_TRUE(ss.str().compare("[{},{}]") == 0);
|
||||
}
|
||||
|
||||
TEST(JSStreamWriter, Complex) {
|
||||
std::stringstream ss;
|
||||
JSStreamWriter b(ss);
|
||||
b.BeginObject();
|
||||
b.Name("a");
|
||||
b.BeginArray();
|
||||
b.Value(1);
|
||||
|
||||
b.BeginObject();
|
||||
b.EndObject();
|
||||
|
||||
b.BeginObject();
|
||||
b.Name("b");
|
||||
b.Value("c");
|
||||
b.EndObject();
|
||||
b.EndArray();
|
||||
|
||||
b.Name("b");
|
||||
b.BeginArray();
|
||||
b.BeginArray();
|
||||
b.EndArray();
|
||||
b.EndArray();
|
||||
b.EndObject();
|
||||
ASSERT_TRUE(ss.str().compare("{\"a\":[1,{},{\"b\":\"c\"}],\"b\":[[]]}") == 0);
|
||||
}
|
||||
|
||||
TEST(JSStreamWriter, Complex2) {
|
||||
std::stringstream ss;
|
||||
JSStreamWriter b(ss);
|
||||
b.BeginObject();
|
||||
b.Name("a");
|
||||
b.BeginArray();
|
||||
b.BeginObject();
|
||||
b.Name("b");
|
||||
b.Value("c");
|
||||
b.Name("d");
|
||||
b.BeginArray();
|
||||
b.BeginObject();
|
||||
b.Name("e");
|
||||
b.BeginArray();
|
||||
b.BeginObject();
|
||||
b.Name("f");
|
||||
b.Value("g");
|
||||
b.EndObject();
|
||||
b.BeginObject();
|
||||
b.Name("h");
|
||||
b.Value("i");
|
||||
b.EndObject();
|
||||
b.EndArray();
|
||||
b.EndObject();
|
||||
b.EndArray();
|
||||
b.EndObject();
|
||||
b.EndArray();
|
||||
b.EndObject();
|
||||
ASSERT_TRUE(ss.str().compare("{\"a\":[{\"b\":\"c\",\"d\":[{\"e\":[{\"f\":\"g\"},{\"h\":\"i\"}]}]}]}")
|
||||
}
|
|
@ -521,6 +521,7 @@ public:
|
|||
NS_IMETHOD_(void) SetInputContext(const InputContext& aContext,
|
||||
const InputContextAction& aAction);
|
||||
NS_IMETHOD_(InputContext) GetInputContext();
|
||||
NS_IMETHOD AttachNativeKeyEvent(mozilla::WidgetKeyboardEvent& aEvent);
|
||||
NS_IMETHOD_(bool) ExecuteNativeKeyBinding(
|
||||
NativeKeyBindingsType aType,
|
||||
const mozilla::WidgetKeyboardEvent& aEvent,
|
||||
|
|
|
@ -1942,6 +1942,13 @@ nsChildView::GetInputContext()
|
|||
return mInputContext;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsChildView::AttachNativeKeyEvent(mozilla::WidgetKeyboardEvent& aEvent)
|
||||
{
|
||||
NS_ENSURE_TRUE(mTextInputHandler, NS_ERROR_NOT_AVAILABLE);
|
||||
return mTextInputHandler->AttachNativeKeyEvent(aEvent);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(bool)
|
||||
nsChildView::ExecuteNativeKeyBinding(NativeKeyBindingsType aType,
|
||||
const WidgetKeyboardEvent& aEvent,
|
||||
|
|
|
@ -100,8 +100,8 @@ typedef void* nsNativeWidget;
|
|||
#endif
|
||||
|
||||
#define NS_IWIDGET_IID \
|
||||
{ 0x8e081187, 0xf123, 0x4572, \
|
||||
{ 0x82, 0xc6, 0x4c, 0xcd, 0xc2, 0x0e, 0xbd, 0xf9 } }
|
||||
{ 0x87d80888, 0x9917, 0x4bfb, \
|
||||
{ 0x81, 0xa9, 0x1c, 0x5e, 0x30, 0x9c, 0x78, 0xb4 } }
|
||||
|
||||
/*
|
||||
* Window shadow styles
|
||||
|
@ -1811,6 +1811,14 @@ public:
|
|||
*/
|
||||
NS_IMETHOD_(InputContext) GetInputContext() = 0;
|
||||
|
||||
/*
|
||||
* Given a WidgetKeyboardEvent, this method synthesizes a corresponding
|
||||
* native (OS-level) event for it. This method allows tests to simulate
|
||||
* keystrokes that trigger native key bindings (which require a native
|
||||
* event).
|
||||
*/
|
||||
NS_IMETHOD AttachNativeKeyEvent(mozilla::WidgetKeyboardEvent& aEvent) = 0;
|
||||
|
||||
/*
|
||||
* Execute native key bindings for aType.
|
||||
*/
|
||||
|
|
|
@ -79,6 +79,7 @@ PuppetWidget::PuppetWidget(TabChild* aTabChild)
|
|||
: mTabChild(aTabChild)
|
||||
, mDPI(-1)
|
||||
, mDefaultScale(-1)
|
||||
, mNativeKeyCommandsValid(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(PuppetWidget);
|
||||
|
||||
|
@ -275,6 +276,14 @@ PuppetWidget::DispatchEvent(WidgetGUIEvent* event, nsEventStatus& aStatus)
|
|||
NS_ABORT_IF_FALSE(!mChild || mChild->mWindowType == eWindowType_popup,
|
||||
"Unexpected event dispatch!");
|
||||
|
||||
AutoCacheNativeKeyCommands autoCache(this);
|
||||
if (event->mFlags.mIsSynthesizedForTests && !mNativeKeyCommandsValid) {
|
||||
WidgetKeyboardEvent* keyEvent = event->AsKeyboardEvent();
|
||||
if (keyEvent) {
|
||||
mTabChild->RequestNativeKeyBindings(&autoCache, keyEvent);
|
||||
}
|
||||
}
|
||||
|
||||
aStatus = nsEventStatus_eIgnore;
|
||||
|
||||
if (event->message == NS_COMPOSITION_START) {
|
||||
|
@ -319,6 +328,12 @@ PuppetWidget::ExecuteNativeKeyBinding(NativeKeyBindingsType aType,
|
|||
DoCommandCallback aCallback,
|
||||
void* aCallbackData)
|
||||
{
|
||||
// B2G doesn't have native key bindings.
|
||||
#ifdef MOZ_B2G
|
||||
return false;
|
||||
#else // #ifdef MOZ_B2G
|
||||
MOZ_ASSERT(mNativeKeyCommandsValid);
|
||||
|
||||
nsTArray<mozilla::CommandInt>& commands = mSingleLineCommands;
|
||||
switch (aType) {
|
||||
case nsIWidget::NativeKeyBindingsForSingleLineEditor:
|
||||
|
@ -340,6 +355,7 @@ PuppetWidget::ExecuteNativeKeyBinding(NativeKeyBindingsType aType,
|
|||
aCallback(static_cast<mozilla::Command>(commands[i]), aCallbackData);
|
||||
}
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
LayerManager*
|
||||
|
|
|
@ -33,6 +33,8 @@ class TabChild;
|
|||
|
||||
namespace widget {
|
||||
|
||||
class AutoCacheNativeKeyCommands;
|
||||
|
||||
class PuppetWidget : public nsBaseWidget, public nsSupportsWeakReference
|
||||
{
|
||||
typedef mozilla::dom::TabChild TabChild;
|
||||
|
@ -136,21 +138,7 @@ public:
|
|||
DoCommandCallback aCallback,
|
||||
void* aCallbackData) MOZ_OVERRIDE;
|
||||
|
||||
void CacheNativeKeyCommands(const InfallibleTArray<mozilla::CommandInt>& aSingleLineCommands,
|
||||
const InfallibleTArray<mozilla::CommandInt>& aMultiLineCommands,
|
||||
const InfallibleTArray<mozilla::CommandInt>& aRichTextCommands)
|
||||
{
|
||||
mSingleLineCommands = aSingleLineCommands;
|
||||
mMultiLineCommands = aMultiLineCommands;
|
||||
mRichTextCommands = aRichTextCommands;
|
||||
}
|
||||
|
||||
void ClearNativeKeyCommands()
|
||||
{
|
||||
mSingleLineCommands.Clear();
|
||||
mMultiLineCommands.Clear();
|
||||
mRichTextCommands.Clear();
|
||||
}
|
||||
friend class AutoCacheNativeKeyCommands;
|
||||
|
||||
//
|
||||
// nsBaseWidget methods we override
|
||||
|
@ -250,11 +238,57 @@ private:
|
|||
double mDefaultScale;
|
||||
|
||||
// Precomputed answers for ExecuteNativeKeyBinding
|
||||
bool mNativeKeyCommandsValid;
|
||||
InfallibleTArray<mozilla::CommandInt> mSingleLineCommands;
|
||||
InfallibleTArray<mozilla::CommandInt> mMultiLineCommands;
|
||||
InfallibleTArray<mozilla::CommandInt> mRichTextCommands;
|
||||
};
|
||||
|
||||
struct AutoCacheNativeKeyCommands
|
||||
{
|
||||
AutoCacheNativeKeyCommands(PuppetWidget* aWidget)
|
||||
: mWidget(aWidget)
|
||||
{
|
||||
mSavedValid = mWidget->mNativeKeyCommandsValid;
|
||||
mSavedSingleLine = mWidget->mSingleLineCommands;
|
||||
mSavedMultiLine = mWidget->mMultiLineCommands;
|
||||
mSavedRichText = mWidget->mRichTextCommands;
|
||||
}
|
||||
|
||||
void Cache(const InfallibleTArray<mozilla::CommandInt>& aSingleLineCommands,
|
||||
const InfallibleTArray<mozilla::CommandInt>& aMultiLineCommands,
|
||||
const InfallibleTArray<mozilla::CommandInt>& aRichTextCommands)
|
||||
{
|
||||
mWidget->mNativeKeyCommandsValid = true;
|
||||
mWidget->mSingleLineCommands = aSingleLineCommands;
|
||||
mWidget->mMultiLineCommands = aMultiLineCommands;
|
||||
mWidget->mRichTextCommands = aRichTextCommands;
|
||||
}
|
||||
|
||||
void CacheNoCommands()
|
||||
{
|
||||
mWidget->mNativeKeyCommandsValid = true;
|
||||
mWidget->mSingleLineCommands.Clear();
|
||||
mWidget->mMultiLineCommands.Clear();
|
||||
mWidget->mRichTextCommands.Clear();
|
||||
}
|
||||
|
||||
~AutoCacheNativeKeyCommands()
|
||||
{
|
||||
mWidget->mNativeKeyCommandsValid = mSavedValid;
|
||||
mWidget->mSingleLineCommands = mSavedSingleLine;
|
||||
mWidget->mMultiLineCommands = mSavedMultiLine;
|
||||
mWidget->mRichTextCommands = mSavedRichText;
|
||||
}
|
||||
|
||||
private:
|
||||
PuppetWidget* mWidget;
|
||||
bool mSavedValid;
|
||||
InfallibleTArray<mozilla::CommandInt> mSavedSingleLine;
|
||||
InfallibleTArray<mozilla::CommandInt> mSavedMultiLine;
|
||||
InfallibleTArray<mozilla::CommandInt> mSavedRichText;
|
||||
};
|
||||
|
||||
class PuppetScreen : public nsBaseScreen
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -181,6 +181,7 @@ public:
|
|||
virtual nsresult ActivateNativeMenuItemAt(const nsAString& indexString) { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
virtual nsresult ForceUpdateNativeMenuAt(const nsAString& indexString) { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
NS_IMETHOD NotifyIME(const IMENotification& aIMENotification) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
NS_IMETHOD AttachNativeKeyEvent(mozilla::WidgetKeyboardEvent& aEvent) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
NS_IMETHOD_(bool) ExecuteNativeKeyBinding(
|
||||
NativeKeyBindingsType aType,
|
||||
const mozilla::WidgetKeyboardEvent& aEvent,
|
||||
|
|
|
@ -79,6 +79,34 @@ ThreadStackHelper::~ThreadStackHelper()
|
|||
#endif
|
||||
}
|
||||
|
||||
#if defined(XP_LINUX) && defined(__arm__)
|
||||
// Some (old) Linux kernels on ARM have a bug where a signal handler
|
||||
// can be called without clearing the IT bits in CPSR first. The result
|
||||
// is that the first few instructions of the handler could be skipped,
|
||||
// ultimately resulting in crashes. To workaround this bug, the handler
|
||||
// on ARM is a trampoline that starts with enough NOP instructions, so
|
||||
// that even if the IT bits are not cleared, only the NOP instructions
|
||||
// will be skipped over.
|
||||
|
||||
template <void (*H)(int, siginfo_t*, void*)>
|
||||
__attribute__((naked)) void
|
||||
SignalTrampoline(int aSignal, siginfo_t* aInfo, void* aContext)
|
||||
{
|
||||
asm volatile (
|
||||
"nop; nop; nop; nop"
|
||||
: : : "memory");
|
||||
|
||||
// Because the assembler may generate additional insturctions below, we
|
||||
// need to ensure NOPs are inserted first by separating them out above.
|
||||
|
||||
asm volatile (
|
||||
"bx %0"
|
||||
:
|
||||
: "r"(H), "l"(aSignal), "l"(aInfo), "l"(aContext)
|
||||
: "memory");
|
||||
}
|
||||
#endif // XP_LINUX && __arm__
|
||||
|
||||
void
|
||||
ThreadStackHelper::GetStack(Stack& aStack)
|
||||
{
|
||||
|
@ -99,7 +127,11 @@ ThreadStackHelper::GetStack(Stack& aStack)
|
|||
}
|
||||
sCurrent = this;
|
||||
struct sigaction sigact = {};
|
||||
#ifdef __arm__
|
||||
sigact.sa_sigaction = SignalTrampoline<SigAction>;
|
||||
#else
|
||||
sigact.sa_sigaction = SigAction;
|
||||
#endif
|
||||
sigemptyset(&sigact.sa_mask);
|
||||
sigact.sa_flags = SA_SIGINFO | SA_RESTART;
|
||||
if (::sigaction(SIGPROF, &sigact, &sOldSigAction)) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче