Merge m-c to fx-team
@ -9,7 +9,6 @@ Cu.import('resource://gre/modules/SettingsChangeNotifier.jsm');
|
||||
Cu.import('resource://gre/modules/DataStoreChangeNotifier.jsm');
|
||||
Cu.import('resource://gre/modules/AlarmService.jsm');
|
||||
Cu.import('resource://gre/modules/ActivitiesService.jsm');
|
||||
Cu.import('resource://gre/modules/PermissionPromptHelper.jsm');
|
||||
Cu.import('resource://gre/modules/NotificationDB.jsm');
|
||||
Cu.import('resource://gre/modules/Payment.jsm');
|
||||
Cu.import("resource://gre/modules/AppsUtils.jsm");
|
||||
|
@ -19,13 +19,13 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="740faa5d0060fb218b407cf224330654ddf833a5"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="dde24450450514039bad6d8ab4fcb7e5d4d44e03"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="bf9aaf39dd5a6491925a022db167c460f8207d34"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="ee6e7320bb83409ebd4685fbd87a8ae033704182"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
|
||||
<project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
|
||||
|
@ -17,10 +17,10 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="740faa5d0060fb218b407cf224330654ddf833a5"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="dde24450450514039bad6d8ab4fcb7e5d4d44e03"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="ee6e7320bb83409ebd4685fbd87a8ae033704182"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<!-- Stock Android things -->
|
||||
@ -128,7 +128,7 @@
|
||||
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="3a9a17613cc685aa232432566ad6cc607eab4ec1"/>
|
||||
<project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="0e31f35a2a77301e91baa8a237aa9e9fa4076084"/>
|
||||
<project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="7d33aaf740bbf6c7c6e9c34a92b371eda311b66b"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="efd87a5797ca40fa2df256630c07e0dfb2f762dc"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c61e5f15fd62888f2c33d7d542b5b65c38102e8b"/>
|
||||
<project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="832f4acaf481a19031e479a40b03d9ce5370ddee"/>
|
||||
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="dd72bacb432efc5135a1f747d00aab91f898bddb"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="276ce45e78b09c4a4ee643646f691d22804754c1">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="740faa5d0060fb218b407cf224330654ddf833a5"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="dde24450450514039bad6d8ab4fcb7e5d4d44e03"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
@ -23,7 +23,7 @@
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="ee6e7320bb83409ebd4685fbd87a8ae033704182"/>
|
||||
<!-- Stock Android things -->
|
||||
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
|
||||
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>
|
||||
|
@ -19,13 +19,13 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="740faa5d0060fb218b407cf224330654ddf833a5"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="dde24450450514039bad6d8ab4fcb7e5d4d44e03"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="bf9aaf39dd5a6491925a022db167c460f8207d34"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="ee6e7320bb83409ebd4685fbd87a8ae033704182"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
|
||||
<project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
|
||||
|
@ -17,10 +17,10 @@
|
||||
</project>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="740faa5d0060fb218b407cf224330654ddf833a5"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="dde24450450514039bad6d8ab4fcb7e5d4d44e03"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="ee6e7320bb83409ebd4685fbd87a8ae033704182"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<!-- Stock Android things -->
|
||||
|
@ -4,6 +4,6 @@
|
||||
"remote": "",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "3e612bc9b3d79a3fa36d2f38af4202abb0ead68f",
|
||||
"revision": "190172ac413ab6476a6d7df3999950ec756f96a4",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -17,12 +17,12 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="740faa5d0060fb218b407cf224330654ddf833a5"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="dde24450450514039bad6d8ab4fcb7e5d4d44e03"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="ee6e7320bb83409ebd4685fbd87a8ae033704182"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
|
||||
<project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="740faa5d0060fb218b407cf224330654ddf833a5"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="dde24450450514039bad6d8ab4fcb7e5d4d44e03"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -17,10 +17,10 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="740faa5d0060fb218b407cf224330654ddf833a5"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="dde24450450514039bad6d8ab4fcb7e5d4d44e03"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="ee6e7320bb83409ebd4685fbd87a8ae033704182"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<!-- Stock Android things -->
|
||||
|
@ -17,12 +17,12 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="740faa5d0060fb218b407cf224330654ddf833a5"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="dde24450450514039bad6d8ab4fcb7e5d4d44e03"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="ee6e7320bb83409ebd4685fbd87a8ae033704182"/>
|
||||
<project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
|
||||
|
@ -4,11 +4,128 @@
|
||||
|
||||
/* Conversation window styles */
|
||||
|
||||
.conversation .controls {
|
||||
background: #f2f2f2;
|
||||
padding: .5em;
|
||||
.conversation {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.conversation .controls {
|
||||
position: absolute;
|
||||
z-index: 999; /* required to have it superimposed to the video element */
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: rgba(0, 0, 0, .70);
|
||||
border: 1px solid #5a5a5a;
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.conversation .controls li {
|
||||
float: left;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.conversation .controls .btn {
|
||||
width: 40px;
|
||||
height: 30px;
|
||||
background: transparent;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 12px 8px;
|
||||
background-size: 14px 14px;
|
||||
border-right: 1px solid #5a5a5a;
|
||||
border-radius: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.conversation .controls .btn:hover {
|
||||
background-color: rgba(255, 255, 255, .35);
|
||||
}
|
||||
|
||||
/* Hangup button */
|
||||
.conversation .controls .btn-hangup {
|
||||
background-color: #D74345;
|
||||
background-image: url(../img/hangup-inverse-14x14.png);
|
||||
}
|
||||
.conversation .controls .btn-hangup:hover {
|
||||
background-color: #C53436;
|
||||
}
|
||||
@media (min-resolution: 2dppx) {
|
||||
.conversation .controls .btn-hangup {
|
||||
background-image: url(../img/hangup-inverse-14x14@2x.png);
|
||||
}
|
||||
}
|
||||
|
||||
/* Common media control buttons behavior */
|
||||
.conversation .controls .media-control {
|
||||
background-color: transparent;
|
||||
opacity: .7; /* reduce the opacity for a non-streaming media */
|
||||
}
|
||||
.conversation .controls .media-control:hover {
|
||||
background-color: rgba(255, 255, 255, .35);
|
||||
opacity: 1;
|
||||
}
|
||||
.conversation .controls .media-control.muted {
|
||||
background-color: #0096DD;
|
||||
opacity: 1;
|
||||
}
|
||||
.conversation .controls .media-control.streaming {
|
||||
opacity: 1;
|
||||
}
|
||||
.conversation .controls .media-control:not(.streaming):hover {
|
||||
background-color: transparent;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Audio mute button */
|
||||
.conversation .controls .btn-mute-audio {
|
||||
background-image: url(../img/audio-inverse-14x14.png);
|
||||
}
|
||||
.conversation .controls .btn-mute-audio.streaming {
|
||||
background-image: url(../img/audio-highlight-14x14.png);
|
||||
}
|
||||
.conversation .controls .btn-mute-audio.muted,
|
||||
.conversation .controls .btn-mute-audio.streaming:hover {
|
||||
background-image: url(../img/mute-inverse-14x14.png);
|
||||
}
|
||||
@media (min-resolution: 2dppx) {
|
||||
.conversation .controls .btn-mute-audio {
|
||||
background-image: url(../img/audio-inverse-14x14@2x.png);
|
||||
}
|
||||
.conversation .controls .btn-mute-audio.streaming {
|
||||
background-image: url(../img/audio-highlight-14x14@2x.png);
|
||||
}
|
||||
.conversation .controls .btn-mute-audio.muted,
|
||||
.conversation .controls .btn-mute-audio.streaming:hover {
|
||||
background-image: url(../img/mute-inverse-14x14@2x.png);
|
||||
}
|
||||
}
|
||||
|
||||
/* Video mute button */
|
||||
.conversation .controls .btn-mute-video {
|
||||
background-image: url(../img/video-inverse-14x14.png);
|
||||
}
|
||||
.conversation .controls .btn-mute-video.streaming {
|
||||
background-image: url(../img/video-highlight-14x14.png);
|
||||
}
|
||||
.conversation .controls .btn-mute-video.muted,
|
||||
.conversation .controls .btn-mute-video.streaming:hover {
|
||||
background-image: url(../img/facemute-14x14.png);
|
||||
}
|
||||
@media (min-resolution: 2dppx) {
|
||||
.conversation .controls .btn-mute-video {
|
||||
background-image: url(../img/video-inverse-14x14@2x.png);
|
||||
}
|
||||
.conversation .controls .btn-mute-video.streaming {
|
||||
background-image: url(../img/video-highlight-14x14@2x.png);
|
||||
}
|
||||
.conversation .controls .btn-mute-video.muted,
|
||||
.conversation .controls .btn-mute-video.streaming:hover {
|
||||
background-image: url(../img/facemute-14x14@2x.png);
|
||||
}
|
||||
}
|
||||
|
||||
/* Video elements */
|
||||
|
||||
.conversation .media video {
|
||||
background: #eee;
|
||||
}
|
||||
@ -20,13 +137,16 @@
|
||||
}
|
||||
|
||||
.conversation .media.nested .remote {
|
||||
display: inline-block;
|
||||
background: #000;
|
||||
width: 100%;
|
||||
min-height: 154px;
|
||||
}
|
||||
|
||||
.conversation .media.nested .local {
|
||||
position: absolute;
|
||||
bottom: .8em;
|
||||
right: .8em;
|
||||
bottom: 4px;
|
||||
right: 0;
|
||||
width: 30%;
|
||||
max-width: 140px;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<!DOCTYPE html>
|
||||
<!-- 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/. -->
|
||||
@ -98,10 +98,11 @@
|
||||
<h3>Large with controls</h3>
|
||||
|
||||
<div class="conversation">
|
||||
<nav class="controls">
|
||||
<button class="btn">Start</button>
|
||||
<button class="btn">Stop</button>
|
||||
</nav>
|
||||
<ul class="controls">
|
||||
<li><button class="btn btn-hangup" title="Hangup"></button></li>
|
||||
<li><button class="btn media-control btn-mute-video streaming" title="Mute video"></button></li>
|
||||
<li><button class="btn media-control btn-mute-audio" title="Mute audio"></button></li>
|
||||
</ul>
|
||||
<div class="media nested">
|
||||
<video class="remote"></video>
|
||||
<video class="local"></video>
|
||||
@ -112,6 +113,11 @@
|
||||
|
||||
<div style="width: 204px">
|
||||
<div class="conversation">
|
||||
<ul class="controls">
|
||||
<li><button class="btn btn-hangup" title="Hangup"></button></li>
|
||||
<li><button class="btn media-control btn-mute-video streaming" title="Mute video"></button></li>
|
||||
<li><button class="btn media-control btn-mute-audio" title="Mute audio"></button></li>
|
||||
</ul>
|
||||
<div class="media nested">
|
||||
<video class="remote"></video>
|
||||
<video class="local"></video>
|
||||
@ -128,6 +134,68 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Controls button variants</h2>
|
||||
|
||||
<h3>Nothing muted</h3>
|
||||
|
||||
<div style="width: 204px; min-height: 26px">
|
||||
<div class="conversation">
|
||||
<ul class="controls">
|
||||
<li><button class="btn btn-hangup" title="Hangup"></button></li>
|
||||
<li><button class="btn media-control btn-mute-video" title="Mute video"></button></li>
|
||||
<li><button class="btn media-control btn-mute-audio" title="Mute audio"></button></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>Local audio muted</h3>
|
||||
|
||||
<div style="width: 204px; min-height: 26px">
|
||||
<div class="conversation">
|
||||
<ul class="controls">
|
||||
<li><button class="btn btn-hangup" title="Hangup"></button></li>
|
||||
<li><button class="btn media-control btn-mute-video" title="Mute video"></button></li>
|
||||
<li><button class="btn media-control btn-mute-audio muted" title="Mute audio"></button></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>Local video muted</h3>
|
||||
|
||||
<div style="width: 204px; min-height: 26px">
|
||||
<div class="conversation">
|
||||
<ul class="controls">
|
||||
<li><button class="btn btn-hangup" title="Hangup"></button></li>
|
||||
<li><button class="btn media-control btn-mute-video muted" title="Mute video"></button></li>
|
||||
<li><button class="btn media-control btn-mute-audio" title="Mute audio"></button></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>Local audio streaming</h3>
|
||||
|
||||
<div style="width: 204px; min-height: 26px">
|
||||
<div class="conversation">
|
||||
<ul class="controls">
|
||||
<li><button class="btn btn-hangup" title="Hangup"></button></li>
|
||||
<li><button class="btn media-control btn-mute-video" title="Mute video"></button></li>
|
||||
<li><button class="btn media-control btn-mute-audio streaming" title="Mute audio"></button></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>Local video streaming</h3>
|
||||
|
||||
<div style="width: 204px; min-height: 26px">
|
||||
<div class="conversation">
|
||||
<ul class="controls">
|
||||
<li><button class="btn btn-hangup" title="Hangup"></button></li>
|
||||
<li><button class="btn media-control btn-mute-video streaming" title="Mute video"></button></li>
|
||||
<li><button class="btn media-control btn-mute-audio" title="Mute audio"></button></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Buttons</h2>
|
||||
|
||||
<h3>Using <code><a></code></h3>
|
||||
|
After Width: | Height: | Size: 226 B |
After Width: | Height: | Size: 439 B |
After Width: | Height: | Size: 190 B |
After Width: | Height: | Size: 340 B |
BIN
browser/components/loop/content/shared/img/facemute-14x14.png
Normal file
After Width: | Height: | Size: 218 B |
BIN
browser/components/loop/content/shared/img/facemute-14x14@2x.png
Normal file
After Width: | Height: | Size: 373 B |
After Width: | Height: | Size: 188 B |
After Width: | Height: | Size: 330 B |
After Width: | Height: | Size: 241 B |
After Width: | Height: | Size: 465 B |
After Width: | Height: | Size: 190 B |
After Width: | Height: | Size: 301 B |
After Width: | Height: | Size: 155 B |
After Width: | Height: | Size: 243 B |
@ -94,10 +94,21 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
var ConversationView = BaseView.extend({
|
||||
className: "conversation",
|
||||
|
||||
/**
|
||||
* Local stream object.
|
||||
* @type {OT.Stream|null}
|
||||
*/
|
||||
localStream: null,
|
||||
|
||||
template: _.template([
|
||||
'<nav class="controls">',
|
||||
' <button class="btn stop" data-l10n-id="stop"></button>',
|
||||
'</nav>',
|
||||
'<ul class="controls cf">',
|
||||
' <li><button class="btn btn-hangup" ',
|
||||
' data-l10n-id="hangup_button"></button></li>',
|
||||
' <li><button class="btn media-control btn-mute-video"',
|
||||
' data-l10n-id="mute_local_video_button"></button></li>',
|
||||
' <li><button class="btn media-control btn-mute-audio"',
|
||||
' data-l10n-id="mute_local_audio_button"></button></li>',
|
||||
'</ul>',
|
||||
'<div class="media nested">',
|
||||
// Both these wrappers are required by the SDK; this is fragile and
|
||||
// will break if a future version of the SDK updates this generated DOM,
|
||||
@ -110,14 +121,19 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
|
||||
// height set to "auto" to fix video layout on Google Chrome
|
||||
// @see https://bugzilla.mozilla.org/show_bug.cgi?id=991122
|
||||
videoStyles: {
|
||||
publisherConfig: {
|
||||
width: "100%",
|
||||
height: "auto",
|
||||
style: { "bugDisplayMode": "off" }
|
||||
style: {
|
||||
bugDisplayMode: "off",
|
||||
buttonDisplayMode: "off"
|
||||
}
|
||||
},
|
||||
|
||||
events: {
|
||||
'click .btn.stop': 'hangup'
|
||||
'click .btn-hangup': 'hangup',
|
||||
'click .btn-mute-audio': 'toggleMuteAudio',
|
||||
'click .btn-mute-video': 'toggleMuteVideo'
|
||||
},
|
||||
|
||||
/**
|
||||
@ -153,9 +169,9 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
event.streams.forEach(function(stream) {
|
||||
if (stream.connection.connectionId !==
|
||||
this.model.session.connection.connectionId) {
|
||||
this.model.session.subscribe(stream, incoming, this.videoStyles);
|
||||
this.model.session.subscribe(stream, incoming, this.publisherConfig);
|
||||
}
|
||||
}.bind(this));
|
||||
}, this);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -169,6 +185,54 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
this.model.endSession();
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggles audio mute state.
|
||||
*
|
||||
* @param {MouseEvent} event
|
||||
*/
|
||||
toggleMuteAudio: function(event) {
|
||||
event.preventDefault();
|
||||
if (!this.localStream) {
|
||||
return;
|
||||
}
|
||||
var msgId;
|
||||
var $button = this.$(".btn-mute-audio");
|
||||
var enabled = !this.localStream.hasAudio;
|
||||
this.publisher.publishAudio(enabled);
|
||||
if (enabled) {
|
||||
msgId = "mute_local_audio_button.title";
|
||||
$button.removeClass("muted");
|
||||
} else {
|
||||
msgId = "unmute_local_audio_button.title";
|
||||
$button.addClass("muted");
|
||||
}
|
||||
$button.attr("title", l10n.get(msgId));
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggles video mute state.
|
||||
*
|
||||
* @param {MouseEvent} event
|
||||
*/
|
||||
toggleMuteVideo: function(event) {
|
||||
event.preventDefault();
|
||||
if (!this.localStream) {
|
||||
return;
|
||||
}
|
||||
var msgId;
|
||||
var $button = this.$(".btn-mute-video");
|
||||
var enabled = !this.localStream.hasVideo;
|
||||
this.publisher.publishVideo(enabled);
|
||||
if (enabled) {
|
||||
$button.removeClass("muted");
|
||||
msgId = "mute_local_video_button.title";
|
||||
} else {
|
||||
$button.addClass("muted");
|
||||
msgId = "unmute_local_video_button.title";
|
||||
}
|
||||
$button.attr("title", l10n.get(msgId));
|
||||
},
|
||||
|
||||
/**
|
||||
* Publishes remote streams available once a session is connected.
|
||||
*
|
||||
@ -179,7 +243,7 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
publish: function(event) {
|
||||
var outgoing = this.$(".outgoing").get(0);
|
||||
|
||||
this.publisher = this.sdk.initPublisher(outgoing, this.videoStyles);
|
||||
this.publisher = this.sdk.initPublisher(outgoing, this.publisherConfig);
|
||||
|
||||
// Suppress OT GuM custom dialog, see bug 1018875
|
||||
function preventOpeningAccessDialog(event) {
|
||||
@ -187,6 +251,20 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
}
|
||||
this.publisher.on("accessDialogOpened", preventOpeningAccessDialog);
|
||||
this.publisher.on("accessDenied", preventOpeningAccessDialog);
|
||||
this.publisher.on("streamCreated", function(event) {
|
||||
this.localStream = event.stream;
|
||||
if (this.localStream.hasAudio) {
|
||||
this.$(".btn-mute-audio").addClass("streaming");
|
||||
}
|
||||
if (this.localStream.hasVideo) {
|
||||
this.$(".btn-mute-video").addClass("streaming");
|
||||
}
|
||||
}.bind(this));
|
||||
this.publisher.on("streamDestroyed", function() {
|
||||
this.localStream = null;
|
||||
this.$(".btn-mute-audio").removeClass("streaming muted");
|
||||
this.$(".btn-mute-video").removeClass("streaming muted");
|
||||
}.bind(this));
|
||||
|
||||
this.model.session.publish(this.publisher);
|
||||
},
|
||||
|
@ -3,29 +3,59 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
browser.jar:
|
||||
content/browser/loop/conversation.html (content/conversation.html)
|
||||
content/browser/loop/panel.html (content/panel.html)
|
||||
content/browser/loop/shared/css/common.css (content/shared/css/common.css)
|
||||
content/browser/loop/shared/css/panel.css (content/shared/css/panel.css)
|
||||
content/browser/loop/shared/css/conversation.css (content/shared/css/conversation.css)
|
||||
content/browser/loop/shared/img/icon_32.png (content/shared/img/icon_32.png)
|
||||
content/browser/loop/shared/img/icon_64.png (content/shared/img/icon_64.png)
|
||||
content/browser/loop/shared/img/loading-icon.gif (content/shared/img/loading-icon.gif)
|
||||
content/browser/loop/shared/js/models.js (content/shared/js/models.js)
|
||||
content/browser/loop/shared/js/router.js (content/shared/js/router.js)
|
||||
content/browser/loop/shared/js/views.js (content/shared/js/views.js)
|
||||
content/browser/loop/shared/libs/react-0.10.0.js (content/shared/libs/react-0.10.0.js)
|
||||
content/browser/loop/shared/libs/lodash-2.4.1.js (content/shared/libs/lodash-2.4.1.js)
|
||||
content/browser/loop/shared/libs/jquery-2.1.0.js (content/shared/libs/jquery-2.1.0.js)
|
||||
content/browser/loop/shared/libs/backbone-1.1.2.js (content/shared/libs/backbone-1.1.2.js)
|
||||
content/browser/loop/shared/sounds/Firefox-Long.ogg (content/shared/sounds/Firefox-Long.ogg)
|
||||
content/browser/loop/libs/l10n.js (content/libs/l10n.js)
|
||||
content/browser/loop/js/client.js (content/js/client.js)
|
||||
content/browser/loop/js/conversation.js (content/js/conversation.js)
|
||||
content/browser/loop/js/desktopRouter.js (content/js/desktopRouter.js)
|
||||
content/browser/loop/js/panel.js (content/js/panel.js)
|
||||
# Desktop html files
|
||||
content/browser/loop/conversation.html (content/conversation.html)
|
||||
content/browser/loop/panel.html (content/panel.html)
|
||||
|
||||
# Desktop libs (see bottom of this file for TokBox sdk assets)
|
||||
content/browser/loop/libs/l10n.js (content/libs/l10n.js)
|
||||
|
||||
# Desktop script
|
||||
content/browser/loop/js/client.js (content/js/client.js)
|
||||
content/browser/loop/js/desktopRouter.js (content/js/desktopRouter.js)
|
||||
content/browser/loop/js/conversation.js (content/js/conversation.js)
|
||||
content/browser/loop/js/panel.js (content/js/panel.js)
|
||||
|
||||
# Shared styles
|
||||
content/browser/loop/shared/css/common.css (content/shared/css/common.css)
|
||||
content/browser/loop/shared/css/panel.css (content/shared/css/panel.css)
|
||||
content/browser/loop/shared/css/conversation.css (content/shared/css/conversation.css)
|
||||
|
||||
# Shared images
|
||||
content/browser/loop/shared/img/icon_32.png (content/shared/img/icon_32.png)
|
||||
content/browser/loop/shared/img/icon_64.png (content/shared/img/icon_64.png)
|
||||
content/browser/loop/shared/img/loading-icon.gif (content/shared/img/loading-icon.gif)
|
||||
content/browser/loop/shared/img/audio-highlight-14x14.png (content/shared/img/audio-highlight-14x14.png)
|
||||
content/browser/loop/shared/img/audio-highlight-14x14@2x.png (content/shared/img/audio-highlight-14x14@2x.png)
|
||||
content/browser/loop/shared/img/audio-inverse-14x14.png (content/shared/img/audio-inverse-14x14.png)
|
||||
content/browser/loop/shared/img/audio-inverse-14x14@2x.png (content/shared/img/audio-inverse-14x14@2x.png)
|
||||
content/browser/loop/shared/img/facemute-14x14.png (content/shared/img/facemute-14x14.png)
|
||||
content/browser/loop/shared/img/facemute-14x14@2x.png (content/shared/img/facemute-14x14@2x.png)
|
||||
content/browser/loop/shared/img/hangup-inverse-14x14.png (content/shared/img/hangup-inverse-14x14.png)
|
||||
content/browser/loop/shared/img/hangup-inverse-14x14@2x.png (content/shared/img/hangup-inverse-14x14@2x.png)
|
||||
content/browser/loop/shared/img/mute-inverse-14x14.png (content/shared/img/mute-inverse-14x14.png)
|
||||
content/browser/loop/shared/img/mute-inverse-14x14@2x.png (content/shared/img/mute-inverse-14x14@2x.png)
|
||||
content/browser/loop/shared/img/video-highlight-14x14.png (content/shared/img/video-highlight-14x14.png)
|
||||
content/browser/loop/shared/img/video-highlight-14x14@2x.png (content/shared/img/video-highlight-14x14@2x.png)
|
||||
content/browser/loop/shared/img/video-inverse-14x14.png (content/shared/img/video-inverse-14x14.png)
|
||||
content/browser/loop/shared/img/video-inverse-14x14@2x.png (content/shared/img/video-inverse-14x14@2x.png)
|
||||
|
||||
# Shared scripts
|
||||
content/browser/loop/shared/js/models.js (content/shared/js/models.js)
|
||||
content/browser/loop/shared/js/router.js (content/shared/js/router.js)
|
||||
content/browser/loop/shared/js/views.js (content/shared/js/views.js)
|
||||
|
||||
# Shared libs
|
||||
content/browser/loop/shared/libs/react-0.10.0.js (content/shared/libs/react-0.10.0.js)
|
||||
content/browser/loop/shared/libs/lodash-2.4.1.js (content/shared/libs/lodash-2.4.1.js)
|
||||
content/browser/loop/shared/libs/jquery-2.1.0.js (content/shared/libs/jquery-2.1.0.js)
|
||||
content/browser/loop/shared/libs/backbone-1.1.2.js (content/shared/libs/backbone-1.1.2.js)
|
||||
|
||||
# Shared sounds
|
||||
content/browser/loop/shared/sounds/Firefox-Long.ogg (content/shared/sounds/Firefox-Long.ogg)
|
||||
|
||||
# Partner SDK assets
|
||||
content/browser/loop/libs/sdk.js (content/libs/sdk.js)
|
||||
content/browser/loop/libs/sdk.js (content/libs/sdk.js)
|
||||
content/browser/loop/otcdn/webrtc/v2.2.5/css/ot.min.css (content/libs/otcdn/webrtc/v2.2.5/css/ot.min.css)
|
||||
content/browser/loop/otcdn/webrtc/v2.2.5/js/dynamic_config.min.js (content/libs/otcdn/webrtc/v2.2.5/js/dynamic_config.min.js)
|
||||
content/browser/loop/otcdn/webrtc/v2.2.5/images/rtc/access-denied-chrome.png (content/libs/otcdn/webrtc/v2.2.5/images/rtc/access-denied-chrome.png)
|
||||
|
@ -4,7 +4,11 @@ missing_conversation_info=Missing conversation information.
|
||||
network_disconnected=The network connection terminated abruptly.
|
||||
peer_ended_conversation=Your peer ended the conversation.
|
||||
unable_retrieve_call_info=Unable to retrieve conversation information.
|
||||
stop=Stop
|
||||
hangup_button.title=Hangup
|
||||
mute_local_audio_button.title=Mute your audio
|
||||
unmute_local_audio_button.title=Unute your audio
|
||||
mute_local_video_button.title=Mute your video
|
||||
unmute_local_video_button.title=Unmute your video
|
||||
start_call=Start the call
|
||||
welcome=Welcome to the Loop web client.
|
||||
incompatible_browser=Incompatible Browser
|
||||
@ -21,7 +25,11 @@ missing_conversation_info=Informations de communication manquantes.
|
||||
network_disconnected=La connexion réseau semble avoir été interrompue.
|
||||
peer_ended_conversation=Votre correspondant a mis fin à la communication.
|
||||
unable_retrieve_call_info=Impossible de récupérer les informations liées à cet appel.
|
||||
stop=Arrêter
|
||||
hangup_button.title=Terminer l'appel
|
||||
mute_local_audio_button.title=Couper la diffusion audio
|
||||
unmute_local_audio_button.title=Reprendre la diffusion audio
|
||||
mute_local_video_button.title=Couper la diffusion vidéo
|
||||
unmute_local_video_button.title=Reprendre la diffusion vidéo
|
||||
start_call=Démarrer l'appel
|
||||
welcome=Bienvenue sur Loop.
|
||||
incompatible_browser=Navigateur non supporté
|
||||
|
@ -59,7 +59,9 @@ describe("loop.shared.views", function() {
|
||||
}, Backbone.Events);
|
||||
fakePublisher = {
|
||||
on: sandbox.spy(),
|
||||
off: sandbox.spy()
|
||||
off: sandbox.spy(),
|
||||
publishAudio: sandbox.spy(),
|
||||
publishVideo: sandbox.spy()
|
||||
};
|
||||
fakeSDK = {
|
||||
initPublisher: sandbox.stub().returns(fakePublisher),
|
||||
@ -124,7 +126,7 @@ describe("loop.shared.views", function() {
|
||||
function() {
|
||||
view.publish();
|
||||
|
||||
sinon.assert.calledTwice(fakePublisher.on);
|
||||
sinon.assert.called(fakePublisher.on);
|
||||
sinon.assert.calledWith(fakePublisher.on, "accessDialogOpened");
|
||||
sinon.assert.calledWith(fakePublisher.on, "accessDenied");
|
||||
});
|
||||
@ -157,6 +159,66 @@ describe("loop.shared.views", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("#toggleMuteAudio", function() {
|
||||
var view;
|
||||
|
||||
beforeEach(function() {
|
||||
view = new sharedViews.ConversationView({
|
||||
sdk: fakeSDK,
|
||||
model: model
|
||||
});
|
||||
view.publish();
|
||||
});
|
||||
|
||||
it("should unpublish local audio when enabled", function() {
|
||||
view.localStream = {hasAudio: true};
|
||||
|
||||
view.toggleMuteAudio({preventDefault: sandbox.spy()});
|
||||
|
||||
sinon.assert.calledOnce(fakePublisher.publishAudio);
|
||||
sinon.assert.calledWithExactly(fakePublisher.publishAudio, false);
|
||||
});
|
||||
|
||||
it("should publish local audio when disabled", function() {
|
||||
view.localStream = {hasAudio: false};
|
||||
|
||||
view.toggleMuteAudio({preventDefault: sandbox.spy()});
|
||||
|
||||
sinon.assert.calledOnce(fakePublisher.publishAudio);
|
||||
sinon.assert.calledWithExactly(fakePublisher.publishAudio, true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#toggleMuteVideo", function() {
|
||||
var view;
|
||||
|
||||
beforeEach(function() {
|
||||
view = new sharedViews.ConversationView({
|
||||
sdk: fakeSDK,
|
||||
model: model
|
||||
});
|
||||
view.publish();
|
||||
});
|
||||
|
||||
it("should unpublish local video when enabled", function() {
|
||||
view.localStream = {hasVideo: true};
|
||||
|
||||
view.toggleMuteVideo({preventDefault: sandbox.spy()});
|
||||
|
||||
sinon.assert.calledOnce(fakePublisher.publishVideo);
|
||||
sinon.assert.calledWithExactly(fakePublisher.publishVideo, false);
|
||||
});
|
||||
|
||||
it("should publish local video when disabled", function() {
|
||||
view.localStream = {hasVideo: false};
|
||||
|
||||
view.toggleMuteVideo({preventDefault: sandbox.spy()});
|
||||
|
||||
sinon.assert.calledOnce(fakePublisher.publishVideo);
|
||||
sinon.assert.calledWithExactly(fakePublisher.publishVideo, true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Model events", function() {
|
||||
var view;
|
||||
|
||||
|
@ -20,7 +20,11 @@ incoming_call_title=Incoming Call…
|
||||
incoming_call=Incoming call
|
||||
accept_button=Accept
|
||||
decline_button=Decline
|
||||
stop=Stop
|
||||
hangup_button.title=Hangup
|
||||
mute_local_audio_button.title=Mute your audio
|
||||
unmute_local_audio_button.title=Unute your audio
|
||||
mute_local_video_button.title=Mute your video
|
||||
unmute_local_video_button.title=Unmute your video
|
||||
|
||||
peer_ended_conversation=Your peer ended the conversation.
|
||||
call_has_ended=Your call has ended.
|
||||
|
@ -4,29 +4,19 @@
|
||||
navigator.mozGetUserMedia({audio: true}, function(stream) {
|
||||
stream.getAudioTracks()[0].enabled = false;
|
||||
var testAudio = document.getElementById('testAudio');
|
||||
// temporary log for bug 1031137.
|
||||
["abort", "canplay", "canplaythrough", "durationchange", "emptied", "ended",
|
||||
"error", "loadeddata", "loadedmetadata", "loadstart", "pause", "play",
|
||||
"playing", "progress", "ratechange", "seeked", "seeking", "stalled", "suspend",
|
||||
"timeupdate", "volumechange", "waiting"].forEach(function(v) {
|
||||
testAudio.addEventListener(v, function() {
|
||||
dump("event received: " + v + "\n");
|
||||
});
|
||||
});
|
||||
|
||||
// Wait some time for good measure
|
||||
var eventReceived = 0;
|
||||
testAudio.addEventListener("timeupdate", function() {
|
||||
dump("timeupdate received (index:" + eventReceived + ")\n");
|
||||
if (++eventReceived == 3) {
|
||||
document.querySelector("html").className = "";
|
||||
}
|
||||
});
|
||||
|
||||
})
|
||||
testAudio.mozSrcObject = stream;
|
||||
testAudio.play();
|
||||
}, function(err) {
|
||||
dump(err + "\n");
|
||||
// Don't go orange if we can't get an audio input stream,
|
||||
// as this is not what we are trying to test and can happen on Windows.
|
||||
document.querySelector("html").className = "";
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -64,6 +64,8 @@ AudioOutputObserver::AudioOutputObserver()
|
||||
AudioOutputObserver::~AudioOutputObserver()
|
||||
{
|
||||
Clear();
|
||||
moz_free(mSaved);
|
||||
mSaved = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
@ -72,8 +74,7 @@ AudioOutputObserver::Clear()
|
||||
while (mPlayoutFifo->size() > 0) {
|
||||
moz_free(mPlayoutFifo->Pop());
|
||||
}
|
||||
moz_free(mSaved);
|
||||
mSaved = nullptr;
|
||||
// we'd like to touch mSaved here, but we can't if we might still be getting callbacks
|
||||
}
|
||||
|
||||
FarEndAudioChunk *
|
||||
|
@ -167,7 +167,7 @@ this.PermissionsInstaller = {
|
||||
// If it's not a system update, then we should keep the prompt
|
||||
// permissions that have been granted or denied previously.
|
||||
permValue =
|
||||
PermissionSettingsModule.getPermission(permName,
|
||||
PermissionSettingsModule.getPermission(expandedPermNames[idx],
|
||||
aApp.manifestURL,
|
||||
aApp.origin,
|
||||
false);
|
||||
|
@ -163,9 +163,15 @@ this.PermissionsTable = { geolocation: {
|
||||
},
|
||||
attention: {
|
||||
app: DENY_ACTION,
|
||||
privileged: ALLOW_ACTION,
|
||||
privileged: DENY_ACTION,
|
||||
certified: ALLOW_ACTION
|
||||
},
|
||||
"moz-attention": {
|
||||
app: DENY_ACTION,
|
||||
privileged: ALLOW_ACTION,
|
||||
certified: ALLOW_ACTION,
|
||||
substitute: ["attention"]
|
||||
},
|
||||
"webapps-manage": {
|
||||
app: DENY_ACTION,
|
||||
privileged: DENY_ACTION,
|
||||
@ -267,14 +273,26 @@ this.PermissionsTable = { geolocation: {
|
||||
},
|
||||
"audio-channel-telephony": {
|
||||
app: DENY_ACTION,
|
||||
privileged: ALLOW_ACTION,
|
||||
privileged: DENY_ACTION,
|
||||
certified: ALLOW_ACTION
|
||||
},
|
||||
"moz-audio-channel-telephony": {
|
||||
app: DENY_ACTION,
|
||||
privileged: ALLOW_ACTION,
|
||||
certified: ALLOW_ACTION,
|
||||
substitute: ["audio-channel-telephony"]
|
||||
},
|
||||
"audio-channel-ringer": {
|
||||
app: DENY_ACTION,
|
||||
privileged: ALLOW_ACTION,
|
||||
privileged: DENY_ACTION,
|
||||
certified: ALLOW_ACTION
|
||||
},
|
||||
"moz-audio-channel-ringer": {
|
||||
app: DENY_ACTION,
|
||||
privileged: ALLOW_ACTION,
|
||||
certified: ALLOW_ACTION,
|
||||
substitute: ["audio-channel-ringer"]
|
||||
},
|
||||
"audio-channel-publicnotification": {
|
||||
app: DENY_ACTION,
|
||||
privileged: DENY_ACTION,
|
||||
@ -340,6 +358,15 @@ this.PermissionsTable = { geolocation: {
|
||||
app: DENY_ACTION,
|
||||
privileged: PROMPT_ACTION,
|
||||
certified: PROMPT_ACTION
|
||||
},
|
||||
// This permission doesn't actually grant access to
|
||||
// anything. It exists only to check the correctness
|
||||
// of web prompt composed permissions in tests.
|
||||
"test-permission": {
|
||||
app: PROMPT_ACTION,
|
||||
privileged: PROMPT_ACTION,
|
||||
certified: ALLOW_ACTION,
|
||||
access: ["read", "write", "create"]
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
"geolocation": {},
|
||||
"audio-capture": {},
|
||||
"video-capture": {},
|
||||
"test-permission": {"access": "readonly"},
|
||||
"downloads": {}
|
||||
},
|
||||
"launch_path": "tests/dom/apps/tests/file_packaged_app.sjs",
|
||||
|
@ -95,11 +95,13 @@ var initialPermissionState = {
|
||||
"geolocation": "prompt",
|
||||
"audio-capture": "prompt",
|
||||
"video-capture": "prompt",
|
||||
"test-permission-read": "prompt",
|
||||
"downloads": "deny"
|
||||
}
|
||||
|
||||
var permissionsToSet = {
|
||||
"geolocation": "allow",
|
||||
"test-permission-read": "allow",
|
||||
"audio-capture": "deny"
|
||||
}
|
||||
|
||||
@ -107,6 +109,7 @@ var permissionsToCheck = {
|
||||
"geolocation": "allow",
|
||||
"audio-capture": "deny",
|
||||
"video-capture": "prompt",
|
||||
"test-permission-read": "allow",
|
||||
"downloads": "deny"
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "mozilla/dom/PContentPermission.h"
|
||||
#include "mozilla/dom/PermissionMessageUtils.h"
|
||||
#include "mozilla/dom/PContentPermissionRequestParent.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "mozilla/dom/TabParent.h"
|
||||
#include "mozilla/unused.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
@ -358,3 +359,129 @@ nsContentPermissionRequestProxy::Allow(JS::HandleValue aChoices)
|
||||
mParent = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// RemotePermissionRequest
|
||||
|
||||
// static
|
||||
uint32_t
|
||||
RemotePermissionRequest::ConvertArrayToPermissionRequest(
|
||||
nsIArray* aSrcArray,
|
||||
nsTArray<PermissionRequest>& aDesArray)
|
||||
{
|
||||
uint32_t len = 0;
|
||||
aSrcArray->GetLength(&len);
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
nsCOMPtr<nsIContentPermissionType> cpt = do_QueryElementAt(aSrcArray, i);
|
||||
nsAutoCString type;
|
||||
nsAutoCString access;
|
||||
cpt->GetType(type);
|
||||
cpt->GetAccess(access);
|
||||
|
||||
nsCOMPtr<nsIArray> optionArray;
|
||||
cpt->GetOptions(getter_AddRefs(optionArray));
|
||||
uint32_t optionsLength = 0;
|
||||
if (optionArray) {
|
||||
optionArray->GetLength(&optionsLength);
|
||||
}
|
||||
nsTArray<nsString> options;
|
||||
for (uint32_t j = 0; j < optionsLength; ++j) {
|
||||
nsCOMPtr<nsISupportsString> isupportsString = do_QueryElementAt(optionArray, j);
|
||||
if (isupportsString) {
|
||||
nsString option;
|
||||
isupportsString->GetData(option);
|
||||
options.AppendElement(option);
|
||||
}
|
||||
}
|
||||
|
||||
aDesArray.AppendElement(PermissionRequest(type, access, options));
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(RemotePermissionRequest, nsIContentPermissionRequest)
|
||||
|
||||
RemotePermissionRequest::RemotePermissionRequest(
|
||||
nsIContentPermissionRequest* aRequest,
|
||||
nsPIDOMWindow* aWindow)
|
||||
: mRequest(aRequest)
|
||||
, mWindow(aWindow)
|
||||
{
|
||||
}
|
||||
|
||||
// nsIContentPermissionRequest methods
|
||||
NS_IMETHODIMP
|
||||
RemotePermissionRequest::GetTypes(nsIArray** aTypes)
|
||||
{
|
||||
NS_ASSERTION(mRequest, "We need a request");
|
||||
return mRequest->GetTypes(aTypes);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
RemotePermissionRequest::GetPrincipal(nsIPrincipal **aRequestingPrincipal)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aRequestingPrincipal);
|
||||
|
||||
return mRequest->GetPrincipal(aRequestingPrincipal);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
RemotePermissionRequest::GetWindow(nsIDOMWindow** aRequestingWindow)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aRequestingWindow);
|
||||
|
||||
return mRequest->GetWindow(aRequestingWindow);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
RemotePermissionRequest::GetElement(nsIDOMElement** aRequestingElement)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aRequestingElement);
|
||||
*aRequestingElement = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
RemotePermissionRequest::Cancel()
|
||||
{
|
||||
NS_ASSERTION(mRequest, "We need a request");
|
||||
return mRequest->Cancel();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
RemotePermissionRequest::Allow(JS::HandleValue aChoices)
|
||||
{
|
||||
NS_ASSERTION(mRequest, "We need a request");
|
||||
return mRequest->Allow(aChoices);
|
||||
}
|
||||
|
||||
// PCOMContentPermissionRequestChild
|
||||
bool
|
||||
RemotePermissionRequest::Recv__delete__(const bool& aAllow,
|
||||
const nsTArray<PermissionChoice>& aChoices)
|
||||
{
|
||||
if (aAllow && mWindow->IsCurrentInnerWindow()) {
|
||||
// Convert choices to a JS val if any.
|
||||
// {"type1": "choice1", "type2": "choiceA"}
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(mWindow))) {
|
||||
return true; // This is not an IPC error.
|
||||
}
|
||||
JSContext* cx = jsapi.cx();
|
||||
JS::Rooted<JSObject*> obj(cx);
|
||||
obj = JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr());
|
||||
for (uint32_t i = 0; i < aChoices.Length(); ++i) {
|
||||
const nsString& choice = aChoices[i].choice();
|
||||
const nsCString& type = aChoices[i].type();
|
||||
JS::Rooted<JSString*> jChoice(cx, JS_NewUCStringCopyN(cx, choice.get(), choice.Length()));
|
||||
JS::Rooted<JS::Value> vChoice(cx, StringValue(jChoice));
|
||||
if (!JS_SetProperty(cx, obj, type.get(), vChoice)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
JS::RootedValue val(cx, JS::ObjectValue(*obj));
|
||||
(void) Allow(val);
|
||||
} else {
|
||||
(void) Cancel();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -8,7 +8,9 @@
|
||||
#include "nsIContentPermissionPrompt.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsIMutableArray.h"
|
||||
#include "PCOMContentPermissionRequestChild.h"
|
||||
|
||||
class nsPIDOMWindow;
|
||||
class nsContentPermissionRequestProxy;
|
||||
|
||||
// Forward declare IPC::Principal here which is defined in
|
||||
@ -83,4 +85,32 @@ class nsContentPermissionRequestProxy : public nsIContentPermissionRequest
|
||||
nsTArray<mozilla::dom::PermissionRequest> mPermissionRequests;
|
||||
};
|
||||
|
||||
/**
|
||||
* RemotePermissionRequest will send a prompt ipdl request to b2g process.
|
||||
*/
|
||||
class RemotePermissionRequest : public nsIContentPermissionRequest
|
||||
, public PCOMContentPermissionRequestChild
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSICONTENTPERMISSIONREQUEST
|
||||
|
||||
RemotePermissionRequest(nsIContentPermissionRequest* aRequest,
|
||||
nsPIDOMWindow* aWindow);
|
||||
|
||||
// It will be called when prompt dismissed.
|
||||
virtual bool Recv__delete__(const bool &aAllow,
|
||||
const nsTArray<PermissionChoice>& aChoices) MOZ_OVERRIDE;
|
||||
virtual void IPDLRelease() MOZ_OVERRIDE { Release(); }
|
||||
|
||||
static uint32_t ConvertArrayToPermissionRequest(
|
||||
nsIArray* aSrcArray,
|
||||
nsTArray<PermissionRequest>& aDesArray);
|
||||
private:
|
||||
virtual ~RemotePermissionRequest() {}
|
||||
|
||||
nsCOMPtr<nsIContentPermissionRequest> mRequest;
|
||||
nsCOMPtr<nsPIDOMWindow> mWindow;
|
||||
};
|
||||
|
||||
#endif // nsContentPermissionHelper_h
|
||||
|
@ -70,6 +70,7 @@
|
||||
#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
|
||||
#include "mozilla/dom/MutableFile.h"
|
||||
#include "mozilla/dom/MutableFileBinding.h"
|
||||
#include "mozilla/dom/PermissionMessageUtils.h"
|
||||
#include "mozilla/dom/quota/PersistenceType.h"
|
||||
#include "mozilla/dom/quota/QuotaManager.h"
|
||||
#include "nsDOMBlobBuilder.h"
|
||||
@ -88,6 +89,7 @@
|
||||
#include "GeckoProfiler.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsIContentIterator.h"
|
||||
#include "nsContentPermissionHelper.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#undef GetClassName
|
||||
@ -3677,6 +3679,49 @@ nsDOMWindowUtils::XpconnectArgument(nsIDOMWindowUtils* aThis)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::AskPermission(nsIContentPermissionRequest* aRequest)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
|
||||
nsRefPtr<RemotePermissionRequest> req =
|
||||
new RemotePermissionRequest(aRequest, window->GetCurrentInnerWindow());
|
||||
|
||||
// for content process
|
||||
if (XRE_GetProcessType() == GeckoProcessType_Content) {
|
||||
MOZ_ASSERT(NS_IsMainThread()); // IPC can only be execute on main thread.
|
||||
|
||||
dom::TabChild* child = dom::TabChild::GetFrom(window->GetDocShell());
|
||||
NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
|
||||
|
||||
nsCOMPtr<nsIArray> typeArray;
|
||||
nsresult rv = req->GetTypes(getter_AddRefs(typeArray));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsTArray<PermissionRequest> permArray;
|
||||
RemotePermissionRequest::ConvertArrayToPermissionRequest(typeArray, permArray);
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
rv = req->GetPrincipal(getter_AddRefs(principal));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
req->AddRef();
|
||||
child->SendPContentPermissionRequestConstructor(req,
|
||||
permArray,
|
||||
IPC::Principal(principal));
|
||||
|
||||
req->Sendprompt();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// for chrome process
|
||||
nsCOMPtr<nsIContentPermissionPrompt> prompt =
|
||||
do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
|
||||
if (prompt) {
|
||||
prompt->Prompt(req);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(nsTranslationNodeList)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_ENTRY(nsITranslationNodeList)
|
||||
|
@ -342,7 +342,7 @@ function setBluetoothEnabled(aEnabled) {
|
||||
/**
|
||||
* Wait for one named BluetoothManager event.
|
||||
*
|
||||
* Resolve if that named event occurs. Never reject.
|
||||
* Resolve if that named event occurs. Never reject.
|
||||
*
|
||||
* Fulfill params: the DOMEvent passed.
|
||||
*
|
||||
@ -367,7 +367,7 @@ function waitForManagerEvent(aEventName) {
|
||||
/**
|
||||
* Wait for one named BluetoothAdapter event.
|
||||
*
|
||||
* Resolve if that named event occurs. Never reject.
|
||||
* Resolve if that named event occurs. Never reject.
|
||||
*
|
||||
* Fulfill params: the DOMEvent passed.
|
||||
*
|
||||
@ -391,6 +391,118 @@ function waitForAdapterEvent(aAdapter, aEventName) {
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for 'onattributechanged' events for state changes of BluetoothAdapter
|
||||
* with specified order.
|
||||
*
|
||||
* Resolve if those expected events occur in order. Never reject.
|
||||
*
|
||||
* Fulfill params: an array which contains every changed attributes during
|
||||
* the waiting.
|
||||
*
|
||||
* @param aAdapter
|
||||
* The BluetoothAdapter you want to use.
|
||||
* @param aStateChangesInOrder
|
||||
* An array which contains an expected order of BluetoothAdapterState.
|
||||
* Example 1: [enabling, enabled]
|
||||
* Example 2: [disabling, disabled]
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function waitForAdapterStateChanged(aAdapter, aStateChangesInOrder) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
let stateIndex = 0;
|
||||
let prevStateIndex = 0;
|
||||
let statesArray = [];
|
||||
let changedAttrs = [];
|
||||
aAdapter.onattributechanged = function(aEvent) {
|
||||
for (let i in aEvent.attrs) {
|
||||
changedAttrs.push(aEvent.attrs[i]);
|
||||
switch (aEvent.attrs[i]) {
|
||||
case "state":
|
||||
log(" 'state' changed to " + aAdapter.state);
|
||||
|
||||
// Received state change order may differ from expected one even though
|
||||
// state changes in expected order, because the value of state may change
|
||||
// again before we receive prior 'onattributechanged' event.
|
||||
//
|
||||
// For example, expected state change order [A,B,C] may result in
|
||||
// received ones:
|
||||
// - [A,C,C] if state becomes C before we receive 2nd 'onattributechanged'
|
||||
// - [B,B,C] if state becomes B before we receive 1st 'onattributechanged'
|
||||
// - [C,C,C] if state becomes C before we receive 1st 'onattributechanged'
|
||||
// - [A,B,C] if all 'onattributechanged' are received in perfect timing
|
||||
//
|
||||
// As a result, we ensure only following conditions instead of exactly
|
||||
// matching received and expected state change order.
|
||||
// - Received state change order never reverse expected one. For example,
|
||||
// [B,A,C] should never occur with expected state change order [A,B,C].
|
||||
// - The changed value of state in received state change order never
|
||||
// appears later than that in expected one. For example, [A,A,C] should
|
||||
// never occur with expected state change order [A,B,C].
|
||||
let stateIndex = aStateChangesInOrder.indexOf(aAdapter.state);
|
||||
if (stateIndex >= prevStateIndex && stateIndex + 1 > statesArray.length) {
|
||||
statesArray.push(aAdapter.state);
|
||||
prevStateIndex = stateIndex;
|
||||
|
||||
if (statesArray.length == aStateChangesInOrder.length) {
|
||||
aAdapter.onattributechanged = null;
|
||||
ok(true, "BluetoothAdapter event 'onattributechanged' got.");
|
||||
deferred.resolve(changedAttrs);
|
||||
}
|
||||
} else {
|
||||
ok(false, "The order of 'onattributechanged' events is unexpected.");
|
||||
}
|
||||
|
||||
break;
|
||||
case "name":
|
||||
log(" 'name' changed to " + aAdapter.name);
|
||||
if (aAdapter.state == "enabling") {
|
||||
isnot(aAdapter.name, "", "adapter.name");
|
||||
}
|
||||
else if (aAdapter.state == "disabling") {
|
||||
is(aAdapter.name, "", "adapter.name");
|
||||
}
|
||||
break;
|
||||
case "address":
|
||||
log(" 'address' changed to " + aAdapter.address);
|
||||
if (aAdapter.state == "enabling") {
|
||||
isnot(aAdapter.address, "", "adapter.address");
|
||||
}
|
||||
else if (aAdapter.state == "disabling") {
|
||||
is(aAdapter.address, "", "adapter.address");
|
||||
}
|
||||
break;
|
||||
case "discoverable":
|
||||
log(" 'discoverable' changed to " + aAdapter.discoverable);
|
||||
if (aAdapter.state == "enabling") {
|
||||
is(aAdapter.discoverable, true, "adapter.discoverable");
|
||||
}
|
||||
else if (aAdapter.state == "disabling") {
|
||||
is(aAdapter.discoverable, false, "adapter.discoverable");
|
||||
}
|
||||
break;
|
||||
case "discovering":
|
||||
log(" 'discovering' changed to " + aAdapter.discovering);
|
||||
if (aAdapter.state == "enabling") {
|
||||
is(aAdapter.discovering, true, "adapter.discovering");
|
||||
}
|
||||
else if (aAdapter.state == "disabling") {
|
||||
is(aAdapter.discovering, false, "adapter.discovering");
|
||||
}
|
||||
break;
|
||||
case "unknown":
|
||||
default:
|
||||
ok(false, "Unknown attribute '" + aEvent.attrs[i] + "' changed." );
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush permission settings and call |finish()|.
|
||||
*/
|
||||
@ -417,7 +529,7 @@ function startBluetoothTestBase(aPermissions, aTestCaseMain) {
|
||||
}
|
||||
|
||||
function startBluetoothTest(aReenable, aTestCaseMain) {
|
||||
startBluetoothTestBase(["settings-read", "settings-write"], function() {
|
||||
startBluetoothTestBase([], function() {
|
||||
let origEnabled, needEnable;
|
||||
return Promise.resolve()
|
||||
.then(function() {
|
||||
|
@ -4,3 +4,4 @@ browser = false
|
||||
qemu = false
|
||||
|
||||
[test_dom_BluetoothManager_API2.js]
|
||||
[test_dom_BluetoothAdapter_enable_API2.js]
|
||||
|
@ -0,0 +1,76 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Test Purpose:
|
||||
// To verify that enable/disable process of BluetoothAdapter is correct.
|
||||
//
|
||||
// Test Procedure:
|
||||
// [0] Set Bluetooth permission and enable default adapter.
|
||||
// [1] Disable Bluetooth and check the correctness of 'onattributechanged'.
|
||||
// [2] Enable Bluetooth and check the correctness of 'onattributechanged'.
|
||||
//
|
||||
// Test Coverage:
|
||||
// - BluetoothAdapter.enable()
|
||||
// - BluetoothAdapter.disable()
|
||||
// - BluetoothAdapter.onattributechanged()
|
||||
// - BluetoothAdapter.address
|
||||
// - BluetoothAdapter.state
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
MARIONETTE_TIMEOUT = 60000;
|
||||
MARIONETTE_HEAD_JS = 'head.js';
|
||||
|
||||
startBluetoothTest(true, function testCaseMain(aAdapter) {
|
||||
log("Checking adapter attributes ...");
|
||||
|
||||
is(aAdapter.state, "enabled", "adapter.state");
|
||||
isnot(aAdapter.address, "", "adapter.address");
|
||||
|
||||
// Since adapter has just been re-enabled, these properties should be 'false'.
|
||||
is(aAdapter.discovering, false, "adapter.discovering");
|
||||
is(aAdapter.discoverable, false, "adapter.discoverable");
|
||||
// TODO: Check the correctness of name and address if we use emulator.
|
||||
// is(aAdapter.name, EMULATOR_NAME, "adapter.name");
|
||||
// is(aAdapter.address, EMULATOR_ADDRESS, "adapter.address");
|
||||
|
||||
log(" adapter.address: " + aAdapter.address);
|
||||
log(" adapter.name: " + aAdapter.name);
|
||||
|
||||
let originalAddr = aAdapter.address;
|
||||
let originalName = aAdapter.name;
|
||||
|
||||
return Promise.resolve()
|
||||
.then(function() {
|
||||
log("[1] Disable Bluetooth and check the correctness of 'onattributechanged'");
|
||||
let promises = [];
|
||||
promises.push(waitForAdapterStateChanged(aAdapter, ["disabling", "disabled"]));
|
||||
promises.push(aAdapter.disable());
|
||||
return Promise.all(promises);
|
||||
})
|
||||
.then(function(aResults) {
|
||||
isnot(aResults[0].indexOf("address"), -1, "Indicator of 'address' changed event");
|
||||
if (originalName != "") {
|
||||
isnot(aResults[0].indexOf("name"), -1, "Indicator of 'name' changed event");
|
||||
}
|
||||
is(aAdapter.address, "", "adapter.address");
|
||||
is(aAdapter.name, "", "adapter.name");
|
||||
})
|
||||
.then(function() {
|
||||
log("[2] Enable Bluetooth and check the correctness of 'onattributechanged'");
|
||||
let promises = [];
|
||||
promises.push(waitForAdapterStateChanged(aAdapter, ["enabling", "enabled"]));
|
||||
promises.push(aAdapter.enable());
|
||||
return Promise.all(promises);
|
||||
})
|
||||
.then(function(aResults) {
|
||||
isnot(aResults[0].indexOf("address"), -1, "Indicator of 'address' changed event");
|
||||
if (originalName != "") {
|
||||
isnot(aResults[0].indexOf("name"), -1, "Indicator of 'name' changed event");
|
||||
}
|
||||
is(aAdapter.address, originalAddr, "adapter.address");
|
||||
is(aAdapter.name, originalName, "adapter.name");
|
||||
})
|
||||
});
|
@ -5,6 +5,151 @@ const {Cc: Cc, Ci: Ci, Cr: Cr, Cu: Cu} = SpecialPowers;
|
||||
|
||||
let Promise = Cu.import("resource://gre/modules/Promise.jsm").Promise;
|
||||
|
||||
const PDU_DCS_CODING_GROUP_BITS = 0xF0;
|
||||
const PDU_DCS_MSG_CODING_7BITS_ALPHABET = 0x00;
|
||||
const PDU_DCS_MSG_CODING_8BITS_ALPHABET = 0x04;
|
||||
const PDU_DCS_MSG_CODING_16BITS_ALPHABET = 0x08;
|
||||
|
||||
const PDU_DCS_MSG_CLASS_BITS = 0x03;
|
||||
const PDU_DCS_MSG_CLASS_NORMAL = 0xFF;
|
||||
const PDU_DCS_MSG_CLASS_0 = 0x00;
|
||||
const PDU_DCS_MSG_CLASS_ME_SPECIFIC = 0x01;
|
||||
const PDU_DCS_MSG_CLASS_SIM_SPECIFIC = 0x02;
|
||||
const PDU_DCS_MSG_CLASS_TE_SPECIFIC = 0x03;
|
||||
const PDU_DCS_MSG_CLASS_USER_1 = 0x04;
|
||||
const PDU_DCS_MSG_CLASS_USER_2 = 0x05;
|
||||
|
||||
const GECKO_SMS_MESSAGE_CLASSES = {};
|
||||
GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_NORMAL] = "normal";
|
||||
GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_0] = "class-0";
|
||||
GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_ME_SPECIFIC] = "class-1";
|
||||
GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_SIM_SPECIFIC] = "class-2";
|
||||
GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_TE_SPECIFIC] = "class-3";
|
||||
GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_USER_1] = "user-1";
|
||||
GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_USER_2] = "user-2";
|
||||
|
||||
const CB_MESSAGE_SIZE_GSM = 88;
|
||||
const CB_MESSAGE_SIZE_ETWS = 56;
|
||||
|
||||
const CB_GSM_MESSAGEID_ETWS_BEGIN = 0x1100;
|
||||
const CB_GSM_MESSAGEID_ETWS_END = 0x1107;
|
||||
|
||||
const CB_GSM_GEOGRAPHICAL_SCOPE_NAMES = [
|
||||
"cell-immediate",
|
||||
"plmn",
|
||||
"location-area",
|
||||
"cell"
|
||||
];
|
||||
|
||||
const CB_ETWS_WARNING_TYPE_NAMES = [
|
||||
"earthquake",
|
||||
"tsunami",
|
||||
"earthquake-tsunami",
|
||||
"test",
|
||||
"other"
|
||||
];
|
||||
|
||||
const CB_DCS_LANG_GROUP_1 = [
|
||||
"de", "en", "it", "fr", "es", "nl", "sv", "da", "pt", "fi",
|
||||
"no", "el", "tr", "hu", "pl", null
|
||||
];
|
||||
const CB_DCS_LANG_GROUP_2 = [
|
||||
"cs", "he", "ar", "ru", "is", null, null, null, null, null,
|
||||
null, null, null, null, null, null
|
||||
];
|
||||
|
||||
/**
|
||||
* Compose input number into specified number of semi-octets.
|
||||
*
|
||||
* @param: aNum
|
||||
* The number to be converted.
|
||||
* @param: aNumSemiOctets
|
||||
* Number of semi-octects to be composed to.
|
||||
*
|
||||
* @return The composed Hex String.
|
||||
*/
|
||||
function buildHexStr(aNum, aNumSemiOctets) {
|
||||
let str = aNum.toString(16);
|
||||
ok(str.length <= aNumSemiOctets);
|
||||
while (str.length < aNumSemiOctets) {
|
||||
str = "0" + str;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to decode the given DCS into encoding type, language,
|
||||
* language indicator and message class.
|
||||
*
|
||||
* @param: aDcs
|
||||
* The DCS to be decoded.
|
||||
*
|
||||
* @return [encoding, language, hasLanguageIndicator,
|
||||
* GECKO_SMS_MESSAGE_CLASSES[messageClass]]
|
||||
*/
|
||||
function decodeGsmDataCodingScheme(aDcs) {
|
||||
let language = null;
|
||||
let hasLanguageIndicator = false;
|
||||
let encoding = PDU_DCS_MSG_CODING_7BITS_ALPHABET;
|
||||
let messageClass = PDU_DCS_MSG_CLASS_NORMAL;
|
||||
|
||||
switch (aDcs & PDU_DCS_CODING_GROUP_BITS) {
|
||||
case 0x00: // 0000
|
||||
language = CB_DCS_LANG_GROUP_1[aDcs & 0x0F];
|
||||
break;
|
||||
|
||||
case 0x10: // 0001
|
||||
switch (aDcs & 0x0F) {
|
||||
case 0x00:
|
||||
hasLanguageIndicator = true;
|
||||
break;
|
||||
case 0x01:
|
||||
encoding = PDU_DCS_MSG_CODING_16BITS_ALPHABET;
|
||||
hasLanguageIndicator = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x20: // 0010
|
||||
language = CB_DCS_LANG_GROUP_2[aDcs & 0x0F];
|
||||
break;
|
||||
|
||||
case 0x40: // 01xx
|
||||
case 0x50:
|
||||
//case 0x60:
|
||||
//case 0x70:
|
||||
case 0x90: // 1001
|
||||
encoding = (aDcs & 0x0C);
|
||||
if (encoding == 0x0C) {
|
||||
encoding = PDU_DCS_MSG_CODING_7BITS_ALPHABET;
|
||||
}
|
||||
messageClass = (aDcs & PDU_DCS_MSG_CLASS_BITS);
|
||||
break;
|
||||
|
||||
case 0xF0:
|
||||
encoding = (aDcs & 0x04) ? PDU_DCS_MSG_CODING_8BITS_ALPHABET
|
||||
: PDU_DCS_MSG_CODING_7BITS_ALPHABET;
|
||||
switch(aDcs & PDU_DCS_MSG_CLASS_BITS) {
|
||||
case 0x01: messageClass = PDU_DCS_MSG_CLASS_USER_1; break;
|
||||
case 0x02: messageClass = PDU_DCS_MSG_CLASS_USER_2; break;
|
||||
case 0x03: messageClass = PDU_DCS_MSG_CLASS_TE_SPECIFIC; break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x30: // 0011 (Reserved)
|
||||
case 0x80: // 1000 (Reserved)
|
||||
case 0xA0: // 1010..1100 (Reserved)
|
||||
case 0xB0:
|
||||
case 0xC0:
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unsupported CBS data coding scheme: " + aDcs);
|
||||
}
|
||||
|
||||
return [encoding, language, hasLanguageIndicator,
|
||||
GECKO_SMS_MESSAGE_CLASSES[messageClass]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Push required permissions and test if |navigator.mozCellBroadcast| exists.
|
||||
* Resolve if it does, reject otherwise.
|
||||
@ -148,7 +293,7 @@ function sendMultipleRawCbsToEmulatorAndWait(aPdus) {
|
||||
promises.push(sendRawCbsToEmulator(pdu));
|
||||
}
|
||||
|
||||
return Promise.all(promises);
|
||||
return Promise.all(promises).then(aResults => aResults[0].message);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,268 +1,215 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 10000;
|
||||
MARIONETTE_TIMEOUT = 20000;
|
||||
MARIONETTE_HEAD_JS = 'head.js';
|
||||
|
||||
const CB_MESSAGE_SIZE_ETWS = 56;
|
||||
|
||||
const CB_GSM_GEOGRAPHICAL_SCOPE_NAMES = [
|
||||
"cell-immediate",
|
||||
"plmn",
|
||||
"location-area",
|
||||
"cell"
|
||||
];
|
||||
|
||||
const CB_ETWS_WARNING_TYPE_NAMES = [
|
||||
"earthquake",
|
||||
"tsunami",
|
||||
"earthquake-tsunami",
|
||||
"test",
|
||||
"other"
|
||||
];
|
||||
|
||||
SpecialPowers.addPermission("cellbroadcast", true, document);
|
||||
SpecialPowers.addPermission("mobileconnection", true, document);
|
||||
|
||||
let cbs = window.navigator.mozCellBroadcast;
|
||||
ok(cbs instanceof window.MozCellBroadcast,
|
||||
"mozCellBroadcast is instanceof " + cbs.constructor);
|
||||
|
||||
let pendingEmulatorCmdCount = 0;
|
||||
function sendCellBroadcastMessage(pdu, callback) {
|
||||
pendingEmulatorCmdCount++;
|
||||
|
||||
let cmd = "cbs pdu " + pdu;
|
||||
runEmulatorCmd(cmd, function(result) {
|
||||
pendingEmulatorCmdCount--;
|
||||
|
||||
is(result[0], "OK", "Emulator response");
|
||||
|
||||
if (callback) {
|
||||
window.setTimeout(callback, 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function buildHexStr(n, numSemiOctets) {
|
||||
let str = n.toString(16);
|
||||
ok(str.length <= numSemiOctets);
|
||||
while (str.length < numSemiOctets) {
|
||||
str = "0" + str;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
function seq(end, begin) {
|
||||
let result = [];
|
||||
for (let i = begin || 0; i < end; i++) {
|
||||
result.push(i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function repeat(func, array, oncomplete) {
|
||||
(function do_call(index) {
|
||||
let next = index < (array.length - 1) ? do_call.bind(null, index + 1) : oncomplete;
|
||||
func.apply(null, [array[index], next]);
|
||||
})(0);
|
||||
}
|
||||
|
||||
function doTestHelper(pdu, nextTest, checkFunc) {
|
||||
cbs.addEventListener("received", function onreceived(event) {
|
||||
cbs.removeEventListener("received", onreceived);
|
||||
|
||||
checkFunc(event.message);
|
||||
|
||||
window.setTimeout(nextTest, 0);
|
||||
});
|
||||
|
||||
if (Array.isArray(pdu)) {
|
||||
repeat(sendCellBroadcastMessage, pdu);
|
||||
} else {
|
||||
sendCellBroadcastMessage(pdu);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests receiving Cell Broadcast messages, event instance type, all attributes
|
||||
* of CellBroadcastMessage exist.
|
||||
*/
|
||||
function testEtwsMessageAttributes() {
|
||||
log("Test ETWS Primary Notification message attributes");
|
||||
|
||||
cbs.addEventListener("received", function onreceived(event) {
|
||||
cbs.removeEventListener("received", onreceived);
|
||||
|
||||
// Bug 838542: following check throws an exception and fails this case.
|
||||
// ok(event instanceof MozCellBroadcastEvent,
|
||||
// "event is instanceof " + event.constructor)
|
||||
ok(event, "event is valid");
|
||||
|
||||
let message = event.message;
|
||||
ok(message, "event.message is valid");
|
||||
function testReceiving_ETWS_MessageAttributes() {
|
||||
log("Test receiving ETWS Primary Notification - Message Attributes");
|
||||
|
||||
let verifyCBMessage = (aMessage) => {
|
||||
// Attributes other than `language` and `body` should always be assigned.
|
||||
ok(message.gsmGeographicalScope != null, "message.gsmGeographicalScope");
|
||||
ok(message.messageCode != null, "message.messageCode");
|
||||
ok(message.messageId != null, "message.messageId");
|
||||
ok('language' in message, "message.language");
|
||||
ok(message.language == null, "message.language");
|
||||
ok('body' in message, "message.body");
|
||||
ok(message.body == null, "message.body");
|
||||
is(message.messageClass, "normal", "message.messageClass");
|
||||
ok(message.timestamp != null, "message.timestamp");
|
||||
ok(message.etws != null, "message.etws");
|
||||
ok(message.etws.warningType != null, "message.etws.warningType");
|
||||
ok(message.etws.emergencyUserAlert != null,
|
||||
"message.etws.emergencyUserAlert");
|
||||
ok(message.etws.popup != null, "message.etws.popup");
|
||||
ok(message.cdmaServiceCategory != null, "message.cdmaServiceCategory");
|
||||
|
||||
window.setTimeout(testReceiving_ETWS_GeographicalScope, 0);
|
||||
});
|
||||
ok(aMessage.gsmGeographicalScope != null, "aMessage.gsmGeographicalScope");
|
||||
ok(aMessage.messageCode != null, "aMessage.messageCode");
|
||||
ok(aMessage.messageId != null, "aMessage.messageId");
|
||||
ok('language' in aMessage, "aMessage.language");
|
||||
ok(aMessage.language == null, "aMessage.language");
|
||||
ok('body' in aMessage, "aMessage.body");
|
||||
ok(aMessage.body == null, "aMessage.body");
|
||||
is(aMessage.messageClass, "normal", "aMessage.messageClass");
|
||||
ok(aMessage.timestamp != null, "aMessage.timestamp");
|
||||
ok(aMessage.etws != null, "aMessage.etws");
|
||||
ok(aMessage.etws.warningType != null, "aMessage.etws.warningType");
|
||||
ok(aMessage.etws.emergencyUserAlert != null,
|
||||
"aMessage.etws.emergencyUserAlert");
|
||||
ok(aMessage.etws.popup != null, "aMessage.etws.popup");
|
||||
ok(aMessage.cdmaServiceCategory != null, "aMessage.cdmaServiceCategory");
|
||||
};
|
||||
|
||||
// Here we use a simple ETWS message for test.
|
||||
let pdu = buildHexStr(0, CB_MESSAGE_SIZE_ETWS * 2); // 6 octets
|
||||
sendCellBroadcastMessage(pdu);
|
||||
|
||||
return sendMultipleRawCbsToEmulatorAndWait([pdu])
|
||||
.then((aMessage) => verifyCBMessage(aMessage));
|
||||
}
|
||||
|
||||
function testReceiving_ETWS_GeographicalScope() {
|
||||
log("Test receiving ETWS Primary Notification - Geographical Scope");
|
||||
|
||||
function do_test(gs, nextTest) {
|
||||
// Here we use a simple ETWS message for test.
|
||||
let pdu = buildHexStr(((gs & 0x03) << 14), 4)
|
||||
let promise = Promise.resolve();
|
||||
|
||||
let verifyCBMessage = (aMessage, aGsName) => {
|
||||
is(aMessage.gsmGeographicalScope, aGsName,
|
||||
"aMessage.gsmGeographicalScope");
|
||||
};
|
||||
|
||||
CB_GSM_GEOGRAPHICAL_SCOPE_NAMES.forEach(function(aGsName, aIndex) {
|
||||
let pdu = buildHexStr(((aIndex & 0x03) << 14), 4)
|
||||
+ buildHexStr(0, (CB_MESSAGE_SIZE_ETWS - 2) * 2);
|
||||
promise = promise
|
||||
.then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
|
||||
.then((aMessage) => verifyCBMessage(aMessage, aGsName));
|
||||
});
|
||||
|
||||
doTestHelper(pdu, nextTest, function(message) {
|
||||
is(message.gsmGeographicalScope, CB_GSM_GEOGRAPHICAL_SCOPE_NAMES[gs],
|
||||
"message.gsmGeographicalScope");
|
||||
});
|
||||
}
|
||||
|
||||
repeat(do_test, seq(CB_GSM_GEOGRAPHICAL_SCOPE_NAMES.length),
|
||||
testReceiving_ETWS_MessageCode);
|
||||
return promise;
|
||||
}
|
||||
|
||||
function testReceiving_ETWS_MessageCode() {
|
||||
log("Test receiving ETWS Primary Notification - Message Code");
|
||||
|
||||
let promise = Promise.resolve();
|
||||
|
||||
// Message Code has 10 bits, and is ORed into a 16 bits 'serial' number. Here
|
||||
// we test every single bit to verify the operation doesn't go wrong.
|
||||
let messageCodesToTest = [
|
||||
let messageCodes = [
|
||||
0x000, 0x001, 0x002, 0x004, 0x008, 0x010, 0x020, 0x040,
|
||||
0x080, 0x100, 0x200, 0x251
|
||||
];
|
||||
|
||||
function do_test(messageCode, nextTest) {
|
||||
let pdu = buildHexStr(((messageCode & 0x3FF) << 4), 4)
|
||||
let verifyCBMessage = (aMessage, aMsgCode) => {
|
||||
is(aMessage.messageCode, aMsgCode, "aMessage.messageCode");
|
||||
};
|
||||
|
||||
messageCodes.forEach(function(aMsgCode) {
|
||||
let pdu = buildHexStr(((aMsgCode & 0x3FF) << 4), 4)
|
||||
+ buildHexStr(0, (CB_MESSAGE_SIZE_ETWS - 2) * 2);
|
||||
promise = promise
|
||||
.then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
|
||||
.then((aMessage) => verifyCBMessage(aMessage, aMsgCode));
|
||||
});
|
||||
|
||||
doTestHelper(pdu, nextTest, function(message) {
|
||||
is(message.messageCode, messageCode, "message.messageCode");
|
||||
});
|
||||
}
|
||||
|
||||
repeat(do_test, messageCodesToTest, testReceiving_ETWS_MessageId);
|
||||
return promise;
|
||||
}
|
||||
|
||||
function testReceiving_ETWS_MessageId() {
|
||||
log("Test receiving ETWS Primary Notification - Message Identifier");
|
||||
|
||||
let promise = Promise.resolve();
|
||||
|
||||
// Message Identifier has 16 bits, but no bitwise operation is needed.
|
||||
// Test some selected values only.
|
||||
let messageIdsToTest = [
|
||||
0x0000, 0x0001, 0x0010, 0x0100, 0x1000, 0x1111, 0x8888, 0x8811
|
||||
let messageIds = [
|
||||
0x0000, 0x0001, 0x0010, 0x0100, 0x1000, 0x1111, 0x8888, 0x8811,
|
||||
];
|
||||
|
||||
function do_test(messageId, nextTest) {
|
||||
let verifyCBMessage = (aMessage, aMessageId) => {
|
||||
is(aMessage.messageId, aMessageId, "aMessage.messageId");
|
||||
};
|
||||
|
||||
messageIds.forEach(function(aMessageId) {
|
||||
let pdu = buildHexStr(0, 4)
|
||||
+ buildHexStr((messageId & 0xFFFF), 4)
|
||||
+ buildHexStr((aMessageId & 0xFFFF), 4)
|
||||
+ buildHexStr(0, (CB_MESSAGE_SIZE_ETWS - 4) * 2);
|
||||
promise = promise
|
||||
.then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
|
||||
.then((aMessage) => verifyCBMessage(aMessage, aMessageId));
|
||||
});
|
||||
|
||||
doTestHelper(pdu, nextTest, function(message) {
|
||||
is(message.messageId, messageId, "message.messageId");
|
||||
});
|
||||
}
|
||||
|
||||
repeat(do_test, messageIdsToTest, testReceiving_ETWS_Timestamp);
|
||||
return promise;
|
||||
}
|
||||
|
||||
function testReceiving_ETWS_Timestamp() {
|
||||
log("Test receiving ETWS Primary Notification - Timestamp");
|
||||
|
||||
// Here we use a simple ETWS message for test.
|
||||
let pdu = buildHexStr(0, 12); // 6 octets
|
||||
doTestHelper(pdu, testReceiving_ETWS_WarningType, function(message) {
|
||||
let verifyCBMessage = (aMessage) => {
|
||||
// Cell Broadcast messages do not contain a timestamp field (however, ETWS
|
||||
// does). We only check the timestamp doesn't go too far (60 seconds) here.
|
||||
let msMessage = message.timestamp;
|
||||
let msMessage = aMessage.timestamp;
|
||||
let msNow = Date.now();
|
||||
ok(Math.abs(msMessage - msNow) < (1000 * 60), "message.timestamp");
|
||||
});
|
||||
ok(Math.abs(msMessage - msNow) < (1000 * 60), "aMessage.timestamp");
|
||||
};
|
||||
|
||||
// Here we use a simple ETWS message for test.
|
||||
let pdu = buildHexStr(0, 12); // 6 octets
|
||||
|
||||
return sendMultipleRawCbsToEmulatorAndWait([pdu])
|
||||
.then((aMessage) => verifyCBMessage(aMessage));
|
||||
}
|
||||
|
||||
function testReceiving_ETWS_WarningType() {
|
||||
log("Test receiving ETWS Primary Notification - Warning Type");
|
||||
|
||||
let promise = Promise.resolve();
|
||||
|
||||
// Warning Type has 7 bits, and is ORed into a 16 bits 'WarningType' field.
|
||||
// Here we test every single bit to verify the operation doesn't go wrong.
|
||||
let warningTypesToTest = [
|
||||
let warningTypes = [
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x08, 0x10, 0x20, 0x40, 0x41
|
||||
];
|
||||
|
||||
function do_test(warningType, nextTest) {
|
||||
let verifyCBMessage = (aMessage, aWarningType) => {
|
||||
ok(aMessage.etws, "aMessage.etws");
|
||||
is(aMessage.etws.warningType, CB_ETWS_WARNING_TYPE_NAMES[aWarningType],
|
||||
"aMessage.etws.warningType");
|
||||
};
|
||||
|
||||
warningTypes.forEach(function(aWarningType) {
|
||||
let pdu = buildHexStr(0, 8)
|
||||
+ buildHexStr(((warningType & 0x7F) << 9), 4)
|
||||
+ buildHexStr(((aWarningType & 0x7F) << 9), 4)
|
||||
+ buildHexStr(0, (CB_MESSAGE_SIZE_ETWS - 6) * 2);
|
||||
|
||||
doTestHelper(pdu, nextTest, function(message) {
|
||||
ok(message.etws, "message.etws");
|
||||
is(message.etws.warningType, CB_ETWS_WARNING_TYPE_NAMES[warningType],
|
||||
"message.etws.warningType");
|
||||
});
|
||||
}
|
||||
|
||||
repeat(do_test, warningTypesToTest, testReceiving_ETWS_EmergencyUserAlert);
|
||||
}
|
||||
|
||||
function doTestEmergencyUserAlert_or_Popup(name, mask, nextTest) {
|
||||
let pdu = buildHexStr(0, 8)
|
||||
+ buildHexStr(mask, 4)
|
||||
+ buildHexStr(0, (CB_MESSAGE_SIZE_ETWS - 6) * 2);
|
||||
doTestHelper(pdu, nextTest, function(message) {
|
||||
ok(message.etws != null, "message.etws");
|
||||
is(message.etws[name], mask != 0, "message.etws." + name);
|
||||
promise = promise
|
||||
.then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
|
||||
.then((aMessage) => verifyCBMessage(aMessage, aWarningType));
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
function testReceiving_ETWS_EmergencyUserAlert() {
|
||||
log("Test receiving ETWS Primary Notification - Emergency User Alert");
|
||||
|
||||
repeat(doTestEmergencyUserAlert_or_Popup.bind(null, "emergencyUserAlert"),
|
||||
[0x100, 0x000], testReceiving_ETWS_Popup);
|
||||
let promise = Promise.resolve();
|
||||
|
||||
let emergencyUserAlertMasks = [0x100, 0x000];
|
||||
|
||||
let verifyCBMessage = (aMessage, aMask) => {
|
||||
ok(aMessage.etws != null, "aMessage.etws");
|
||||
is(aMessage.etws.emergencyUserAlert, aMask != 0, "aMessage.etws.emergencyUserAlert");
|
||||
};
|
||||
|
||||
emergencyUserAlertMasks.forEach(function(aMask) {
|
||||
let pdu = buildHexStr(0, 8)
|
||||
+ buildHexStr(aMask, 4)
|
||||
+ buildHexStr(0, (CB_MESSAGE_SIZE_ETWS - 6) * 2);
|
||||
promise = promise
|
||||
.then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
|
||||
.then((aMessage) => verifyCBMessage(aMessage, aMask));
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
function testReceiving_ETWS_Popup() {
|
||||
log("Test receiving ETWS Primary Notification - Popup");
|
||||
|
||||
repeat(doTestEmergencyUserAlert_or_Popup.bind(null, "popup"),
|
||||
[0x80, 0x000], cleanUp);
|
||||
let promise = Promise.resolve();
|
||||
|
||||
let popupMasks = [0x80, 0x000];
|
||||
|
||||
let verifyCBMessage = (aMessage, aMask) => {
|
||||
ok(aMessage.etws != null, "aMessage.etws");
|
||||
is(aMessage.etws.popup, aMask != 0, "aMessage.etws.popup");
|
||||
};
|
||||
|
||||
popupMasks.forEach(function(aMask) {
|
||||
let pdu = buildHexStr(0, 8)
|
||||
+ buildHexStr(aMask, 4)
|
||||
+ buildHexStr(0, (CB_MESSAGE_SIZE_ETWS - 6) * 2);
|
||||
promise = promise
|
||||
.then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
|
||||
.then((aMessage) => verifyCBMessage(aMessage, aMask));
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
function cleanUp() {
|
||||
if (pendingEmulatorCmdCount > 0) {
|
||||
window.setTimeout(cleanUp, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
SpecialPowers.removePermission("mobileconnection", document);
|
||||
SpecialPowers.removePermission("cellbroadcast", true, document);
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
waitFor(testEtwsMessageAttributes, function() {
|
||||
return navigator.mozMobileConnections[0].voice.connected;
|
||||
startTestCommon(function testCaseMain() {
|
||||
return testReceiving_ETWS_MessageAttributes()
|
||||
.then(() => testReceiving_ETWS_GeographicalScope())
|
||||
.then(() => testReceiving_ETWS_MessageCode())
|
||||
.then(() => testReceiving_ETWS_MessageId())
|
||||
.then(() => testReceiving_ETWS_Timestamp())
|
||||
.then(() => testReceiving_ETWS_WarningType())
|
||||
.then(() => testReceiving_ETWS_EmergencyUserAlert())
|
||||
.then(() => testReceiving_ETWS_Popup());
|
||||
});
|
||||
|
||||
|
@ -1,59 +1,8 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 30000;
|
||||
|
||||
const PDU_DCS_CODING_GROUP_BITS = 0xF0;
|
||||
const PDU_DCS_MSG_CODING_7BITS_ALPHABET = 0x00;
|
||||
const PDU_DCS_MSG_CODING_8BITS_ALPHABET = 0x04;
|
||||
const PDU_DCS_MSG_CODING_16BITS_ALPHABET = 0x08;
|
||||
|
||||
const PDU_DCS_MSG_CLASS_BITS = 0x03;
|
||||
const PDU_DCS_MSG_CLASS_NORMAL = 0xFF;
|
||||
const PDU_DCS_MSG_CLASS_0 = 0x00;
|
||||
const PDU_DCS_MSG_CLASS_ME_SPECIFIC = 0x01;
|
||||
const PDU_DCS_MSG_CLASS_SIM_SPECIFIC = 0x02;
|
||||
const PDU_DCS_MSG_CLASS_TE_SPECIFIC = 0x03;
|
||||
const PDU_DCS_MSG_CLASS_USER_1 = 0x04;
|
||||
const PDU_DCS_MSG_CLASS_USER_2 = 0x05;
|
||||
|
||||
const GECKO_SMS_MESSAGE_CLASSES = {};
|
||||
GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_NORMAL] = "normal";
|
||||
GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_0] = "class-0";
|
||||
GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_ME_SPECIFIC] = "class-1";
|
||||
GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_SIM_SPECIFIC] = "class-2";
|
||||
GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_TE_SPECIFIC] = "class-3";
|
||||
GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_USER_1] = "user-1";
|
||||
GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_USER_2] = "user-2";
|
||||
|
||||
const CB_MESSAGE_SIZE_GSM = 88;
|
||||
|
||||
const CB_GSM_MESSAGEID_ETWS_BEGIN = 0x1100;
|
||||
const CB_GSM_MESSAGEID_ETWS_END = 0x1107;
|
||||
|
||||
const CB_GSM_GEOGRAPHICAL_SCOPE_NAMES = [
|
||||
"cell-immediate",
|
||||
"plmn",
|
||||
"location-area",
|
||||
"cell"
|
||||
];
|
||||
|
||||
const CB_ETWS_WARNING_TYPE_NAMES = [
|
||||
"earthquake",
|
||||
"tsunami",
|
||||
"earthquake-tsunami",
|
||||
"test",
|
||||
"other"
|
||||
];
|
||||
|
||||
const CB_DCS_LANG_GROUP_1 = [
|
||||
"de", "en", "it", "fr", "es", "nl", "sv", "da", "pt", "fi",
|
||||
"no", "el", "tr", "hu", "pl", null
|
||||
];
|
||||
const CB_DCS_LANG_GROUP_2 = [
|
||||
"cs", "he", "ar", "ru", "is", null, null, null, null, null,
|
||||
null, null, null, null, null, null
|
||||
];
|
||||
MARIONETTE_TIMEOUT = 60000;
|
||||
MARIONETTE_HEAD_JS = 'head.js';
|
||||
|
||||
const CB_MAX_CONTENT_7BIT = Math.floor((CB_MESSAGE_SIZE_GSM - 6) * 8 / 7);
|
||||
const CB_MAX_CONTENT_UCS2 = Math.floor((CB_MESSAGE_SIZE_GSM - 6) / 2);
|
||||
@ -70,412 +19,327 @@ const BODY_UCS2 = "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
|
||||
+ "\u0000"; // 41 unicode chars.
|
||||
const BODY_UCS2_IND = BODY_UCS2.substr(1);
|
||||
|
||||
SpecialPowers.addPermission("cellbroadcast", true, document);
|
||||
SpecialPowers.addPermission("mobileconnection", true, document);
|
||||
|
||||
is(BODY_7BITS.length, CB_MAX_CONTENT_7BIT, "BODY_7BITS.length");
|
||||
is(BODY_7BITS_IND.length, CB_MAX_CONTENT_7BIT - 3, "BODY_7BITS_IND.length");
|
||||
is(BODY_UCS2.length, CB_MAX_CONTENT_UCS2, "BODY_UCS2.length");
|
||||
is(BODY_UCS2_IND.length, CB_MAX_CONTENT_UCS2 - 1, "BODY_UCS2_IND.length")
|
||||
is(BODY_UCS2_IND.length, CB_MAX_CONTENT_UCS2 - 1, "BODY_UCS2_IND.length");
|
||||
|
||||
let cbs = window.navigator.mozCellBroadcast;
|
||||
ok(cbs instanceof window.MozCellBroadcast,
|
||||
"mozCellBroadcast is instanceof " + cbs.constructor);
|
||||
|
||||
let pendingEmulatorCmdCount = 0;
|
||||
function sendCellBroadcastMessage(pdu, callback) {
|
||||
pendingEmulatorCmdCount++;
|
||||
|
||||
let cmd = "cbs pdu " + pdu;
|
||||
runEmulatorCmd(cmd, function(result) {
|
||||
pendingEmulatorCmdCount--;
|
||||
|
||||
is(result[0], "OK", "Emulator response");
|
||||
|
||||
if (callback) {
|
||||
window.setTimeout(callback, 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function buildHexStr(n, numSemiOctets) {
|
||||
let str = n.toString(16);
|
||||
ok(str.length <= numSemiOctets);
|
||||
while (str.length < numSemiOctets) {
|
||||
str = "0" + str;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
function seq(end, begin) {
|
||||
let result = [];
|
||||
for (let i = begin || 0; i < end; i++) {
|
||||
result.push(i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function repeat(func, array, oncomplete) {
|
||||
(function do_call(index) {
|
||||
let next = index < (array.length - 1) ? do_call.bind(null, index + 1) : oncomplete;
|
||||
func.apply(null, [array[index], next]);
|
||||
})(0);
|
||||
}
|
||||
|
||||
function doTestHelper(pdu, nextTest, checkFunc) {
|
||||
cbs.addEventListener("received", function onreceived(event) {
|
||||
cbs.removeEventListener("received", onreceived);
|
||||
|
||||
checkFunc(event.message);
|
||||
|
||||
window.setTimeout(nextTest, 0);
|
||||
});
|
||||
|
||||
if (Array.isArray(pdu)) {
|
||||
repeat(sendCellBroadcastMessage, pdu);
|
||||
} else {
|
||||
sendCellBroadcastMessage(pdu);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests receiving Cell Broadcast messages, event instance type, all attributes
|
||||
* of CellBroadcastMessage exist.
|
||||
*/
|
||||
function testGsmMessageAttributes() {
|
||||
log("Test GSM Cell Broadcast message attributes");
|
||||
|
||||
cbs.addEventListener("received", function onreceived(event) {
|
||||
cbs.removeEventListener("received", onreceived);
|
||||
|
||||
// Bug 838542: following check throws an exception and fails this case.
|
||||
// ok(event instanceof MozCellBroadcastEvent,
|
||||
// "event is instanceof " + event.constructor)
|
||||
ok(event, "event is valid");
|
||||
|
||||
let message = event.message;
|
||||
ok(message, "event.message is valid");
|
||||
function testReceiving_GSM_MessageAttributes() {
|
||||
log("Test receiving GSM Cell Broadcast - Message Attributes");
|
||||
|
||||
let verifyCBMessage = (aMessage) => {
|
||||
// Attributes other than `language` and `body` should always be assigned.
|
||||
ok(message.gsmGeographicalScope != null, "message.gsmGeographicalScope");
|
||||
ok(message.messageCode != null, "message.messageCode");
|
||||
ok(message.messageId != null, "message.messageId");
|
||||
ok(message.language != null, "message.language");
|
||||
ok(message.body != null, "message.body");
|
||||
ok(message.messageClass != null, "message.messageClass");
|
||||
ok(message.timestamp != null, "message.timestamp");
|
||||
ok('etws' in message, "message.etws");
|
||||
if (message.etws) {
|
||||
ok('warningType' in message.etws, "message.etws.warningType");
|
||||
ok(message.etws.emergencyUserAlert != null, "message.etws.emergencyUserAlert");
|
||||
ok(message.etws.popup != null, "message.etws.popup");
|
||||
ok(aMessage.gsmGeographicalScope != null, "aMessage.gsmGeographicalScope");
|
||||
ok(aMessage.messageCode != null, "aMessage.messageCode");
|
||||
ok(aMessage.messageId != null, "aMessage.messageId");
|
||||
ok(aMessage.language != null, "aMessage.language");
|
||||
ok(aMessage.body != null, "aMessage.body");
|
||||
ok(aMessage.messageClass != null, "aMessage.messageClass");
|
||||
ok(aMessage.timestamp != null, "aMessage.timestamp");
|
||||
ok('etws' in aMessage, "aMessage.etws");
|
||||
if (aMessage.etws) {
|
||||
ok('warningType' in aMessage.etws, "aMessage.etws.warningType");
|
||||
ok(aMessage.etws.emergencyUserAlert != null, "aMessage.etws.emergencyUserAlert");
|
||||
ok(aMessage.etws.popup != null, "aMessage.etws.popup");
|
||||
}
|
||||
ok(message.cdmaServiceCategory != null, "message.cdmaServiceCategory");
|
||||
|
||||
window.setTimeout(testReceiving_GSM_GeographicalScope, 0);
|
||||
});
|
||||
ok(aMessage.cdmaServiceCategory != null, "aMessage.cdmaServiceCategory");
|
||||
};
|
||||
|
||||
// Here we use a simple GSM message for test.
|
||||
let pdu = buildHexStr(0, CB_MESSAGE_SIZE_GSM * 2);
|
||||
sendCellBroadcastMessage(pdu);
|
||||
|
||||
return sendMultipleRawCbsToEmulatorAndWait([pdu])
|
||||
.then((aMessage) => verifyCBMessage(aMessage));
|
||||
}
|
||||
|
||||
function testReceiving_GSM_GeographicalScope() {
|
||||
log("Test receiving GSM Cell Broadcast - Geographical Scope");
|
||||
|
||||
function do_test(gs, nextTest) {
|
||||
let pdu = buildHexStr(((gs & 0x03) << 14), 4)
|
||||
let promise = Promise.resolve();
|
||||
|
||||
let verifyCBMessage = (aMessage, aGsName) => {
|
||||
is(aMessage.gsmGeographicalScope, aGsName,
|
||||
"aMessage.gsmGeographicalScope");
|
||||
};
|
||||
|
||||
CB_GSM_GEOGRAPHICAL_SCOPE_NAMES.forEach(function(aGsName, aIndex) {
|
||||
let pdu = buildHexStr(((aIndex & 0x03) << 14), 4)
|
||||
+ buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 2) * 2);
|
||||
promise = promise
|
||||
.then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
|
||||
.then((aMessage) => verifyCBMessage(aMessage, aGsName));
|
||||
});
|
||||
|
||||
doTestHelper(pdu, nextTest, function(message) {
|
||||
is(message.gsmGeographicalScope, CB_GSM_GEOGRAPHICAL_SCOPE_NAMES[gs],
|
||||
"message.gsmGeographicalScope");
|
||||
});
|
||||
}
|
||||
|
||||
repeat(do_test, seq(CB_GSM_GEOGRAPHICAL_SCOPE_NAMES.length),
|
||||
testReceiving_GSM_MessageCode);
|
||||
return promise;
|
||||
}
|
||||
|
||||
function testReceiving_GSM_MessageCode() {
|
||||
log("Test receiving GSM Cell Broadcast - Message Code");
|
||||
|
||||
let promise = Promise.resolve();
|
||||
|
||||
// Message Code has 10 bits, and is ORed into a 16 bits 'serial' number. Here
|
||||
// we test every single bit to verify the operation doesn't go wrong.
|
||||
let messageCodesToTest = [
|
||||
let messageCodes = [
|
||||
0x000, 0x001, 0x002, 0x004, 0x008, 0x010, 0x020, 0x040,
|
||||
0x080, 0x100, 0x200, 0x251
|
||||
];
|
||||
|
||||
function do_test(messageCode, nextTest) {
|
||||
let pdu = buildHexStr(((messageCode & 0x3FF) << 4), 4)
|
||||
let verifyCBMessage = (aMessage, aMsgCode) => {
|
||||
is(aMessage.messageCode, aMsgCode, "aMessage.messageCode");
|
||||
};
|
||||
|
||||
messageCodes.forEach(function(aMsgCode) {
|
||||
let pdu = buildHexStr(((aMsgCode & 0x3FF) << 4), 4)
|
||||
+ buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 2) * 2);
|
||||
promise = promise
|
||||
.then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
|
||||
.then((aMessage) => verifyCBMessage(aMessage, aMsgCode));
|
||||
});
|
||||
|
||||
doTestHelper(pdu, nextTest, function(message) {
|
||||
is(message.messageCode, messageCode, "message.messageCode");
|
||||
});
|
||||
}
|
||||
|
||||
repeat(do_test, messageCodesToTest, testReceiving_GSM_MessageId);
|
||||
return promise;
|
||||
}
|
||||
|
||||
function testReceiving_GSM_MessageId() {
|
||||
log("Test receiving GSM Cell Broadcast - Message Identifier");
|
||||
|
||||
let promise = Promise.resolve();
|
||||
|
||||
// Message Identifier has 16 bits, but no bitwise operation is needed.
|
||||
// Test some selected values only.
|
||||
let messageIdsToTest = [
|
||||
let messageIds = [
|
||||
0x0000, 0x0001, 0x0010, 0x0100, 0x1000, 0x1111, 0x8888, 0x8811,
|
||||
];
|
||||
|
||||
function do_test(messageId, nextTest) {
|
||||
let verifyCBMessage = (aMessage, aMessageId) => {
|
||||
is(aMessage.messageId, aMessageId, "aMessage.messageId");
|
||||
ok(aMessage.etws == null, "aMessage.etws");
|
||||
};
|
||||
|
||||
messageIds.forEach(function(aMessageId) {
|
||||
let pdu = buildHexStr(0, 4)
|
||||
+ buildHexStr((messageId & 0xFFFF), 4)
|
||||
+ buildHexStr((aMessageId & 0xFFFF), 4)
|
||||
+ buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 4) * 2);
|
||||
promise = promise
|
||||
.then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
|
||||
.then((aMessage) => verifyCBMessage(aMessage, aMessageId));
|
||||
});
|
||||
|
||||
doTestHelper(pdu, nextTest, function(message) {
|
||||
is(message.messageId, messageId, "message.messageId");
|
||||
ok(message.etws == null, "message.etws");
|
||||
});
|
||||
}
|
||||
|
||||
repeat(do_test, messageIdsToTest, testReceiving_GSM_Language_and_Body);
|
||||
}
|
||||
|
||||
// Copied from GsmPDUHelper.readCbDataCodingScheme
|
||||
function decodeDataCodingScheme(dcs) {
|
||||
let language = null;
|
||||
let hasLanguageIndicator = false;
|
||||
let encoding = PDU_DCS_MSG_CODING_7BITS_ALPHABET;
|
||||
let messageClass = PDU_DCS_MSG_CLASS_NORMAL;
|
||||
|
||||
switch (dcs & PDU_DCS_CODING_GROUP_BITS) {
|
||||
case 0x00: // 0000
|
||||
language = CB_DCS_LANG_GROUP_1[dcs & 0x0F];
|
||||
break;
|
||||
|
||||
case 0x10: // 0001
|
||||
switch (dcs & 0x0F) {
|
||||
case 0x00:
|
||||
hasLanguageIndicator = true;
|
||||
break;
|
||||
case 0x01:
|
||||
encoding = PDU_DCS_MSG_CODING_16BITS_ALPHABET;
|
||||
hasLanguageIndicator = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x20: // 0010
|
||||
language = CB_DCS_LANG_GROUP_2[dcs & 0x0F];
|
||||
break;
|
||||
|
||||
case 0x40: // 01xx
|
||||
case 0x50:
|
||||
//case 0x60:
|
||||
//case 0x70:
|
||||
case 0x90: // 1001
|
||||
encoding = (dcs & 0x0C);
|
||||
if (encoding == 0x0C) {
|
||||
encoding = PDU_DCS_MSG_CODING_7BITS_ALPHABET;
|
||||
}
|
||||
messageClass = (dcs & PDU_DCS_MSG_CLASS_BITS);
|
||||
break;
|
||||
|
||||
case 0xF0:
|
||||
encoding = (dcs & 0x04) ? PDU_DCS_MSG_CODING_8BITS_ALPHABET
|
||||
: PDU_DCS_MSG_CODING_7BITS_ALPHABET;
|
||||
switch(dcs & PDU_DCS_MSG_CLASS_BITS) {
|
||||
case 0x01: messageClass = PDU_DCS_MSG_CLASS_USER_1; break;
|
||||
case 0x02: messageClass = PDU_DCS_MSG_CLASS_USER_2; break;
|
||||
case 0x03: messageClass = PDU_DCS_MSG_CLASS_TE_SPECIFIC; break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x30: // 0011 (Reserved)
|
||||
case 0x80: // 1000 (Reserved)
|
||||
case 0xA0: // 1010..1100 (Reserved)
|
||||
case 0xB0:
|
||||
case 0xC0:
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unsupported CBS data coding scheme: " + dcs);
|
||||
}
|
||||
|
||||
return [encoding, language, hasLanguageIndicator,
|
||||
GECKO_SMS_MESSAGE_CLASSES[messageClass]];
|
||||
return promise;
|
||||
}
|
||||
|
||||
function testReceiving_GSM_Language_and_Body() {
|
||||
log("Test receiving GSM Cell Broadcast - Language & Body");
|
||||
|
||||
function do_test(dcs) {
|
||||
let encoding, language, indicator, messageClass;
|
||||
let promise = Promise.resolve();
|
||||
|
||||
let testDcs = [];
|
||||
dcs = 0;
|
||||
while (dcs <= 0xFF) {
|
||||
try {
|
||||
[encoding, language, indicator, messageClass] = decodeDataCodingScheme(dcs);
|
||||
let dcsInfo = { dcs: dcs };
|
||||
[ dcsInfo.encoding, dcsInfo.language,
|
||||
dcsInfo.indicator, dcsInfo.messageClass ] = decodeGsmDataCodingScheme(dcs);
|
||||
testDcs.push(dcsInfo);
|
||||
} catch (e) {
|
||||
// Unsupported coding group, skip.
|
||||
let nextGroup = (dcs & PDU_DCS_CODING_GROUP_BITS) + 0x10;
|
||||
window.setTimeout(do_test.bind(null, nextGroup), 0);
|
||||
return;
|
||||
let dcs = (dcs & PDU_DCS_CODING_GROUP_BITS) + 0x10;
|
||||
}
|
||||
|
||||
let pdu = buildHexStr(0, 8)
|
||||
+ buildHexStr(dcs, 2)
|
||||
+ buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 5) * 2);
|
||||
|
||||
let nextTest = (dcs < 0xFF) ? do_test.bind(null, dcs + 1)
|
||||
: testReceiving_GSM_Timestamp;
|
||||
doTestHelper(pdu, nextTest, function(message) {
|
||||
if (language) {
|
||||
is(message.language, language, "message.language");
|
||||
} else if (indicator) {
|
||||
is(message.language, "@@", "message.language");
|
||||
} else {
|
||||
ok(message.language == null, "message.language");
|
||||
}
|
||||
|
||||
switch (encoding) {
|
||||
case PDU_DCS_MSG_CODING_7BITS_ALPHABET:
|
||||
is(message.body, indicator ? BODY_7BITS_IND : BODY_7BITS, "message.body");
|
||||
break;
|
||||
case PDU_DCS_MSG_CODING_8BITS_ALPHABET:
|
||||
ok(message.body == null, "message.body");
|
||||
break;
|
||||
case PDU_DCS_MSG_CODING_16BITS_ALPHABET:
|
||||
is(message.body, indicator ? BODY_UCS2_IND : BODY_UCS2, "message.body");
|
||||
break;
|
||||
}
|
||||
|
||||
is(message.messageClass, messageClass, "message.messageClass");
|
||||
});
|
||||
dcs++;
|
||||
}
|
||||
|
||||
do_test(0);
|
||||
let verifyCBMessage = (aMessage, aDcsInfo) => {
|
||||
if (aDcsInfo.language) {
|
||||
is(aMessage.language, aDcsInfo.language, "aMessage.language");
|
||||
} else if (aDcsInfo.indicator) {
|
||||
is(aMessage.language, "@@", "aMessage.language");
|
||||
} else {
|
||||
ok(aMessage.language == null, "aMessage.language");
|
||||
}
|
||||
|
||||
switch (aDcsInfo.encoding) {
|
||||
case PDU_DCS_MSG_CODING_7BITS_ALPHABET:
|
||||
is(aMessage.body, aDcsInfo.indicator ? BODY_7BITS_IND : BODY_7BITS, "aMessage.body");
|
||||
break;
|
||||
case PDU_DCS_MSG_CODING_8BITS_ALPHABET:
|
||||
ok(aMessage.body == null, "aMessage.body");
|
||||
break;
|
||||
case PDU_DCS_MSG_CODING_16BITS_ALPHABET:
|
||||
is(aMessage.body, aDcsInfo.indicator ? BODY_UCS2_IND : BODY_UCS2, "aMessage.body");
|
||||
break;
|
||||
}
|
||||
|
||||
is(aMessage.messageClass, aDcsInfo.messageClass, "aMessage.messageClass");
|
||||
};
|
||||
|
||||
testDcs.forEach(function(aDcsInfo) {
|
||||
let pdu = buildHexStr(0, 8)
|
||||
+ buildHexStr(aDcsInfo.dcs, 2)
|
||||
+ buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 5) * 2);
|
||||
promise = promise
|
||||
.then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
|
||||
.then((aMessage) => verifyCBMessage(aMessage, aDcsInfo));
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
function testReceiving_GSM_Timestamp() {
|
||||
log("Test receiving GSM Cell Broadcast - Timestamp");
|
||||
|
||||
let pdu = buildHexStr(0, CB_MESSAGE_SIZE_GSM * 2);
|
||||
doTestHelper(pdu, testReceiving_GSM_WarningType, function(message) {
|
||||
let verifyCBMessage = (aMessage) => {
|
||||
// Cell Broadcast messages do not contain a timestamp field (however, ETWS
|
||||
// does). We only check the timestamp doesn't go too far (60 seconds) here.
|
||||
let msMessage = message.timestamp;
|
||||
let msMessage = aMessage.timestamp;
|
||||
let msNow = Date.now();
|
||||
ok(Math.abs(msMessage - msNow) < (1000 * 60), "message.timestamp");
|
||||
});
|
||||
ok(Math.abs(msMessage - msNow) < (1000 * 60), "aMessage.timestamp");
|
||||
};
|
||||
|
||||
let pdu = buildHexStr(0, CB_MESSAGE_SIZE_GSM * 2);
|
||||
|
||||
return sendMultipleRawCbsToEmulatorAndWait([pdu])
|
||||
.then((aMessage) => verifyCBMessage(aMessage));
|
||||
}
|
||||
|
||||
function testReceiving_GSM_WarningType() {
|
||||
log("Test receiving GSM Cell Broadcast - Warning Type");
|
||||
|
||||
let messageIdsToTest = [];
|
||||
let promise = Promise.resolve();
|
||||
|
||||
let messageIds = [];
|
||||
for (let i = CB_GSM_MESSAGEID_ETWS_BEGIN; i <= CB_GSM_MESSAGEID_ETWS_END; i++) {
|
||||
messageIdsToTest.push(i);
|
||||
messageIds.push(i);
|
||||
}
|
||||
|
||||
function do_test(messageId, nextTest) {
|
||||
let verifyCBMessage = (aMessage, aMessageId) => {
|
||||
is(aMessage.messageId, aMessageId, "aMessage.messageId");
|
||||
ok(aMessage.etws != null, "aMessage.etws");
|
||||
|
||||
let offset = aMessageId - CB_GSM_MESSAGEID_ETWS_BEGIN;
|
||||
if (offset < CB_ETWS_WARNING_TYPE_NAMES.length) {
|
||||
is(aMessage.etws.warningType, CB_ETWS_WARNING_TYPE_NAMES[offset],
|
||||
"aMessage.etws.warningType");
|
||||
} else {
|
||||
ok(aMessage.etws.warningType == null, "aMessage.etws.warningType");
|
||||
}
|
||||
};
|
||||
|
||||
messageIds.forEach(function(aMessageId) {
|
||||
let pdu = buildHexStr(0, 4)
|
||||
+ buildHexStr((messageId & 0xFFFF), 4)
|
||||
+ buildHexStr((aMessageId & 0xFFFF), 4)
|
||||
+ buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 4) * 2);
|
||||
|
||||
doTestHelper(pdu, nextTest, function(message) {
|
||||
is(message.messageId, messageId, "message.messageId");
|
||||
ok(message.etws != null, "message.etws");
|
||||
|
||||
let offset = messageId - CB_GSM_MESSAGEID_ETWS_BEGIN;
|
||||
if (offset < CB_ETWS_WARNING_TYPE_NAMES.length) {
|
||||
is(message.etws.warningType, CB_ETWS_WARNING_TYPE_NAMES[offset],
|
||||
"message.etws.warningType");
|
||||
} else {
|
||||
ok(message.etws.warningType == null, "message.etws.warningType");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
repeat(do_test, messageIdsToTest, testReceiving_GSM_EmergencyUserAlert);
|
||||
}
|
||||
|
||||
function doTestEmergencyUserAlert_or_Popup(name, mask, nextTest) {
|
||||
let pdu = buildHexStr(mask, 4)
|
||||
+ buildHexStr(CB_GSM_MESSAGEID_ETWS_BEGIN, 4)
|
||||
+ buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 4) * 2);
|
||||
|
||||
doTestHelper(pdu, nextTest, function(message) {
|
||||
is(message.messageId, CB_GSM_MESSAGEID_ETWS_BEGIN, "message.messageId");
|
||||
ok(message.etws != null, "message.etws");
|
||||
is(message.etws[name], mask != 0, "message.etws." + name);
|
||||
promise = promise
|
||||
.then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
|
||||
.then((aMessage) => verifyCBMessage(aMessage, aMessageId));
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
function testReceiving_GSM_EmergencyUserAlert() {
|
||||
log("Test receiving GSM Cell Broadcast - Emergency User Alert");
|
||||
|
||||
repeat(doTestEmergencyUserAlert_or_Popup.bind(null, "emergencyUserAlert"),
|
||||
[0x2000, 0x0000], testReceiving_GSM_Popup);
|
||||
let promise = Promise.resolve();
|
||||
|
||||
let emergencyUserAlertMasks = [0x2000, 0x0000];
|
||||
|
||||
let verifyCBMessage = (aMessage, aMask) => {
|
||||
is(aMessage.messageId, CB_GSM_MESSAGEID_ETWS_BEGIN, "aMessage.messageId");
|
||||
ok(aMessage.etws != null, "aMessage.etws");
|
||||
is(aMessage.etws.emergencyUserAlert, aMask != 0, "aMessage.etws.emergencyUserAlert");
|
||||
};
|
||||
|
||||
emergencyUserAlertMasks.forEach(function(aMask) {
|
||||
let pdu = buildHexStr(aMask, 4)
|
||||
+ buildHexStr(CB_GSM_MESSAGEID_ETWS_BEGIN, 4)
|
||||
+ buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 4) * 2);
|
||||
promise = promise
|
||||
.then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
|
||||
.then((aMessage) => verifyCBMessage(aMessage, aMask));
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
function testReceiving_GSM_Popup() {
|
||||
log("Test receiving GSM Cell Broadcast - Popup");
|
||||
|
||||
repeat(doTestEmergencyUserAlert_or_Popup.bind(null, "popup"),
|
||||
[0x1000, 0x0000], testReceiving_GSM_Multipart);
|
||||
let promise = Promise.resolve();
|
||||
|
||||
let popupMasks = [0x1000, 0x0000];
|
||||
|
||||
let verifyCBMessage = (aMessage, aMask) => {
|
||||
is(aMessage.messageId, CB_GSM_MESSAGEID_ETWS_BEGIN, "aMessage.messageId");
|
||||
ok(aMessage.etws != null, "aMessage.etws");
|
||||
is(aMessage.etws.popup, aMask != 0, "aMessage.etws.popup");
|
||||
};
|
||||
|
||||
popupMasks.forEach(function(aMask) {
|
||||
let pdu = buildHexStr(aMask, 4)
|
||||
+ buildHexStr(CB_GSM_MESSAGEID_ETWS_BEGIN, 4)
|
||||
+ buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 4) * 2);
|
||||
promise = promise
|
||||
.then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
|
||||
.then((aMessage) => verifyCBMessage(aMessage, aMask));
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
function testReceiving_GSM_Multipart() {
|
||||
log("Test receiving GSM Cell Broadcast - Multipart Messages");
|
||||
|
||||
function do_test(numParts, nextTest) {
|
||||
let promise = Promise.resolve();
|
||||
|
||||
// According to 9.4.1.2.4 Page Parameter in TS 23.041, the maximal Number of
|
||||
// pages per CB message is 15.
|
||||
let numParts = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
|
||||
|
||||
let verifyCBMessage = (aMessage, aNumParts) => {
|
||||
is(aMessage.body.length, (aNumParts * CB_MAX_CONTENT_7BIT),
|
||||
"aMessage.body");
|
||||
};
|
||||
|
||||
numParts.forEach(function(aNumParts) {
|
||||
let pdus = [];
|
||||
for (let i = 1; i <= numParts; i++) {
|
||||
for (let i = 1; i <= aNumParts; i++) {
|
||||
let pdu = buildHexStr(0, 10)
|
||||
+ buildHexStr((i << 4) + numParts, 2)
|
||||
+ buildHexStr((i << 4) + aNumParts, 2)
|
||||
+ buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 6) * 2);
|
||||
pdus.push(pdu);
|
||||
}
|
||||
promise = promise
|
||||
.then(() => sendMultipleRawCbsToEmulatorAndWait(pdus))
|
||||
.then((aMessage) => verifyCBMessage(aMessage, aNumParts));
|
||||
});
|
||||
|
||||
doTestHelper(pdus, nextTest, function(message) {
|
||||
is(message.body.length, (numParts * CB_MAX_CONTENT_7BIT),
|
||||
"message.body");
|
||||
});
|
||||
}
|
||||
|
||||
repeat(do_test, seq(16, 1), testReceiving_GSM_ServiceCategory);
|
||||
return promise;
|
||||
}
|
||||
|
||||
function testReceiving_GSM_ServiceCategory() {
|
||||
log("Test receiving GSM Cell Broadcast - Service Category");
|
||||
|
||||
cbs.addEventListener("received", function onreceived(event) {
|
||||
cbs.removeEventListener("received", onreceived);
|
||||
|
||||
let message = event.message;
|
||||
|
||||
let verifyCBMessage = (aMessage) => {
|
||||
// Bug 910091
|
||||
// "Service Category" is not defined in GSM. We should always get '0' here.
|
||||
is(message.cdmaServiceCategory, 0, "message.cdmaServiceCategory");
|
||||
|
||||
window.setTimeout(cleanUp, 0);
|
||||
});
|
||||
is(aMessage.cdmaServiceCategory, 0, "aMessage.cdmaServiceCategory");
|
||||
};
|
||||
|
||||
let pdu = buildHexStr(0, CB_MESSAGE_SIZE_GSM * 2);
|
||||
sendCellBroadcastMessage(pdu);
|
||||
return sendMultipleRawCbsToEmulatorAndWait([pdu])
|
||||
.then((aMessage) => verifyCBMessage(aMessage));
|
||||
}
|
||||
|
||||
function cleanUp() {
|
||||
if (pendingEmulatorCmdCount > 0) {
|
||||
window.setTimeout(cleanUp, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
SpecialPowers.removePermission("mobileconnection", document);
|
||||
SpecialPowers.removePermission("cellbroadcast", true, document);
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
waitFor(testGsmMessageAttributes, function() {
|
||||
return navigator.mozMobileConnections[0].voice.connected;
|
||||
startTestCommon(function testCaseMain() {
|
||||
return testReceiving_GSM_MessageAttributes()
|
||||
.then(() => testReceiving_GSM_GeographicalScope())
|
||||
.then(() => testReceiving_GSM_MessageCode())
|
||||
.then(() => testReceiving_GSM_MessageId())
|
||||
.then(() => testReceiving_GSM_Language_and_Body())
|
||||
.then(() => testReceiving_GSM_Timestamp())
|
||||
.then(() => testReceiving_GSM_WarningType())
|
||||
.then(() => testReceiving_GSM_EmergencyUserAlert())
|
||||
.then(() => testReceiving_GSM_Popup())
|
||||
.then(() => testReceiving_GSM_Multipart())
|
||||
.then(() => testReceiving_GSM_ServiceCategory());
|
||||
});
|
||||
|
||||
|
@ -1,19 +1,17 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 60000;
|
||||
MARIONETTE_TIMEOUT = 10000;
|
||||
MARIONETTE_HEAD_JS = 'head.js';
|
||||
|
||||
const BODY_7BITS = "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
|
||||
+ "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
|
||||
+ "@@@@@@@@@@@@@"; // 93 ascii chars.
|
||||
const CB_PDU_SIZE = 88;
|
||||
|
||||
function testReceivingMultiSIM() {
|
||||
let CB_PDU = "";
|
||||
while (CB_PDU.length < CB_PDU_SIZE * 2) {
|
||||
CB_PDU += "00";
|
||||
}
|
||||
function testReceiving_MultiSIM() {
|
||||
log("Test receiving GSM Cell Broadcast - Multi-SIM");
|
||||
|
||||
let pdu = buildHexStr(0, CB_MESSAGE_SIZE_GSM * 2);
|
||||
|
||||
let verifyCBMessage = (aMessage, aServiceId) => {
|
||||
log("Verify CB message received from serviceId: " + aServiceId);
|
||||
@ -22,13 +20,13 @@ function testReceivingMultiSIM() {
|
||||
};
|
||||
|
||||
return selectModem(1)
|
||||
.then(() => sendMultipleRawCbsToEmulatorAndWait([CB_PDU]))
|
||||
.then((results) => verifyCBMessage(results[0].message, 1))
|
||||
.then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
|
||||
.then((aMessage) => verifyCBMessage(aMessage, 1))
|
||||
.then(() => selectModem(0))
|
||||
.then(() => sendMultipleRawCbsToEmulatorAndWait([CB_PDU]))
|
||||
.then((results) => verifyCBMessage(results[0].message, 0));
|
||||
.then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
|
||||
.then((aMessage) => verifyCBMessage(aMessage, 0));
|
||||
}
|
||||
|
||||
startTestCommon(function testCaseMain() {
|
||||
return runIfMultiSIM(testReceivingMultiSIM);
|
||||
return runIfMultiSIM(testReceiving_MultiSIM);
|
||||
});
|
||||
|
@ -185,19 +185,6 @@ ContactManager.prototype = {
|
||||
Services.DOMRequest.fireError(req.cursor, msg.errorMsg);
|
||||
}
|
||||
break;
|
||||
case "PermissionPromptHelper:AskPermission:OK":
|
||||
if (DEBUG) debug("id: " + msg.requestID);
|
||||
req = this.getRequest(msg.requestID);
|
||||
if (!req) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (msg.result == Ci.nsIPermissionManager.ALLOW_ACTION) {
|
||||
req.allow();
|
||||
} else {
|
||||
req.cancel();
|
||||
}
|
||||
break;
|
||||
case "Contact:Changed":
|
||||
// Fire oncontactchange event
|
||||
if (DEBUG) debug("Contacts:ContactChanged: " + msg.contactID + ", " + msg.reason);
|
||||
@ -235,6 +222,7 @@ ContactManager.prototype = {
|
||||
|
||||
askPermission: function (aAccess, aRequest, aAllowCallback, aCancelCallback) {
|
||||
if (DEBUG) debug("askPermission for contacts");
|
||||
|
||||
let access;
|
||||
switch(aAccess) {
|
||||
case "create":
|
||||
@ -255,38 +243,42 @@ ContactManager.prototype = {
|
||||
}
|
||||
|
||||
// Shortcut for ALLOW_ACTION so we avoid a parent roundtrip
|
||||
let principal = this._window.document.nodePrincipal;
|
||||
let type = "contacts-" + access;
|
||||
let permValue =
|
||||
Services.perms.testExactPermissionFromPrincipal(this._window.document.nodePrincipal, type);
|
||||
Services.perms.testExactPermissionFromPrincipal(principal, type);
|
||||
if (permValue == Ci.nsIPermissionManager.ALLOW_ACTION) {
|
||||
aAllowCallback();
|
||||
return;
|
||||
} else if (permValue == Ci.nsIPermissionManager.DENY_ACTION) {
|
||||
aCancelCallback();
|
||||
}
|
||||
|
||||
let requestID = this.getRequestId({
|
||||
request: aRequest,
|
||||
allow: function() {
|
||||
aAllowCallback();
|
||||
}.bind(this),
|
||||
cancel : function() {
|
||||
if (aCancelCallback) {
|
||||
aCancelCallback()
|
||||
} else if (aRequest) {
|
||||
Services.DOMRequest.fireError(aRequest, "Not Allowed");
|
||||
}
|
||||
}.bind(this)
|
||||
});
|
||||
|
||||
let principal = this._window.document.nodePrincipal;
|
||||
cpmm.sendAsyncMessage("PermissionPromptHelper:AskPermission", {
|
||||
// Create an array with a single nsIContentPermissionType element.
|
||||
let type = {
|
||||
type: "contacts",
|
||||
access: access,
|
||||
requestID: requestID,
|
||||
origin: principal.origin,
|
||||
appID: principal.appId,
|
||||
browserFlag: principal.isInBrowserElement,
|
||||
windowID: this._window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).outerWindowID
|
||||
});
|
||||
options: null,
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionType])
|
||||
};
|
||||
let typeArray = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
|
||||
typeArray.appendElement(type, false);
|
||||
|
||||
// create a nsIContentPermissionRequest
|
||||
let request = {
|
||||
types: typeArray,
|
||||
principal: principal,
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionRequest]),
|
||||
allow: aAllowCallback,
|
||||
cancel: aCancelCallback,
|
||||
window: this._window
|
||||
};
|
||||
|
||||
// Using askPermission from nsIDOMWindowUtils that takes care of the
|
||||
// remoting if needed.
|
||||
let windowUtils = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
windowUtils.askPermission(request);
|
||||
},
|
||||
|
||||
save: function save(aContact) {
|
||||
@ -464,7 +456,6 @@ ContactManager.prototype = {
|
||||
"Contact:Save:Return:OK", "Contact:Save:Return:KO",
|
||||
"Contact:Remove:Return:OK", "Contact:Remove:Return:KO",
|
||||
"Contact:Changed",
|
||||
"PermissionPromptHelper:AskPermission:OK",
|
||||
"Contacts:GetAll:Next", "Contacts:GetAll:Return:KO",
|
||||
"Contacts:Count",
|
||||
"Contacts:Revision", "Contacts:GetRevision:Return:KO",]);
|
||||
|
@ -3,7 +3,6 @@
|
||||
// Fix the environment to run Contacts tests
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/ContactService.jsm");
|
||||
SpecialPowers.Cu.import("resource://gre/modules/PermissionPromptHelper.jsm");
|
||||
}
|
||||
|
||||
SpecialPowers.addPermission("contacts-write", true, document);
|
||||
|
@ -427,6 +427,22 @@ TestArray.addTest(
|
||||
}
|
||||
);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
TestArray.addTest(
|
||||
"Fail cleanly when NSS refuses to generate a key pair",
|
||||
function() {
|
||||
var that = this;
|
||||
var alg = {
|
||||
name: "RSAES-PKCS1-v1_5",
|
||||
modulusLength: 2299, // NSS does not like this key length
|
||||
publicExponent: new Uint8Array([0x01, 0x00, 0x01])
|
||||
};
|
||||
|
||||
crypto.subtle.generateKey(alg, false, ["encrypt"])
|
||||
.then( error(that), complete(that) );
|
||||
}
|
||||
);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
TestArray.addTest(
|
||||
"SHA-256 digest",
|
||||
|
@ -58,6 +58,11 @@ DeviceStorageRequestChild::
|
||||
mCallback = nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> window = mRequest->GetOwner();
|
||||
if (!window) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (aValue.type()) {
|
||||
|
||||
case DeviceStorageResponseValue::TErrorResponse:
|
||||
@ -73,7 +78,7 @@ DeviceStorageRequestChild::
|
||||
mDSFile->GetFullPath(fullPath);
|
||||
AutoJSContext cx;
|
||||
JS::Rooted<JS::Value> result(cx);
|
||||
StringToJsval(mRequest->GetOwner(), fullPath, &result);
|
||||
StringToJsval(window, fullPath, &result);
|
||||
mRequest->FireSuccess(result);
|
||||
break;
|
||||
}
|
||||
@ -86,7 +91,7 @@ DeviceStorageRequestChild::
|
||||
mDSFile->GetFullPath(fullPath);
|
||||
AutoJSContext cx;
|
||||
JS::Rooted<JS::Value> result(cx);
|
||||
StringToJsval(mRequest->GetOwner(), fullPath, &result);
|
||||
StringToJsval(window, fullPath, &result);
|
||||
|
||||
mDSFileDescriptor->mDSFile = mDSFile;
|
||||
mDSFileDescriptor->mFileDescriptor = r.fileDescriptor();
|
||||
@ -103,7 +108,7 @@ DeviceStorageRequestChild::
|
||||
nsCOMPtr<nsIDOMFile> file = do_QueryInterface(blob);
|
||||
AutoJSContext cx;
|
||||
JS::Rooted<JS::Value> result(cx,
|
||||
InterfaceToJsval(mRequest->GetOwner(), file, &NS_GET_IID(nsIDOMFile)));
|
||||
InterfaceToJsval(window, file, &NS_GET_IID(nsIDOMFile)));
|
||||
mRequest->FireSuccess(result);
|
||||
break;
|
||||
}
|
||||
@ -131,7 +136,7 @@ DeviceStorageRequestChild::
|
||||
AvailableStorageResponse r = aValue;
|
||||
AutoJSContext cx;
|
||||
JS::Rooted<JS::Value> result(cx);
|
||||
StringToJsval(mRequest->GetOwner(), r.mountState(), &result);
|
||||
StringToJsval(window, r.mountState(), &result);
|
||||
mRequest->FireSuccess(result);
|
||||
break;
|
||||
}
|
||||
@ -141,7 +146,7 @@ DeviceStorageRequestChild::
|
||||
StorageStatusResponse r = aValue;
|
||||
AutoJSContext cx;
|
||||
JS::Rooted<JS::Value> result(cx);
|
||||
StringToJsval(mRequest->GetOwner(), r.storageStatus(), &result);
|
||||
StringToJsval(window, r.storageStatus(), &result);
|
||||
mRequest->FireSuccess(result);
|
||||
break;
|
||||
}
|
||||
@ -151,7 +156,7 @@ DeviceStorageRequestChild::
|
||||
FormatStorageResponse r = aValue;
|
||||
AutoJSContext cx;
|
||||
JS::Rooted<JS::Value> result(cx);
|
||||
StringToJsval(mRequest->GetOwner(), r.mountState(), &result);
|
||||
StringToJsval(window, r.mountState(), &result);
|
||||
mRequest->FireSuccess(result);
|
||||
break;
|
||||
}
|
||||
@ -161,7 +166,7 @@ DeviceStorageRequestChild::
|
||||
MountStorageResponse r = aValue;
|
||||
AutoJSContext cx;
|
||||
JS::Rooted<JS::Value> result(cx);
|
||||
StringToJsval(mRequest->GetOwner(), r.storageStatus(), &result);
|
||||
StringToJsval(window, r.storageStatus(), &result);
|
||||
mRequest->FireSuccess(result);
|
||||
break;
|
||||
}
|
||||
@ -171,7 +176,7 @@ DeviceStorageRequestChild::
|
||||
UnmountStorageResponse r = aValue;
|
||||
AutoJSContext cx;
|
||||
JS::Rooted<JS::Value> result(cx);
|
||||
StringToJsval(mRequest->GetOwner(), r.storageStatus(), &result);
|
||||
StringToJsval(window, r.storageStatus(), &result);
|
||||
mRequest->FireSuccess(result);
|
||||
break;
|
||||
}
|
||||
|
@ -1816,6 +1816,9 @@ public:
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!mRequest->GetOwner()) {
|
||||
return NS_OK;
|
||||
}
|
||||
mRequest->FireError(mError);
|
||||
mRequest = nullptr;
|
||||
return NS_OK;
|
||||
@ -1906,13 +1909,18 @@ ContinueCursorEvent::Continue()
|
||||
NS_IMETHODIMP
|
||||
ContinueCursorEvent::Run()
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> window = mRequest->GetOwner();
|
||||
if (!window) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsRefPtr<DeviceStorageFile> file = GetNextFile();
|
||||
|
||||
nsDOMDeviceStorageCursor* cursor
|
||||
= static_cast<nsDOMDeviceStorageCursor*>(mRequest.get());
|
||||
|
||||
AutoJSContext cx;
|
||||
JS::Rooted<JS::Value> val(cx, nsIFileToJsval(cursor->GetOwner(), file));
|
||||
JS::Rooted<JS::Value> val(cx, nsIFileToJsval(window, file));
|
||||
|
||||
if (file) {
|
||||
cursor->mOkToCallContinue = true;
|
||||
@ -2135,6 +2143,10 @@ public:
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsCOMPtr<nsPIDOMWindow> window = mRequest->GetOwner();
|
||||
if (!window) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsString state = NS_LITERAL_STRING("unavailable");
|
||||
if (mFile) {
|
||||
@ -2143,7 +2155,7 @@ public:
|
||||
|
||||
AutoJSContext cx;
|
||||
JS::Rooted<JS::Value> result(cx);
|
||||
StringToJsval(mRequest->GetOwner(), state, &result);
|
||||
StringToJsval(window, state, &result);
|
||||
mRequest->FireSuccess(result);
|
||||
mRequest = nullptr;
|
||||
return NS_OK;
|
||||
@ -2169,6 +2181,10 @@ public:
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsCOMPtr<nsPIDOMWindow> window = mRequest->GetOwner();
|
||||
if (!window) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsString state = NS_LITERAL_STRING("undefined");
|
||||
if (mFile) {
|
||||
@ -2177,7 +2193,7 @@ public:
|
||||
|
||||
AutoJSContext cx;
|
||||
JS::Rooted<JS::Value> result(cx);
|
||||
StringToJsval(mRequest->GetOwner(), state, &result);
|
||||
StringToJsval(window, state, &result);
|
||||
mRequest->FireSuccess(result);
|
||||
mRequest = nullptr;
|
||||
return NS_OK;
|
||||
@ -2203,6 +2219,10 @@ public:
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsCOMPtr<nsPIDOMWindow> window = mRequest->GetOwner();
|
||||
if (!window) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsString state = NS_LITERAL_STRING("unavailable");
|
||||
if (mFile) {
|
||||
@ -2211,7 +2231,7 @@ public:
|
||||
|
||||
AutoJSContext cx;
|
||||
JS::Rooted<JS::Value> result(cx);
|
||||
StringToJsval(mRequest->GetOwner(), state, &result);
|
||||
StringToJsval(window, state, &result);
|
||||
mRequest->FireSuccess(result);
|
||||
mRequest = nullptr;
|
||||
return NS_OK;
|
||||
@ -2237,6 +2257,10 @@ public:
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsCOMPtr<nsPIDOMWindow> window = mRequest->GetOwner();
|
||||
if (!window) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsString state = NS_LITERAL_STRING("unavailable");
|
||||
if (mFile) {
|
||||
@ -2245,7 +2269,7 @@ public:
|
||||
|
||||
AutoJSContext cx;
|
||||
JS::Rooted<JS::Value> result(cx);
|
||||
StringToJsval(mRequest->GetOwner(), state, &result);
|
||||
StringToJsval(window, state, &result);
|
||||
mRequest->FireSuccess(result);
|
||||
mRequest = nullptr;
|
||||
return NS_OK;
|
||||
@ -2271,6 +2295,10 @@ public:
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsCOMPtr<nsPIDOMWindow> window = mRequest->GetOwner();
|
||||
if (!window) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsString state = NS_LITERAL_STRING("unavailable");
|
||||
if (mFile) {
|
||||
@ -2279,7 +2307,7 @@ public:
|
||||
|
||||
AutoJSContext cx;
|
||||
JS::Rooted<JS::Value> result(cx);
|
||||
StringToJsval(mRequest->GetOwner(), state, &result);
|
||||
StringToJsval(window, state, &result);
|
||||
mRequest->FireSuccess(result);
|
||||
mRequest = nullptr;
|
||||
return NS_OK;
|
||||
@ -2322,10 +2350,13 @@ public:
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsCOMPtr<nsPIDOMWindow> window = mRequest->GetOwner();
|
||||
if (!window) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
AutoJSContext cx;
|
||||
JS::Rooted<JS::Value> result(cx, JSVAL_NULL);
|
||||
nsPIDOMWindow* window = mRequest->GetOwner();
|
||||
|
||||
if (mFile) {
|
||||
result = nsIFileToJsval(window, mFile);
|
||||
|
@ -3,15 +3,22 @@
|
||||
<script>
|
||||
var im = navigator.mozInputMethod;
|
||||
if (im) {
|
||||
// Automatically append location hash to current input field.
|
||||
im.oninputcontextchange = function() {
|
||||
var ctx = im.inputcontext;
|
||||
if (ctx) {
|
||||
intervalId = setTimeout(function() {
|
||||
ctx.replaceSurroundingText(location.hash);
|
||||
}, 0);
|
||||
}
|
||||
};
|
||||
im.oninputcontextchange = onIcc;
|
||||
|
||||
if (im.inputcontext) {
|
||||
onIcc();
|
||||
}
|
||||
}
|
||||
|
||||
function onIcc() {
|
||||
var ctx = im.inputcontext;
|
||||
if (ctx) {
|
||||
ctx.replaceSurroundingText(location.hash).then(function() {
|
||||
/* Happy flow */
|
||||
}, function(err) {
|
||||
dump('ReplaceSurroundingText failed ' + err + '\n');
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
@ -11,6 +11,7 @@ support-files =
|
||||
[test_basic.html]
|
||||
[test_bug944397.html]
|
||||
[test_bug949059.html]
|
||||
[test_bug953044.html]
|
||||
[test_bug960946.html]
|
||||
[test_bug978918.html]
|
||||
[test_delete_focused_element.html]
|
||||
|
@ -23,85 +23,85 @@ inputmethod_setup(function() {
|
||||
function appFrameScript() {
|
||||
let input = content.document.getElementById('test-input');
|
||||
input.oninput = function() {
|
||||
dump('oninput was called in file_test_app.html.');
|
||||
sendAsyncMessage('test:InputMethod:oninput', {});
|
||||
sendAsyncMessage('test:InputMethod:oninput', {
|
||||
value: input.value
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
* Bug 957213. Sometimes we need to refocus the input field to avoid
|
||||
* intermittent test failure.
|
||||
*/
|
||||
content.setInterval(function() {
|
||||
input.focus();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
let timeoutId = null;
|
||||
let app, keyboard;
|
||||
|
||||
// Create an app frame to recieve keyboard inputs.
|
||||
let app = document.createElement('iframe');
|
||||
app.src = 'file_test_app.html';
|
||||
app.setAttribute('mozbrowser', true);
|
||||
document.body.appendChild(app);
|
||||
app.addEventListener('mozbrowserloadend', function() {
|
||||
let mm = SpecialPowers.getBrowserFrameMessageManager(app);
|
||||
mm.loadFrameScript('data:,(' + appFrameScript.toString() + ')();', false);
|
||||
mm.addMessageListener("test:InputMethod:oninput", function() {
|
||||
ok(true, 'Keyboard input was received.');
|
||||
clearTimeout(timeoutId);
|
||||
inputmethod_cleanup();
|
||||
});
|
||||
});
|
||||
|
||||
// Create a browser frame to load the input method app.
|
||||
let keyboard = document.createElement('iframe');
|
||||
keyboard.setAttribute('mozbrowser', true);
|
||||
document.body.appendChild(keyboard);
|
||||
|
||||
// Bug 953044 setInputMethodActive(false) before input method app loads should
|
||||
// always succeed.
|
||||
let req = keyboard.setInputMethodActive(false);
|
||||
req.onsuccess = function() {
|
||||
ok(true, 'setInputMethodActive before loading succeeded.');
|
||||
};
|
||||
|
||||
req.onerror = function() {
|
||||
ok(false, 'setInputMethodActive before loading failed: ' + this.error.name);
|
||||
clearTimeout(timeoutId);
|
||||
inputmethod_cleanup();
|
||||
};
|
||||
/**
|
||||
* So this test does the following:
|
||||
* 1. Create a mozbrowser iframe with a text field in it, and focus the text field
|
||||
* 2. 100ms. after loading we create new keyboard iframe, that will try to execute
|
||||
* replaceSurroundingText on the current active inputcontext
|
||||
* 3. That should trigger 'input' event on the said text field
|
||||
* 4. And if that happens we know everything is OK
|
||||
*/
|
||||
|
||||
let path = location.pathname;
|
||||
let imeUrl = location.protocol + '//' + location.host +
|
||||
path.substring(0, path.lastIndexOf('/')) +
|
||||
'/file_inputmethod.html#data';
|
||||
SpecialPowers.pushPermissions([{
|
||||
type: 'input',
|
||||
allow: true,
|
||||
context: imeUrl
|
||||
}], function() {
|
||||
let req = keyboard.setInputMethodActive(true);
|
||||
let basePath = location.protocol + '//' + location.host +
|
||||
path.substring(0, path.lastIndexOf('/'));
|
||||
|
||||
req.onsuccess = function() {
|
||||
ok(true, 'setInputMethodActive succeeded.');
|
||||
};
|
||||
// STEP 1: Create an app frame to recieve keyboard inputs.
|
||||
function step1() {
|
||||
app = document.createElement('iframe');
|
||||
app.src = basePath + '/file_test_app.html';
|
||||
app.setAttribute('mozbrowser', true);
|
||||
document.body.appendChild(app);
|
||||
app.addEventListener('mozbrowserloadend', function() {
|
||||
let mm = SpecialPowers.getBrowserFrameMessageManager(app);
|
||||
mm.loadFrameScript('data:,(' + appFrameScript.toString() + ')();', false);
|
||||
mm.addMessageListener("test:InputMethod:oninput", function(ev) {
|
||||
step4(SpecialPowers.wrap(ev).json.value);
|
||||
});
|
||||
|
||||
req.onerror = function() {
|
||||
ok(false, 'setInputMethodActive failed: ' + this.error.name);
|
||||
inputmethod_cleanup();
|
||||
};
|
||||
step2();
|
||||
});
|
||||
}
|
||||
|
||||
// Loads the input method app to the browser frame after a delay.
|
||||
SpecialPowers.DOMWindowUtils.focus(app);
|
||||
setTimeout(function() {
|
||||
keyboard.src = imeUrl;
|
||||
timeoutId = setTimeout(function() {
|
||||
inputmethod_cleanup();
|
||||
ok(false, 'Failed to generate keyboard input.');
|
||||
}, 20000);
|
||||
}, 100);
|
||||
});
|
||||
function step2() {
|
||||
// STEP 2a: Create a browser frame to load the input method app.
|
||||
keyboard = document.createElement('iframe');
|
||||
keyboard.setAttribute('mozbrowser', true);
|
||||
document.body.appendChild(keyboard);
|
||||
|
||||
// STEP 2b: Grant input privileges to the keyboard iframe
|
||||
let imeUrl = basePath + '/file_inputmethod.html#data';
|
||||
|
||||
SpecialPowers.pushPermissions([{
|
||||
type: 'input',
|
||||
allow: true,
|
||||
context: imeUrl
|
||||
}], function() {
|
||||
// STEP 2c: Tell Gecko to use this iframe as its keyboard app
|
||||
let req = keyboard.setInputMethodActive(true);
|
||||
|
||||
req.onsuccess = function() {
|
||||
ok(true, 'setInputMethodActive succeeded.');
|
||||
};
|
||||
|
||||
req.onerror = function() {
|
||||
ok(false, 'setInputMethodActive failed: ' + this.error.name);
|
||||
inputmethod_cleanup();
|
||||
};
|
||||
|
||||
// STEP 3: Loads the input method app to the browser frame after a delay.
|
||||
setTimeout(function() {
|
||||
keyboard.src = imeUrl;
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
|
||||
function step4(val) {
|
||||
ok(true, 'Keyboard input was received.');
|
||||
is(val, '#dataYuan', 'Input value');
|
||||
inputmethod_cleanup();
|
||||
}
|
||||
|
||||
step1();
|
||||
}
|
||||
|
||||
</script>
|
||||
|
52
dom/inputmethod/mochitest/test_bug953044.html
Normal file
@ -0,0 +1,52 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=953044
|
||||
-->
|
||||
<head>
|
||||
<title>Basic test for InputMethod API.</title>
|
||||
<script type="application/javascript;version=1.7" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript;version=1.7" src="inputmethod_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=953044">Mozilla Bug 953044</a>
|
||||
<p id="display"></p>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="application/javascript;version=1.7">
|
||||
|
||||
inputmethod_setup(function() {
|
||||
runTest();
|
||||
});
|
||||
|
||||
function runTest() {
|
||||
// Create an app frame to recieve keyboard inputs.
|
||||
let app = document.createElement('iframe');
|
||||
app.src = 'file_test_app.html';
|
||||
app.setAttribute('mozbrowser', true);
|
||||
document.body.appendChild(app);
|
||||
|
||||
// Create a browser frame to load the input method app.
|
||||
let keyboard = document.createElement('iframe');
|
||||
keyboard.setAttribute('mozbrowser', true);
|
||||
document.body.appendChild(keyboard);
|
||||
|
||||
// Bug 953044 setInputMethodActive(false) before input method app loads should
|
||||
// always succeed.
|
||||
let req = keyboard.setInputMethodActive(false);
|
||||
req.onsuccess = function() {
|
||||
ok(true, 'setInputMethodActive before loading succeeded.');
|
||||
inputmethod_cleanup();
|
||||
};
|
||||
|
||||
req.onerror = function() {
|
||||
ok(false, 'setInputMethodActive before loading failed: ' + this.error.name);
|
||||
inputmethod_cleanup();
|
||||
};
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -47,8 +47,9 @@ interface nsIDOMEventTarget;
|
||||
interface nsIRunnable;
|
||||
interface nsICompositionStringSynthesizer;
|
||||
interface nsITranslationNodeList;
|
||||
interface nsIContentPermissionRequest;
|
||||
|
||||
[scriptable, uuid(46e3f206-9a8f-4d66-8248-7ec6a37de45a)]
|
||||
[scriptable, uuid(ca202fa7-7b8f-4814-acc3-a8545f67320b)]
|
||||
interface nsIDOMWindowUtils : nsISupports {
|
||||
|
||||
/**
|
||||
@ -1663,6 +1664,12 @@ interface nsIDOMWindowUtils : nsISupports {
|
||||
* purpose of the test for bug 503926.
|
||||
*/
|
||||
void xpconnectArgument(in nsIDOMWindowUtils aThis);
|
||||
|
||||
/**
|
||||
* Helper for JS components that need to send permission requests with
|
||||
* e10s support properly.
|
||||
*/
|
||||
void askPermission(in nsIContentPermissionRequest aRequest);
|
||||
};
|
||||
|
||||
[scriptable, uuid(c694e359-7227-4392-a138-33c0cc1f15a6)]
|
||||
|
@ -1904,11 +1904,11 @@ ContentParent::InitInternal(ProcessPriority aInitialPriority,
|
||||
MOZ_ASSERT(opened);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
DebugOnly<bool> opened = PSharedBufferManager::Open(this);
|
||||
MOZ_ASSERT(opened);
|
||||
DebugOnly<bool> opened = PSharedBufferManager::Open(this);
|
||||
MOZ_ASSERT(opened);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (aSendRegisteredChrome) {
|
||||
nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService();
|
||||
|
@ -10,6 +10,7 @@
|
||||
// XXXbz Doing this in a header is a gigantic footgun. See
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=932421#c3 for why.
|
||||
#undef CreateEvent
|
||||
#undef LoadImage
|
||||
|
||||
/*
|
||||
PContentPermissionRequestChild implementations also are
|
||||
|
@ -35,38 +35,6 @@ namespace mozilla {
|
||||
|
||||
static MediaPermissionManager *gMediaPermMgr = nullptr;
|
||||
|
||||
static uint32_t
|
||||
ConvertArrayToPermissionRequest(nsIArray* aSrcArray,
|
||||
nsTArray<PermissionRequest>& aDesArray)
|
||||
{
|
||||
uint32_t len = 0;
|
||||
aSrcArray->GetLength(&len);
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
nsCOMPtr<nsIContentPermissionType> cpt = do_QueryElementAt(aSrcArray, i);
|
||||
nsAutoCString type;
|
||||
nsAutoCString access;
|
||||
cpt->GetType(type);
|
||||
cpt->GetAccess(access);
|
||||
|
||||
nsCOMPtr<nsIArray> optionArray;
|
||||
cpt->GetOptions(getter_AddRefs(optionArray));
|
||||
uint32_t optionsLength = 0;
|
||||
optionArray->GetLength(&optionsLength);
|
||||
nsTArray<nsString> options;
|
||||
for (uint32_t j = 0; j < optionsLength; ++j) {
|
||||
nsCOMPtr<nsISupportsString> isupportsString = do_QueryElementAt(optionArray, j);
|
||||
if (isupportsString) {
|
||||
nsString option;
|
||||
isupportsString->GetData(option);
|
||||
options.AppendElement(option);
|
||||
}
|
||||
}
|
||||
|
||||
aDesArray.AppendElement(PermissionRequest(type, access, options));
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static void
|
||||
CreateDeviceNameList(nsTArray<nsCOMPtr<nsIMediaDevice> > &aDevices,
|
||||
nsTArray<nsString> &aDeviceNameList)
|
||||
@ -449,7 +417,7 @@ MediaDeviceSuccessCallback::DoPrompt(nsRefPtr<MediaPermissionRequest> &req)
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsTArray<PermissionRequest> permArray;
|
||||
ConvertArrayToPermissionRequest(typeArray, permArray);
|
||||
RemotePermissionRequest::ConvertArrayToPermissionRequest(typeArray, permArray);
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
rv = req->GetPrincipal(getter_AddRefs(principal));
|
||||
|
@ -5,3 +5,5 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
PARALLEL_DIRS += ['interfaces', 'src']
|
||||
|
||||
TEST_DIRS += ['test']
|
||||
|
5
dom/mobileid/test/mochitest.ini
Normal file
@ -0,0 +1,5 @@
|
||||
[DEFAULT]
|
||||
skip-if = (buildapp != 'b2g')
|
||||
|
||||
[test_mobileid_basics.html]
|
||||
[test_mobileid_no_permission.html]
|
7
dom/mobileid/test/moz.build
Normal file
@ -0,0 +1,7 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
MOCHITEST_MANIFESTS += ['mochitest.ini']
|
78
dom/mobileid/test/test_mobileid_basics.html
Normal file
@ -0,0 +1,78 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for navigator.getMobileIdAssertion</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
const MOCK_CID = SpecialPowers.wrap(SpecialPowers.Components)
|
||||
.ID("{4cb9b8b3-bc8c-46c0-a2b6-2eb0b1ffce94}");
|
||||
const MOBILE_ID_SERVICE_CONTRACT_ID = "@mozilla.org/mobileidentity-service;1";
|
||||
|
||||
function finish() {
|
||||
SpecialPowers.wrap(SpecialPowers.Components).manager
|
||||
.QueryInterface(SpecialPowers.Ci.nsIComponentRegistrar)
|
||||
.unregisterFactory(MOCK_CID, mockMobileIdService);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var mockMobileIdService = {
|
||||
QueryInterface: function(aIID) {
|
||||
if (SpecialPowers.wrap(aIID).equals(SpecialPowers.Ci.nsISupports) ||
|
||||
SpecialPowers.wrap(aIID).equals(SpecialPowers.Ci.nsIMobileIdentityService)) {
|
||||
return this;
|
||||
}
|
||||
throw SpecialPowers.Components.results.NS_ERROR_NO_INTERFACE;
|
||||
},
|
||||
|
||||
createInstance: function(aOuter, aIID) {
|
||||
if (aOuter != null) {
|
||||
throw SpecialPowers.Components.results.NS_ERROR_NO_AGGREGATION;
|
||||
}
|
||||
return this.QueryInterface(aIID);
|
||||
},
|
||||
|
||||
getMobileIdAssertion: function(aWindow, aOptions) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
resolve(aOptions);
|
||||
});
|
||||
}
|
||||
};
|
||||
mockMobileIdService = SpecialPowers.wrapCallbackObject(mockMobileIdService);
|
||||
|
||||
SpecialPowers.wrap(SpecialPowers.Components).manager
|
||||
.QueryInterface(SpecialPowers.Ci.nsIComponentRegistrar)
|
||||
.registerFactory(MOCK_CID, "mobileid service",
|
||||
MOBILE_ID_SERVICE_CONTRACT_ID,
|
||||
mockMobileIdService);
|
||||
|
||||
// Tests
|
||||
|
||||
SpecialPowers.pushPermissions([{"type": "mobileid",
|
||||
"allow": 1,
|
||||
"context": document}], function() {
|
||||
ok("getMobileIdAssertion" in navigator,
|
||||
"navigator.getMobileIdAssertion should exist");
|
||||
|
||||
var options = { forceSelection: true };
|
||||
var promise = navigator.getMobileIdAssertion(options)
|
||||
.then(function(result) {
|
||||
ok(promise instanceof Promise, "Should return a Promise");
|
||||
is(result.forceSelection, options.forceSelection,
|
||||
"MobileIdentityService should receive correct options");
|
||||
finish();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
24
dom/mobileid/test/test_mobileid_no_permission.html
Normal file
@ -0,0 +1,24 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for navigator.getMobileIdAssertion - No permission</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
ok(!("getMobileIdAssertion" in navigator),
|
||||
"navigator.getMobileIdAssertion should NOT exist");
|
||||
|
||||
SimpleTest.finish();
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -82,6 +82,16 @@ let emulator = (function() {
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
function clearTagData(re) {
|
||||
let deferred = Promise.defer();
|
||||
let cmd = "nfc tag clear " + re;
|
||||
|
||||
this.run(cmd, function(result) {
|
||||
is(result.pop(), "OK", "clear tag" + re);
|
||||
deferred.resolve();
|
||||
});
|
||||
}
|
||||
|
||||
function snepPutNdef(dsap, ssap, flags, tnf, type, payload, id) {
|
||||
let deferred = Promise.defer();
|
||||
let cmd = "nfc snep put " + dsap + " " + ssap + " [" + flags + "," +
|
||||
@ -103,6 +113,7 @@ let emulator = (function() {
|
||||
deactivate: deactivate,
|
||||
notifyDiscoverRE: notifyDiscoverRE,
|
||||
setTagData: setTagData,
|
||||
clearTagData: clearTagData,
|
||||
snepPutNdef: snepPutNdef
|
||||
};
|
||||
}());
|
||||
|
@ -10,6 +10,6 @@ qemu=true
|
||||
[test_nfc_manager_tech_lost.js]
|
||||
[test_nfc_peer.js]
|
||||
[test_nfc_peer_sendndef.js]
|
||||
[test_nfc_tag.js]
|
||||
[test_nfc_read_tag.js]
|
||||
[test_nfc_checkP2PRegistration.js]
|
||||
[test_nfc_error_messages.js]
|
||||
|
@ -6,11 +6,10 @@ MARIONETTE_HEAD_JS = "head.js";
|
||||
|
||||
let url = "http://www.mozilla.org";
|
||||
|
||||
// TODO : Get this from emulator console command.
|
||||
const T1T_RE_INDEX = 2;
|
||||
const T2T_RE_INDEX = 3;
|
||||
const T3T_RE_INDEX = 4;
|
||||
const T4T_RE_INDEX = 5;
|
||||
const T1T_RE_INDEX = 2;
|
||||
const T2T_RE_INDEX = 3;
|
||||
const T3T_RE_INDEX = 4;
|
||||
const T4T_RE_INDEX = 5;
|
||||
|
||||
function testUrlTagDiscover(re) {
|
||||
log("Running \'testUrlTagDiscover\'");
|
||||
@ -41,6 +40,26 @@ function testUrlTagDiscover(re) {
|
||||
.then(() => emulator.activateRE(re));
|
||||
}
|
||||
|
||||
function testEmptyTagDiscover(re) {
|
||||
log("Running \'testEmptyTagDiscover\'");
|
||||
|
||||
window.navigator.mozSetMessageHandler("nfc-manager-tech-discovered", function(msg) {
|
||||
log("Received \'nfc-manager-tech-ndiscovered\'");
|
||||
is(msg.type, "techDiscovered", "check for correct message type");
|
||||
let index = msg.techList.indexOf("NDEF");
|
||||
isnot(index, -1, "check for \'NDEF\' in tech list");
|
||||
|
||||
let records = msg.records;
|
||||
ok(records == null);
|
||||
|
||||
toggleNFC(false).then(runNextTest);
|
||||
});
|
||||
|
||||
toggleNFC(true)
|
||||
.then(() => emulator.clearTagData(re))
|
||||
.then(() => emulator.activateRE(re));
|
||||
}
|
||||
|
||||
function testUrlT1TDiscover() {
|
||||
testUrlTagDiscover(T1T_RE_INDEX);
|
||||
}
|
||||
@ -57,11 +76,31 @@ function testUrlT4TDiscover() {
|
||||
testUrlTagDiscover(T4T_RE_INDEX);
|
||||
}
|
||||
|
||||
function testEmptyT1TDiscover() {
|
||||
testEmptyTagDiscover(T1T_RE_INDEX);
|
||||
}
|
||||
|
||||
function testEmptyT2TDiscover() {
|
||||
testEmptyTagDiscover(T2T_RE_INDEX);
|
||||
}
|
||||
|
||||
function testEmptyT3TDiscover() {
|
||||
testEmptyTagDiscover(T3T_RE_INDEX);
|
||||
}
|
||||
|
||||
function testEmptyT4TDiscover() {
|
||||
testEmptyTagDiscover(T4T_RE_INDEX);
|
||||
}
|
||||
|
||||
let tests = [
|
||||
testUrlT1TDiscover,
|
||||
testUrlT2TDiscover,
|
||||
testUrlT3TDiscover,
|
||||
testUrlT4TDiscover
|
||||
testUrlT1TDiscover,
|
||||
testUrlT2TDiscover,
|
||||
testUrlT3TDiscover,
|
||||
testUrlT4TDiscover,
|
||||
testEmptyT1TDiscover,
|
||||
testEmptyT2TDiscover,
|
||||
testEmptyT3TDiscover,
|
||||
testEmptyT4TDiscover
|
||||
];
|
||||
|
||||
SpecialPowers.pushPermissions(
|
@ -1,146 +0,0 @@
|
||||
/* 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/. */
|
||||
|
||||
/* PermissionPromptHelper checks the permissionDB for a given permission
|
||||
* name and performs prompting if needed.
|
||||
* Usage: send PermissionPromptHelper:AskPermission via the FrameMessageManager with:
|
||||
* |origin|, |appID|, |browserFlag| -> used for getting the principal and
|
||||
* |type| and |access| to call testExactPermissionFromPrincipal.
|
||||
* Note that |access| isn't currently used.
|
||||
* Other arugments are:
|
||||
* requestID: ID that gets returned with the result message.
|
||||
*
|
||||
* Once the permission is checked, it returns with the message
|
||||
* "PermissionPromptHelper:AskPermission:OK"
|
||||
* The result contains the |result| e.g.Ci.nsIPermissionManager.ALLOW_ACTION
|
||||
* and a requestID that
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
let DEBUG = 0;
|
||||
let debug;
|
||||
if (DEBUG)
|
||||
debug = function (s) { dump("-*- Permission Prompt Helper component: " + s + "\n"); }
|
||||
else
|
||||
debug = function (s) {}
|
||||
|
||||
const Cu = Components.utils;
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["PermissionPromptHelper"];
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
|
||||
"@mozilla.org/parentprocessmessagemanager;1",
|
||||
"nsIMessageListenerManager");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "permissionPromptService",
|
||||
"@mozilla.org/permission-prompt-service;1",
|
||||
"nsIPermissionPromptService");
|
||||
|
||||
let appsService = Cc["@mozilla.org/AppsService;1"].getService(Ci.nsIAppsService);
|
||||
|
||||
this.PermissionPromptHelper = {
|
||||
init: function init() {
|
||||
debug("Init");
|
||||
ppmm.addMessageListener("PermissionPromptHelper:AskPermission", this);
|
||||
Services.obs.addObserver(this, "profile-before-change", false);
|
||||
},
|
||||
|
||||
askPermission: function askPermission(aMessage, aCallbacks) {
|
||||
let msg = aMessage.json;
|
||||
|
||||
let access = msg.type;
|
||||
if (msg.access) {
|
||||
access = access + "-" + msg.access;
|
||||
}
|
||||
|
||||
let uri = Services.io.newURI(msg.origin, null, null);
|
||||
let principal =
|
||||
Services.scriptSecurityManager.getAppCodebasePrincipal(uri, msg.appID, msg.browserFlag);
|
||||
|
||||
let permValue =
|
||||
Services.perms.testExactPermissionFromPrincipal(principal, access);
|
||||
|
||||
if (permValue == Ci.nsIPermissionManager.DENY_ACTION ||
|
||||
permValue == Ci.nsIPermissionManager.UNKNOWN_ACTION) {
|
||||
aCallbacks.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (permValue == Ci.nsIPermissionManager.PROMPT_ACTION) {
|
||||
|
||||
// create the options from permission request.
|
||||
let options = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
|
||||
if (msg.options) {
|
||||
for (let option of options) {
|
||||
options.appendElement(option);
|
||||
}
|
||||
}
|
||||
|
||||
// create an array with a nsIContentPermissionType element
|
||||
let type = {
|
||||
type: msg.type,
|
||||
access: msg.access ? msg.access : "unused",
|
||||
options: options,
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionType])
|
||||
};
|
||||
let typeArray = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
|
||||
typeArray.appendElement(type, false);
|
||||
|
||||
// create a nsIContentPermissionRequest
|
||||
let request = {
|
||||
types: typeArray,
|
||||
principal: principal,
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionRequest]),
|
||||
allow: aCallbacks.allow,
|
||||
cancel: aCallbacks.cancel,
|
||||
window: Services.wm.getOuterWindowWithId(msg.windowID)
|
||||
};
|
||||
|
||||
permissionPromptService.getPermission(request);
|
||||
return;
|
||||
}
|
||||
|
||||
if (permValue == Ci.nsIPermissionManager.ALLOW_ACTION) {
|
||||
aCallbacks.allow();
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
observe: function observe(aSubject, aTopic, aData) {
|
||||
ppmm.removeMessageListener("PermissionPromptHelper:AskPermission", this);
|
||||
Services.obs.removeObserver(this, "profile-before-change");
|
||||
ppmm = null;
|
||||
},
|
||||
|
||||
receiveMessage: function receiveMessage(aMessage) {
|
||||
debug("PermissionPromptHelper::receiveMessage " + aMessage.name);
|
||||
let mm = aMessage.target;
|
||||
let msg = aMessage.data;
|
||||
|
||||
let result;
|
||||
if (aMessage.name == "PermissionPromptHelper:AskPermission") {
|
||||
this.askPermission(aMessage, {
|
||||
cancel: function() {
|
||||
mm.sendAsyncMessage("PermissionPromptHelper:AskPermission:OK",
|
||||
{ result: Ci.nsIPermissionManager.DENY_ACTION,
|
||||
requestID: msg.requestID });
|
||||
},
|
||||
allow: function(aChoice) {
|
||||
mm.sendAsyncMessage("PermissionPromptHelper:AskPermission:OK",
|
||||
{ result: Ci.nsIPermissionManager.ALLOW_ACTION,
|
||||
choice: aChoice,
|
||||
requestID: msg.requestID });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PermissionPromptHelper.init();
|
@ -14,6 +14,5 @@ EXTRA_COMPONENTS += [
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'PermissionPromptHelper.jsm',
|
||||
'PermissionSettings.jsm',
|
||||
]
|
||||
|
@ -17,7 +17,6 @@
|
||||
#include "nsRect.h" // for nsIntRect
|
||||
#include "nsRegion.h" // for nsIntRegion
|
||||
#include "nsTArray.h" // for nsTArray
|
||||
#include "prlog.h" // for PR_LOG
|
||||
|
||||
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
|
||||
#include <ui/Fence.h>
|
||||
@ -26,24 +25,23 @@
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
// To get this logging, you need PR logging enabled (either by
|
||||
// doing a debug build, or #define'ing FORCE_PR_LOG at the top
|
||||
// of a .cpp file), and then run with NSPR_LOG_MODULES=tiling:5
|
||||
// in your environment at runtime.
|
||||
#ifdef PR_LOGGING
|
||||
# define TILING_PRLOG(_args) PR_LOG(gTilingLog, PR_LOG_DEBUG, _args)
|
||||
# define TILING_PRLOG_OBJ(_args, obj) \
|
||||
// You can enable all the TILING_LOG print statements by
|
||||
// changing the 0 to a 1 in the following #define.
|
||||
#define ENABLE_TILING_LOG 0
|
||||
|
||||
#if ENABLE_TILING_LOG
|
||||
# define TILING_LOG(_args) printf_stderr _args ;
|
||||
# define TILING_LOG_OBJ(_args, obj) \
|
||||
{ \
|
||||
std::stringstream ss; \
|
||||
AppendToString(ss, obj); \
|
||||
nsAutoCString tmpstr; \
|
||||
tmpstr = ss.str().c_str(); \
|
||||
PR_LOG(gTilingLog, PR_LOG_DEBUG, _args); \
|
||||
printf_stderr _args ; \
|
||||
}
|
||||
extern PRLogModuleInfo* gTilingLog;
|
||||
#else
|
||||
# define TILING_PRLOG(_args)
|
||||
# define TILING_PRLOG_OBJ(_args, obj)
|
||||
# define TILING_LOG(_args)
|
||||
# define TILING_LOG_OBJ(_args, obj)
|
||||
#endif
|
||||
|
||||
// An abstract implementation of a tile buffer. This code covers the logic of
|
||||
|
@ -28,8 +28,6 @@
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
PRLogModuleInfo* gTilingLog;
|
||||
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
void
|
||||
@ -161,9 +159,6 @@ ClientLayerManager::CreateThebesLayerWithHint(ThebesLayerCreationHint aHint)
|
||||
(AsShadowForwarder()->GetCompositorBackendType() == LayersBackend::LAYERS_OPENGL ||
|
||||
AsShadowForwarder()->GetCompositorBackendType() == LayersBackend::LAYERS_D3D9 ||
|
||||
AsShadowForwarder()->GetCompositorBackendType() == LayersBackend::LAYERS_D3D11)) {
|
||||
if (!gTilingLog) {
|
||||
gTilingLog = PR_NewLogModule("tiling");
|
||||
}
|
||||
if (gfxPrefs::LayersUseSimpleTiles()) {
|
||||
nsRefPtr<SimpleClientTiledThebesLayer> layer =
|
||||
new SimpleClientTiledThebesLayer(this, aHint);
|
||||
|
@ -2,11 +2,6 @@
|
||||
* 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/. */
|
||||
|
||||
// Uncomment this to enable the TILING_PRLOG stuff in this file
|
||||
// for release builds. To get the output you need to have
|
||||
// NSPR_LOG_MODULES=tiling:5 in your environment at runtime.
|
||||
// #define FORCE_PR_LOG
|
||||
|
||||
#include "ClientTiledThebesLayer.h"
|
||||
#include "FrameMetrics.h" // for FrameMetrics
|
||||
#include "Units.h" // for ScreenIntRect, CSSPoint, etc
|
||||
@ -142,7 +137,7 @@ ClientTiledThebesLayer::BeginPaint()
|
||||
return;
|
||||
}
|
||||
|
||||
TILING_PRLOG(("TILING %p: Found scrollAncestor %p and displayPortAncestor %p\n", this,
|
||||
TILING_LOG(("TILING %p: Found scrollAncestor %p and displayPortAncestor %p\n", this,
|
||||
scrollAncestor, displayPortAncestor));
|
||||
|
||||
const FrameMetrics& scrollMetrics = scrollAncestor->GetFrameMetrics();
|
||||
@ -166,23 +161,23 @@ ClientTiledThebesLayer::BeginPaint()
|
||||
+ displayportMetrics.mCompositionBounds.TopLeft();
|
||||
mPaintData.mCriticalDisplayPort = RoundedOut(
|
||||
ApplyParentLayerToLayerTransform(transformDisplayPortToLayer, criticalDisplayPort));
|
||||
TILING_PRLOG_OBJ(("TILING %p: Critical displayport %s\n", this, tmpstr.get()), mPaintData.mCriticalDisplayPort);
|
||||
TILING_LOG_OBJ(("TILING %p: Critical displayport %s\n", this, tmpstr.get()), mPaintData.mCriticalDisplayPort);
|
||||
|
||||
// Store the resolution from the displayport ancestor layer. Because this is Gecko-side,
|
||||
// before any async transforms have occurred, we can use the zoom for this.
|
||||
mPaintData.mResolution = displayportMetrics.GetZoomToParent();
|
||||
TILING_PRLOG(("TILING %p: Resolution %f\n", this, mPaintData.mResolution.scale));
|
||||
TILING_LOG(("TILING %p: Resolution %f\n", this, mPaintData.mResolution.scale));
|
||||
|
||||
// Store the applicable composition bounds in this layer's Layer units.
|
||||
mPaintData.mTransformToCompBounds =
|
||||
GetTransformToAncestorsParentLayer(this, scrollAncestor);
|
||||
mPaintData.mCompositionBounds = ApplyParentLayerToLayerTransform(
|
||||
mPaintData.mTransformToCompBounds.Inverse(), scrollMetrics.mCompositionBounds);
|
||||
TILING_PRLOG_OBJ(("TILING %p: Composition bounds %s\n", this, tmpstr.get()), mPaintData.mCompositionBounds);
|
||||
TILING_LOG_OBJ(("TILING %p: Composition bounds %s\n", this, tmpstr.get()), mPaintData.mCompositionBounds);
|
||||
|
||||
// Calculate the scroll offset since the last transaction
|
||||
mPaintData.mScrollOffset = displayportMetrics.GetScrollOffset() * displayportMetrics.GetZoomToParent();
|
||||
TILING_PRLOG_OBJ(("TILING %p: Scroll offset %s\n", this, tmpstr.get()), mPaintData.mScrollOffset);
|
||||
TILING_LOG_OBJ(("TILING %p: Scroll offset %s\n", this, tmpstr.get()), mPaintData.mScrollOffset);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -221,7 +216,7 @@ ClientTiledThebesLayer::RenderHighPrecision(nsIntRegion& aInvalidRegion,
|
||||
oldValidRegion.And(oldValidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort));
|
||||
}
|
||||
|
||||
TILING_PRLOG_OBJ(("TILING %p: Progressive update with old valid region %s\n", this, tmpstr.get()), oldValidRegion);
|
||||
TILING_LOG_OBJ(("TILING %p: Progressive update with old valid region %s\n", this, tmpstr.get()), oldValidRegion);
|
||||
|
||||
return mContentClient->mTiledBuffer.ProgressiveUpdate(mValidRegion, aInvalidRegion,
|
||||
oldValidRegion, &mPaintData, aCallback, aCallbackData);
|
||||
@ -234,8 +229,8 @@ ClientTiledThebesLayer::RenderHighPrecision(nsIntRegion& aInvalidRegion,
|
||||
mValidRegion.And(mValidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort));
|
||||
}
|
||||
|
||||
TILING_PRLOG_OBJ(("TILING %p: Non-progressive paint invalid region %s\n", this, tmpstr.get()), aInvalidRegion);
|
||||
TILING_PRLOG_OBJ(("TILING %p: Non-progressive paint new valid region %s\n", this, tmpstr.get()), mValidRegion);
|
||||
TILING_LOG_OBJ(("TILING %p: Non-progressive paint invalid region %s\n", this, tmpstr.get()), aInvalidRegion);
|
||||
TILING_LOG_OBJ(("TILING %p: Non-progressive paint new valid region %s\n", this, tmpstr.get()), mValidRegion);
|
||||
|
||||
mContentClient->mTiledBuffer.SetFrameResolution(mPaintData.mResolution);
|
||||
mContentClient->mTiledBuffer.PaintThebes(mValidRegion, aInvalidRegion, aCallback, aCallbackData);
|
||||
@ -277,8 +272,8 @@ ClientTiledThebesLayer::RenderLowPrecision(nsIntRegion& aInvalidRegion,
|
||||
// region. We don't want to spend time drawing things twice.
|
||||
aInvalidRegion.Sub(aInvalidRegion, mValidRegion);
|
||||
|
||||
TILING_PRLOG_OBJ(("TILING %p: Progressive paint: low-precision invalid region is %s\n", this, tmpstr.get()), aInvalidRegion);
|
||||
TILING_PRLOG_OBJ(("TILING %p: Progressive paint: low-precision old valid region is %s\n", this, tmpstr.get()), oldValidRegion);
|
||||
TILING_LOG_OBJ(("TILING %p: Progressive paint: low-precision invalid region is %s\n", this, tmpstr.get()), aInvalidRegion);
|
||||
TILING_LOG_OBJ(("TILING %p: Progressive paint: low-precision old valid region is %s\n", this, tmpstr.get()), oldValidRegion);
|
||||
|
||||
if (!aInvalidRegion.IsEmpty()) {
|
||||
updatedBuffer = mContentClient->mLowPrecisionTiledBuffer.ProgressiveUpdate(
|
||||
@ -286,11 +281,11 @@ ClientTiledThebesLayer::RenderLowPrecision(nsIntRegion& aInvalidRegion,
|
||||
&mPaintData, aCallback, aCallbackData);
|
||||
}
|
||||
|
||||
TILING_PRLOG_OBJ(("TILING %p: Progressive paint: low-precision new valid region is %s\n", this, tmpstr.get()), mLowPrecisionValidRegion);
|
||||
TILING_LOG_OBJ(("TILING %p: Progressive paint: low-precision new valid region is %s\n", this, tmpstr.get()), mLowPrecisionValidRegion);
|
||||
return updatedBuffer;
|
||||
}
|
||||
if (!mLowPrecisionValidRegion.IsEmpty()) {
|
||||
TILING_PRLOG(("TILING %p: Clearing low-precision buffer\n", this));
|
||||
TILING_LOG(("TILING %p: Clearing low-precision buffer\n", this));
|
||||
// Clear the low precision tiled buffer.
|
||||
mLowPrecisionValidRegion.SetEmpty();
|
||||
mContentClient->mLowPrecisionTiledBuffer.ResetPaintedAndValidState();
|
||||
@ -308,7 +303,7 @@ ClientTiledThebesLayer::EndPaint()
|
||||
mPaintData.mLastScrollOffset = mPaintData.mScrollOffset;
|
||||
mPaintData.mPaintFinished = true;
|
||||
mPaintData.mFirstPaint = false;
|
||||
TILING_PRLOG(("TILING %p: Paint finished\n", this));
|
||||
TILING_LOG(("TILING %p: Paint finished\n", this));
|
||||
}
|
||||
|
||||
void
|
||||
@ -334,9 +329,9 @@ ClientTiledThebesLayer::RenderLayer()
|
||||
mValidRegion = nsIntRegion();
|
||||
}
|
||||
|
||||
TILING_PRLOG_OBJ(("TILING %p: Initial visible region %s\n", this, tmpstr.get()), mVisibleRegion);
|
||||
TILING_PRLOG_OBJ(("TILING %p: Initial valid region %s\n", this, tmpstr.get()), mValidRegion);
|
||||
TILING_PRLOG_OBJ(("TILING %p: Initial low-precision valid region %s\n", this, tmpstr.get()), mLowPrecisionValidRegion);
|
||||
TILING_LOG_OBJ(("TILING %p: Initial visible region %s\n", this, tmpstr.get()), mVisibleRegion);
|
||||
TILING_LOG_OBJ(("TILING %p: Initial valid region %s\n", this, tmpstr.get()), mValidRegion);
|
||||
TILING_LOG_OBJ(("TILING %p: Initial low-precision valid region %s\n", this, tmpstr.get()), mLowPrecisionValidRegion);
|
||||
|
||||
nsIntRegion invalidRegion;
|
||||
invalidRegion.Sub(mVisibleRegion, mValidRegion);
|
||||
@ -353,7 +348,7 @@ ClientTiledThebesLayer::RenderLayer()
|
||||
|
||||
// In some cases we can take a fast path and just be done with it.
|
||||
if (UseFastPath()) {
|
||||
TILING_PRLOG(("TILING %p: Taking fast-path\n", this));
|
||||
TILING_LOG(("TILING %p: Taking fast-path\n", this));
|
||||
mValidRegion = mVisibleRegion;
|
||||
mContentClient->mTiledBuffer.PaintThebes(mValidRegion, invalidRegion, callback, data);
|
||||
ClientManager()->Hold(this);
|
||||
@ -377,13 +372,13 @@ ClientTiledThebesLayer::RenderLayer()
|
||||
invalidRegion.And(invalidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort));
|
||||
}
|
||||
|
||||
TILING_PRLOG_OBJ(("TILING %p: First-transaction valid region %s\n", this, tmpstr.get()), mValidRegion);
|
||||
TILING_PRLOG_OBJ(("TILING %p: First-transaction invalid region %s\n", this, tmpstr.get()), invalidRegion);
|
||||
TILING_LOG_OBJ(("TILING %p: First-transaction valid region %s\n", this, tmpstr.get()), mValidRegion);
|
||||
TILING_LOG_OBJ(("TILING %p: First-transaction invalid region %s\n", this, tmpstr.get()), invalidRegion);
|
||||
} else {
|
||||
if (!mPaintData.mCriticalDisplayPort.IsEmpty()) {
|
||||
invalidRegion.And(invalidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort));
|
||||
}
|
||||
TILING_PRLOG_OBJ(("TILING %p: Repeat-transaction invalid region %s\n", this, tmpstr.get()), invalidRegion);
|
||||
TILING_LOG_OBJ(("TILING %p: Repeat-transaction invalid region %s\n", this, tmpstr.get()), invalidRegion);
|
||||
}
|
||||
|
||||
nsIntRegion lowPrecisionInvalidRegion;
|
||||
@ -393,7 +388,7 @@ ClientTiledThebesLayer::RenderLayer()
|
||||
lowPrecisionInvalidRegion.Sub(mVisibleRegion, mLowPrecisionValidRegion);
|
||||
lowPrecisionInvalidRegion.Sub(lowPrecisionInvalidRegion, mValidRegion);
|
||||
}
|
||||
TILING_PRLOG_OBJ(("TILING %p: Low-precision invalid region %s\n", this, tmpstr.get()), lowPrecisionInvalidRegion);
|
||||
TILING_LOG_OBJ(("TILING %p: Low-precision invalid region %s\n", this, tmpstr.get()), lowPrecisionInvalidRegion);
|
||||
|
||||
bool updatedHighPrecision = RenderHighPrecision(invalidRegion, callback, data);
|
||||
if (updatedHighPrecision) {
|
||||
@ -419,7 +414,7 @@ ClientTiledThebesLayer::RenderLayer()
|
||||
// updates, then mark the paint as unfinished and request a repeat transaction.
|
||||
// This is so that we don't perform low-precision updates in the same transaction
|
||||
// as high-precision updates.
|
||||
TILING_PRLOG(("TILING %p: Scheduling repeat transaction for low-precision painting\n", this));
|
||||
TILING_LOG(("TILING %p: Scheduling repeat transaction for low-precision painting\n", this));
|
||||
ClientManager()->SetRepeatTransaction();
|
||||
mPaintData.mLowPrecisionPaintCount = 1;
|
||||
mPaintData.mPaintFinished = false;
|
||||
|
@ -3,11 +3,6 @@
|
||||
* 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/. */
|
||||
|
||||
// Uncomment this to enable the TILING_PRLOG stuff in this file
|
||||
// for release builds. To get the output you need to have
|
||||
// NSPR_LOG_MODULES=tiling:5 in your environment at runtime.
|
||||
// #define FORCE_PR_LOG
|
||||
|
||||
#include "mozilla/layers/TiledContentClient.h"
|
||||
#include <math.h> // for ceil, ceilf, floor
|
||||
#include "ClientTiledThebesLayer.h" // for ClientTiledThebesLayer
|
||||
@ -192,7 +187,7 @@ SharedFrameMetricsHelper::UpdateFromCompositorFrameMetrics(
|
||||
if (aLowPrecision && !mLastProgressiveUpdateWasLowPrecision) {
|
||||
// Skip low precision rendering until we're at risk of checkerboarding.
|
||||
if (!mProgressiveUpdateWasInDanger) {
|
||||
TILING_PRLOG(("TILING: Aborting low-precision rendering because not at risk of checkerboarding\n"));
|
||||
TILING_LOG(("TILING: Aborting low-precision rendering because not at risk of checkerboarding\n"));
|
||||
return true;
|
||||
}
|
||||
mProgressiveUpdateWasInDanger = false;
|
||||
@ -202,7 +197,7 @@ SharedFrameMetricsHelper::UpdateFromCompositorFrameMetrics(
|
||||
// Always abort updates if the resolution has changed. There's no use
|
||||
// in drawing at the incorrect resolution.
|
||||
if (!FuzzyEquals(compositorMetrics.GetZoom().scale, contentMetrics.GetZoom().scale)) {
|
||||
TILING_PRLOG(("TILING: Aborting because resolution changed from %f to %f\n",
|
||||
TILING_LOG(("TILING: Aborting because resolution changed from %f to %f\n",
|
||||
contentMetrics.GetZoom().scale, compositorMetrics.GetZoom().scale));
|
||||
return true;
|
||||
}
|
||||
@ -242,7 +237,7 @@ SharedFrameMetricsHelper::UpdateFromCompositorFrameMetrics(
|
||||
// Abort drawing stale low-precision content if there's a more recent
|
||||
// display-port in the pipeline.
|
||||
if (aLowPrecision && !aHasPendingNewThebesContent) {
|
||||
TILING_PRLOG(("TILING: Aborting low-precision because of new pending content\n"));
|
||||
TILING_LOG(("TILING: Aborting low-precision because of new pending content\n"));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -277,10 +272,10 @@ SharedFrameMetricsHelper::AboutToCheckerboard(const FrameMetrics& aContentMetric
|
||||
showing = showing.Intersect(aContentMetrics.mScrollableRect);
|
||||
|
||||
if (!painted.Contains(showing)) {
|
||||
TILING_PRLOG_OBJ(("TILING: About to checkerboard; content %s\n", tmpstr.get()), aContentMetrics);
|
||||
TILING_PRLOG_OBJ(("TILING: About to checkerboard; painted %s\n", tmpstr.get()), painted);
|
||||
TILING_PRLOG_OBJ(("TILING: About to checkerboard; compositor %s\n", tmpstr.get()), aCompositorMetrics);
|
||||
TILING_PRLOG_OBJ(("TILING: About to checkerboard; showing %s\n", tmpstr.get()), showing);
|
||||
TILING_LOG_OBJ(("TILING: About to checkerboard; content %s\n", tmpstr.get()), aContentMetrics);
|
||||
TILING_LOG_OBJ(("TILING: About to checkerboard; painted %s\n", tmpstr.get()), painted);
|
||||
TILING_LOG_OBJ(("TILING: About to checkerboard; compositor %s\n", tmpstr.get()), aCompositorMetrics);
|
||||
TILING_LOG_OBJ(("TILING: About to checkerboard; showing %s\n", tmpstr.get()), showing);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -716,8 +711,8 @@ ClientTiledLayerBuffer::PaintThebes(const nsIntRegion& aNewValidRegion,
|
||||
LayerManager::DrawThebesLayerCallback aCallback,
|
||||
void* aCallbackData)
|
||||
{
|
||||
TILING_PRLOG_OBJ(("TILING %p: PaintThebes painting region %s\n", mThebesLayer, tmpstr.get()), aPaintRegion);
|
||||
TILING_PRLOG_OBJ(("TILING %p: PaintThebes new valid region %s\n", mThebesLayer, tmpstr.get()), aNewValidRegion);
|
||||
TILING_LOG_OBJ(("TILING %p: PaintThebes painting region %s\n", mThebesLayer, tmpstr.get()), aPaintRegion);
|
||||
TILING_LOG_OBJ(("TILING %p: PaintThebes new valid region %s\n", mThebesLayer, tmpstr.get()), aNewValidRegion);
|
||||
|
||||
mCallback = aCallback;
|
||||
mCallbackData = aCallbackData;
|
||||
@ -1047,7 +1042,7 @@ ClientTiledLayerBuffer::ComputeProgressiveUpdateRegion(const nsIntRegion& aInval
|
||||
nsIntRegion staleRegion;
|
||||
staleRegion.And(aInvalidRegion, aOldValidRegion);
|
||||
|
||||
TILING_PRLOG_OBJ(("TILING %p: Progressive update stale region %s\n", mThebesLayer, tmpstr.get()), staleRegion);
|
||||
TILING_LOG_OBJ(("TILING %p: Progressive update stale region %s\n", mThebesLayer, tmpstr.get()), staleRegion);
|
||||
|
||||
ContainerLayer* scrollAncestor = nullptr;
|
||||
mThebesLayer->GetAncestorLayers(&scrollAncestor, nullptr);
|
||||
@ -1079,7 +1074,7 @@ ClientTiledLayerBuffer::ComputeProgressiveUpdateRegion(const nsIntRegion& aInval
|
||||
viewTransform);
|
||||
#endif
|
||||
|
||||
TILING_PRLOG(("TILING %p: Progressive update view transform %f %f zoom %f abort %d\n", mThebesLayer, viewTransform.mTranslation.x, viewTransform.mTranslation.y, viewTransform.mScale.scale, abortPaint));
|
||||
TILING_LOG(("TILING %p: Progressive update view transform %f %f zoom %f abort %d\n", mThebesLayer, viewTransform.mTranslation.x, viewTransform.mTranslation.y, viewTransform.mScale.scale, abortPaint));
|
||||
|
||||
if (abortPaint) {
|
||||
// We ignore if front-end wants to abort if this is the first,
|
||||
@ -1099,7 +1094,7 @@ ClientTiledLayerBuffer::ComputeProgressiveUpdateRegion(const nsIntRegion& aInval
|
||||
aPaintData->mTransformToCompBounds,
|
||||
viewTransform);
|
||||
|
||||
TILING_PRLOG_OBJ(("TILING %p: Progressive update transformed compositor bounds %s\n", mThebesLayer, tmpstr.get()), transformedCompositionBounds);
|
||||
TILING_LOG_OBJ(("TILING %p: Progressive update transformed compositor bounds %s\n", mThebesLayer, tmpstr.get()), transformedCompositionBounds);
|
||||
|
||||
// Compute a "coherent update rect" that we should paint all at once in a
|
||||
// single transaction. This is to avoid rendering glitches on animated
|
||||
@ -1118,7 +1113,7 @@ ClientTiledLayerBuffer::ComputeProgressiveUpdateRegion(const nsIntRegion& aInval
|
||||
#endif
|
||||
)));
|
||||
|
||||
TILING_PRLOG_OBJ(("TILING %p: Progressive update final coherency rect %s\n", mThebesLayer, tmpstr.get()), coherentUpdateRect);
|
||||
TILING_LOG_OBJ(("TILING %p: Progressive update final coherency rect %s\n", mThebesLayer, tmpstr.get()), coherentUpdateRect);
|
||||
|
||||
aRegionToPaint.And(aInvalidRegion, coherentUpdateRect);
|
||||
aRegionToPaint.Or(aRegionToPaint, staleRegion);
|
||||
@ -1134,13 +1129,13 @@ ClientTiledLayerBuffer::ComputeProgressiveUpdateRegion(const nsIntRegion& aInval
|
||||
paintingVisible = true;
|
||||
}
|
||||
|
||||
TILING_PRLOG_OBJ(("TILING %p: Progressive update final paint region %s\n", mThebesLayer, tmpstr.get()), aRegionToPaint);
|
||||
TILING_LOG_OBJ(("TILING %p: Progressive update final paint region %s\n", mThebesLayer, tmpstr.get()), aRegionToPaint);
|
||||
|
||||
// Paint area that's visible and overlaps previously valid content to avoid
|
||||
// visible glitches in animated elements, such as gifs.
|
||||
bool paintInSingleTransaction = paintingVisible && (drawingStale || aPaintData->mFirstPaint);
|
||||
|
||||
TILING_PRLOG(("TILING %p: paintingVisible %d drawingStale %d firstPaint %d singleTransaction %d\n",
|
||||
TILING_LOG(("TILING %p: paintingVisible %d drawingStale %d firstPaint %d singleTransaction %d\n",
|
||||
mThebesLayer, paintingVisible, drawingStale, aPaintData->mFirstPaint, paintInSingleTransaction));
|
||||
|
||||
// The following code decides what order to draw tiles in, based on the
|
||||
@ -1214,9 +1209,9 @@ ClientTiledLayerBuffer::ProgressiveUpdate(nsIntRegion& aValidRegion,
|
||||
LayerManager::DrawThebesLayerCallback aCallback,
|
||||
void* aCallbackData)
|
||||
{
|
||||
TILING_PRLOG_OBJ(("TILING %p: Progressive update valid region %s\n", mThebesLayer, tmpstr.get()), aValidRegion);
|
||||
TILING_PRLOG_OBJ(("TILING %p: Progressive update invalid region %s\n", mThebesLayer, tmpstr.get()), aInvalidRegion);
|
||||
TILING_PRLOG_OBJ(("TILING %p: Progressive update old valid region %s\n", mThebesLayer, tmpstr.get()), aOldValidRegion);
|
||||
TILING_LOG_OBJ(("TILING %p: Progressive update valid region %s\n", mThebesLayer, tmpstr.get()), aValidRegion);
|
||||
TILING_LOG_OBJ(("TILING %p: Progressive update invalid region %s\n", mThebesLayer, tmpstr.get()), aInvalidRegion);
|
||||
TILING_LOG_OBJ(("TILING %p: Progressive update old valid region %s\n", mThebesLayer, tmpstr.get()), aOldValidRegion);
|
||||
|
||||
bool repeat = false;
|
||||
bool isBufferChanged = false;
|
||||
@ -1230,7 +1225,7 @@ ClientTiledLayerBuffer::ProgressiveUpdate(nsIntRegion& aValidRegion,
|
||||
aPaintData,
|
||||
repeat);
|
||||
|
||||
TILING_PRLOG_OBJ(("TILING %p: Progressive update computed paint region %s repeat %d\n", mThebesLayer, tmpstr.get(), repeat), regionToPaint);
|
||||
TILING_LOG_OBJ(("TILING %p: Progressive update computed paint region %s repeat %d\n", mThebesLayer, tmpstr.get(), repeat), regionToPaint);
|
||||
|
||||
// There's no further work to be done.
|
||||
if (regionToPaint.IsEmpty()) {
|
||||
@ -1253,8 +1248,8 @@ ClientTiledLayerBuffer::ProgressiveUpdate(nsIntRegion& aValidRegion,
|
||||
aInvalidRegion.Sub(aInvalidRegion, regionToPaint);
|
||||
} while (repeat);
|
||||
|
||||
TILING_PRLOG_OBJ(("TILING %p: Progressive update final valid region %s buffer changed %d\n", mThebesLayer, tmpstr.get(), isBufferChanged), aValidRegion);
|
||||
TILING_PRLOG_OBJ(("TILING %p: Progressive update final invalid region %s\n", mThebesLayer, tmpstr.get()), aInvalidRegion);
|
||||
TILING_LOG_OBJ(("TILING %p: Progressive update final valid region %s buffer changed %d\n", mThebesLayer, tmpstr.get(), isBufferChanged), aValidRegion);
|
||||
TILING_LOG_OBJ(("TILING %p: Progressive update final invalid region %s\n", mThebesLayer, tmpstr.get()), aInvalidRegion);
|
||||
|
||||
// Return false if nothing has been drawn, or give what has been drawn
|
||||
// to the shadow layer to upload.
|
||||
|
@ -37,7 +37,6 @@ SharedBufferManagerChild::SharedBufferManagerChild()
|
||||
: mBufferMutex("BufferMonitor")
|
||||
#endif
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static bool
|
||||
@ -154,8 +153,9 @@ bool
|
||||
SharedBufferManagerChild::StartUpOnThread(base::Thread* aThread)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(aThread, "SharedBufferManager needs a thread.");
|
||||
if (sSharedBufferManagerChildSingleton != nullptr)
|
||||
if (sSharedBufferManagerChildSingleton != nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sSharedBufferManagerChildThread = aThread;
|
||||
if (!aThread->IsRunning()) {
|
||||
@ -337,8 +337,10 @@ android::sp<android::GraphicBuffer>
|
||||
SharedBufferManagerChild::GetGraphicBuffer(int64_t key)
|
||||
{
|
||||
MutexAutoLock lock(mBufferMutex);
|
||||
if (mBuffers.count(key) == 0)
|
||||
if (mBuffers.count(key) == 0) {
|
||||
printf_stderr("SharedBufferManagerChild::GetGraphicBuffer -- invalid key");
|
||||
return nullptr;
|
||||
}
|
||||
return mBuffers[key];
|
||||
}
|
||||
#endif
|
||||
|
@ -98,13 +98,19 @@ SharedBufferManagerParent::SharedBufferManagerParent(Transport* aTransport, base
|
||||
, mBuffersMutex("BuffersMonitor")
|
||||
#endif
|
||||
{
|
||||
if (!sManagerMonitor)
|
||||
if (!sManagerMonitor) {
|
||||
sManagerMonitor = new Monitor("Manager Monitor");
|
||||
}
|
||||
|
||||
MonitorAutoLock lock(*sManagerMonitor.get());
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread");
|
||||
if (!aThread->IsRunning())
|
||||
if (!aThread->IsRunning()) {
|
||||
aThread->Start();
|
||||
}
|
||||
|
||||
if (sManagers.count(aOwner) != 0) {
|
||||
printf_stderr("SharedBufferManagerParent already exists.");
|
||||
}
|
||||
mOwner = aOwner;
|
||||
sManagers[aOwner] = this;
|
||||
}
|
||||
@ -124,6 +130,7 @@ void
|
||||
SharedBufferManagerParent::ActorDestroy(ActorDestroyReason aWhy)
|
||||
{
|
||||
#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
|
||||
MutexAutoLock lock(mBuffersMutex);
|
||||
mBuffers.clear();
|
||||
#endif
|
||||
}
|
||||
@ -142,19 +149,15 @@ PSharedBufferManagerParent* SharedBufferManagerParent::Create(Transport* aTransp
|
||||
if (!base::OpenProcessHandle(aOtherProcess, &processHandle)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
base::Thread* thread = nullptr;
|
||||
if (sManagers.count(aOtherProcess) == 1) {
|
||||
thread = sManagers[aOtherProcess]->mThread;
|
||||
}
|
||||
else {
|
||||
char thrname[128];
|
||||
base::snprintf(thrname, 128, "BufMgrParent#%d", aOtherProcess);
|
||||
thread = new base::Thread(thrname);
|
||||
}
|
||||
char thrname[128];
|
||||
base::snprintf(thrname, 128, "BufMgrParent#%d", aOtherProcess);
|
||||
thread = new base::Thread(thrname);
|
||||
|
||||
SharedBufferManagerParent* manager = new SharedBufferManagerParent(aTransport, aOtherProcess, thread);
|
||||
if (!thread->IsRunning())
|
||||
if (!thread->IsRunning()) {
|
||||
thread->Start();
|
||||
}
|
||||
thread->message_loop()->PostTask(FROM_HERE,
|
||||
NewRunnableFunction(ConnectSharedBufferManagerInParentProcess,
|
||||
manager, aTransport, processHandle));
|
||||
@ -187,14 +190,19 @@ bool SharedBufferManagerParent::RecvAllocateGrallocBuffer(const IntSize& aSize,
|
||||
return true;
|
||||
}
|
||||
|
||||
int64_t bufferKey;
|
||||
{
|
||||
MonitorAutoLock lock(*sManagerMonitor.get());
|
||||
bufferKey = ++sBufferKey;
|
||||
}
|
||||
GrallocBufferRef ref;
|
||||
ref.mOwner = mOwner;
|
||||
ref.mKey = ++sBufferKey;
|
||||
ref.mKey = bufferKey;
|
||||
*aHandle = MagicGrallocBufferHandle(outgoingBuffer, ref);
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mBuffersMutex);
|
||||
mBuffers[sBufferKey] = outgoingBuffer;
|
||||
mBuffers[bufferKey] = outgoingBuffer;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
@ -227,8 +235,9 @@ void SharedBufferManagerParent::DropGrallocBufferSync(SharedBufferManagerParent*
|
||||
|
||||
void SharedBufferManagerParent::DropGrallocBuffer(mozilla::layers::SurfaceDescriptor aDesc)
|
||||
{
|
||||
if (aDesc.type() != SurfaceDescriptor::TNewSurfaceDescriptorGralloc)
|
||||
if (aDesc.type() != SurfaceDescriptor::TNewSurfaceDescriptorGralloc) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (PlatformThread::CurrentId() == mThread->thread_id()) {
|
||||
DropGrallocBufferImpl(aDesc);
|
||||
@ -245,15 +254,17 @@ void SharedBufferManagerParent::DropGrallocBufferImpl(mozilla::layers::SurfaceDe
|
||||
MutexAutoLock lock(mBuffersMutex);
|
||||
int64_t key = -1;
|
||||
MaybeMagicGrallocBufferHandle handle;
|
||||
if (aDesc.type() == SurfaceDescriptor::TNewSurfaceDescriptorGralloc)
|
||||
if (aDesc.type() == SurfaceDescriptor::TNewSurfaceDescriptorGralloc) {
|
||||
handle = aDesc.get_NewSurfaceDescriptorGralloc().buffer();
|
||||
else
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (handle.type() == MaybeMagicGrallocBufferHandle::TGrallocBufferRef)
|
||||
if (handle.type() == MaybeMagicGrallocBufferHandle::TGrallocBufferRef) {
|
||||
key = handle.get_GrallocBufferRef().mKey;
|
||||
else if (handle.type() == MaybeMagicGrallocBufferHandle::TMagicGrallocBufferHandle)
|
||||
} else if (handle.type() == MaybeMagicGrallocBufferHandle::TMagicGrallocBufferHandle) {
|
||||
key = handle.get_MagicGrallocBufferHandle().mRef.mKey;
|
||||
}
|
||||
|
||||
NS_ASSERTION(key != -1, "Invalid buffer key");
|
||||
NS_ASSERTION(mBuffers.count(key) == 1, "No such buffer");
|
||||
@ -281,9 +292,9 @@ SharedBufferManagerParent::GetGraphicBuffer(int64_t key)
|
||||
MutexAutoLock lock(mBuffersMutex);
|
||||
if (mBuffers.count(key) == 1) {
|
||||
return mBuffers[key];
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// The buffer can be dropped, or invalid
|
||||
printf_stderr("SharedBufferManagerParent::GetGraphicBuffer -- invalid key");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -55,27 +55,27 @@ bool ParseLangSysTable(const ots::OpenTypeFile *file,
|
||||
if (!subtable->ReadU16(&offset_lookup_order) ||
|
||||
!subtable->ReadU16(&req_feature_index) ||
|
||||
!subtable->ReadU16(&feature_count)) {
|
||||
return OTS_FAILURE_MSG("Failed to read langsys header for table %4s", (char *)&tag);
|
||||
return OTS_FAILURE_MSG("Failed to read langsys header for tag %4.4s", (char *)&tag);
|
||||
}
|
||||
// |offset_lookup_order| is reserved and should be NULL.
|
||||
if (offset_lookup_order != 0) {
|
||||
return OTS_FAILURE_MSG("Bad lookup offset order %d in table %4s", offset_lookup_order, (char *)&tag);
|
||||
return OTS_FAILURE_MSG("Bad lookup offset order %d for langsys tag %4.4s", offset_lookup_order, (char *)&tag);
|
||||
}
|
||||
if (req_feature_index != kNoRequiredFeatureIndexDefined &&
|
||||
req_feature_index >= num_features) {
|
||||
return OTS_FAILURE_MSG("Bad required features index %d in table %4s", req_feature_index, (char *)&tag);
|
||||
return OTS_FAILURE_MSG("Bad required features index %d for langsys tag %4.4s", req_feature_index, (char *)&tag);
|
||||
}
|
||||
if (feature_count > num_features) {
|
||||
return OTS_FAILURE_MSG("Bad feature count %d in table %4s", feature_count, (char *)&tag);
|
||||
return OTS_FAILURE_MSG("Bad feature count %d for langsys tag %4.4s", feature_count, (char *)&tag);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < feature_count; ++i) {
|
||||
uint16_t feature_index = 0;
|
||||
if (!subtable->ReadU16(&feature_index)) {
|
||||
return OTS_FAILURE_MSG("Failed to read feature index %d in table %4s", i, (char *)&tag);
|
||||
return OTS_FAILURE_MSG("Failed to read feature index %d for langsys tag %4.4s", i, (char *)&tag);
|
||||
}
|
||||
if (feature_index >= num_features) {
|
||||
return OTS_FAILURE_MSG("Bad feature index %d for feature %d in table %4s", feature_index, i, (char *)&tag);
|
||||
return OTS_FAILURE_MSG("Bad feature index %d for feature %d for langsys tag %4.4s", feature_index, i, (char *)&tag);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@ -90,20 +90,20 @@ bool ParseScriptTable(const ots::OpenTypeFile *file,
|
||||
uint16_t lang_sys_count = 0;
|
||||
if (!subtable.ReadU16(&offset_default_lang_sys) ||
|
||||
!subtable.ReadU16(&lang_sys_count)) {
|
||||
return OTS_FAILURE_MSG("Failed to read script header for table %4s", (char *)&tag);
|
||||
return OTS_FAILURE_MSG("Failed to read script header for script tag %4.4s", (char *)&tag);
|
||||
}
|
||||
|
||||
// The spec requires a script table for 'DFLT' tag must contain non-NULL
|
||||
// |offset_default_lang_sys| and |lang_sys_count| == 0
|
||||
if (tag == kScriptTableTagDflt &&
|
||||
(offset_default_lang_sys == 0 || lang_sys_count != 0)) {
|
||||
return OTS_FAILURE_MSG("DFLT table doesn't satisfy the spec. in table %4s", (char *)&tag);
|
||||
return OTS_FAILURE_MSG("DFLT table doesn't satisfy the spec. for script tag %4.4s", (char *)&tag);
|
||||
}
|
||||
|
||||
const unsigned lang_sys_record_end =
|
||||
6 * static_cast<unsigned>(lang_sys_count) + 4;
|
||||
if (lang_sys_record_end > std::numeric_limits<uint16_t>::max()) {
|
||||
return OTS_FAILURE_MSG("Bad end of langsys record %d in table %4s", lang_sys_record_end, (char *)&tag);
|
||||
return OTS_FAILURE_MSG("Bad end of langsys record %d for script tag %4.4s", lang_sys_record_end, (char *)&tag);
|
||||
}
|
||||
|
||||
std::vector<LangSysRecord> lang_sys_records;
|
||||
@ -112,11 +112,11 @@ bool ParseScriptTable(const ots::OpenTypeFile *file,
|
||||
for (unsigned i = 0; i < lang_sys_count; ++i) {
|
||||
if (!subtable.ReadU32(&lang_sys_records[i].tag) ||
|
||||
!subtable.ReadU16(&lang_sys_records[i].offset)) {
|
||||
return OTS_FAILURE_MSG("Failed to read langsys record header %d for table %4s", i, (char *)&tag);
|
||||
return OTS_FAILURE_MSG("Failed to read langsys record header %d for script tag %4.4s", i, (char *)&tag);
|
||||
}
|
||||
// The record array must store the records alphabetically by tag
|
||||
if (last_tag != 0 && last_tag > lang_sys_records[i].tag) {
|
||||
return OTS_FAILURE_MSG("Bad last tag %d for langsys record %d in table %4s", last_tag, i, (char *)&tag);
|
||||
return OTS_FAILURE_MSG("Bad last tag %d for langsys record %d for script tag %4.4s", last_tag, i, (char *)&tag);
|
||||
}
|
||||
if (lang_sys_records[i].offset < lang_sys_record_end ||
|
||||
lang_sys_records[i].offset >= length) {
|
||||
@ -130,7 +130,7 @@ bool ParseScriptTable(const ots::OpenTypeFile *file,
|
||||
for (unsigned i = 0; i < lang_sys_count; ++i) {
|
||||
subtable.set_offset(lang_sys_records[i].offset);
|
||||
if (!ParseLangSysTable(file, &subtable, lang_sys_records[i].tag, num_features)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse langsys table %d (%4s) in table %4s", i, (char *)&lang_sys_records[i].tag, (char *)&tag);
|
||||
return OTS_FAILURE_MSG("Failed to parse langsys table %d (%4.4s) for script tag %4.4s", i, (char *)&lang_sys_records[i].tag, (char *)&tag);
|
||||
}
|
||||
}
|
||||
|
||||
@ -158,7 +158,7 @@ bool ParseFeatureTable(const ots::OpenTypeFile *file,
|
||||
if (offset_feature_params != 0 &&
|
||||
(offset_feature_params < feature_table_end ||
|
||||
offset_feature_params >= length)) {
|
||||
return OTS_FAILURE_MSG("Badd feature parames offset %d", offset_feature_params);
|
||||
return OTS_FAILURE_MSG("Bad feature params offset %d", offset_feature_params);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < lookup_count; ++i) {
|
||||
@ -1228,7 +1228,7 @@ bool ParseScriptListTable(const ots::OpenTypeFile *file,
|
||||
}
|
||||
last_tag = record.tag;
|
||||
if (record.offset < script_record_end || record.offset >= length) {
|
||||
return OTS_FAILURE_MSG("Bad record offset %d for script %4s entry %d in script list table", record.offset, (char *)&record.tag, i);
|
||||
return OTS_FAILURE_MSG("Bad record offset %d for script %4.4s entry %d in script list table", record.offset, (char *)&record.tag, i);
|
||||
}
|
||||
script_list.push_back(record);
|
||||
}
|
||||
@ -1284,7 +1284,7 @@ bool ParseFeatureListTable(const ots::OpenTypeFile *file,
|
||||
last_tag = feature_records[i].tag;
|
||||
if (feature_records[i].offset < feature_record_end ||
|
||||
feature_records[i].offset >= length) {
|
||||
return OTS_FAILURE_MSG("Bad feature offset %d for feature %d %4s", feature_records[i].offset, i, (char *)&feature_records[i].tag);
|
||||
return OTS_FAILURE_MSG("Bad feature offset %d for feature %d %4.4s", feature_records[i].offset, i, (char *)&feature_records[i].tag);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6493,13 +6493,7 @@ gfxShapedText::AllocateDetailedGlyphs(uint32_t aIndex, uint32_t aCount)
|
||||
mDetailedGlyphs = new DetailedGlyphStore();
|
||||
}
|
||||
|
||||
DetailedGlyph *details = mDetailedGlyphs->Allocate(aIndex, aCount);
|
||||
if (!details) {
|
||||
GetCharacterGlyphs()[aIndex].SetMissing(0);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return details;
|
||||
return mDetailedGlyphs->Allocate(aIndex, aCount);
|
||||
}
|
||||
|
||||
void
|
||||
@ -6513,9 +6507,6 @@ gfxShapedText::SetGlyphs(uint32_t aIndex, CompressedGlyph aGlyph,
|
||||
uint32_t glyphCount = aGlyph.GetGlyphCount();
|
||||
if (glyphCount > 0) {
|
||||
DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, glyphCount);
|
||||
if (!details) {
|
||||
return;
|
||||
}
|
||||
memcpy(details, aGlyphs, sizeof(DetailedGlyph)*glyphCount);
|
||||
}
|
||||
GetCharacterGlyphs()[aIndex] = aGlyph;
|
||||
@ -6544,9 +6535,6 @@ gfxShapedText::SetMissingGlyph(uint32_t aIndex, uint32_t aChar, gfxFont *aFont)
|
||||
}
|
||||
|
||||
DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
|
||||
if (!details) {
|
||||
return;
|
||||
}
|
||||
|
||||
details->mGlyphID = aChar;
|
||||
if (IsDefaultIgnorable(aChar)) {
|
||||
@ -6569,14 +6557,12 @@ gfxShapedText::FilterIfIgnorable(uint32_t aIndex, uint32_t aCh)
|
||||
{
|
||||
if (IsDefaultIgnorable(aCh)) {
|
||||
DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
|
||||
if (details) {
|
||||
details->mGlyphID = aCh;
|
||||
details->mAdvance = 0;
|
||||
details->mXOffset = 0;
|
||||
details->mYOffset = 0;
|
||||
GetCharacterGlyphs()[aIndex].SetMissing(1);
|
||||
return true;
|
||||
}
|
||||
details->mGlyphID = aCh;
|
||||
details->mAdvance = 0;
|
||||
details->mXOffset = 0;
|
||||
details->mYOffset = 0;
|
||||
GetCharacterGlyphs()[aIndex].SetMissing(1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -7656,24 +7642,6 @@ gfxTextRun::CountMissingGlyphs()
|
||||
return count;
|
||||
}
|
||||
|
||||
gfxTextRun::DetailedGlyph *
|
||||
gfxTextRun::AllocateDetailedGlyphs(uint32_t aIndex, uint32_t aCount)
|
||||
{
|
||||
NS_ASSERTION(aIndex < GetLength(), "Index out of range");
|
||||
|
||||
if (!mDetailedGlyphs) {
|
||||
mDetailedGlyphs = new DetailedGlyphStore();
|
||||
}
|
||||
|
||||
DetailedGlyph *details = mDetailedGlyphs->Allocate(aIndex, aCount);
|
||||
if (!details) {
|
||||
mCharacterGlyphs[aIndex].SetMissing(0);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return details;
|
||||
}
|
||||
|
||||
void
|
||||
gfxTextRun::CopyGlyphDataFrom(gfxShapedWord *aShapedWord, uint32_t aOffset)
|
||||
{
|
||||
|
@ -2625,23 +2625,16 @@ protected:
|
||||
DetailedGlyph* Allocate(uint32_t aOffset, uint32_t aCount) {
|
||||
uint32_t detailIndex = mDetails.Length();
|
||||
DetailedGlyph *details = mDetails.AppendElements(aCount);
|
||||
if (!details) {
|
||||
return nullptr;
|
||||
}
|
||||
// We normally set up glyph records sequentially, so the common case
|
||||
// here is to append new records to the mOffsetToIndex array;
|
||||
// test for that before falling back to the InsertElementSorted
|
||||
// method.
|
||||
if (mOffsetToIndex.Length() == 0 ||
|
||||
aOffset > mOffsetToIndex[mOffsetToIndex.Length() - 1].mOffset) {
|
||||
if (!mOffsetToIndex.AppendElement(DGRec(aOffset, detailIndex))) {
|
||||
return nullptr;
|
||||
}
|
||||
mOffsetToIndex.AppendElement(DGRec(aOffset, detailIndex));
|
||||
} else {
|
||||
if (!mOffsetToIndex.InsertElementSorted(DGRec(aOffset, detailIndex),
|
||||
CompareRecordOffsets())) {
|
||||
return nullptr;
|
||||
}
|
||||
mOffsetToIndex.InsertElementSorted(DGRec(aOffset, detailIndex),
|
||||
CompareRecordOffsets());
|
||||
}
|
||||
return details;
|
||||
}
|
||||
@ -3457,9 +3450,6 @@ protected:
|
||||
private:
|
||||
// **** general helpers ****
|
||||
|
||||
// Allocate aCount DetailedGlyphs for the given index
|
||||
DetailedGlyph *AllocateDetailedGlyphs(uint32_t aCharIndex, uint32_t aCount);
|
||||
|
||||
// Get the total advance for a range of glyphs.
|
||||
int32_t GetAdvanceForGlyphs(uint32_t aStart, uint32_t aEnd);
|
||||
|
||||
|
@ -773,10 +773,10 @@ gfxUtils::ClampToScaleFactor(gfxFloat aVal)
|
||||
|
||||
gfxFloat power = log(aVal)/log(kScaleResolution);
|
||||
|
||||
// If power is within 1e-6 of an integer, round to nearest to
|
||||
// If power is within 1e-5 of an integer, round to nearest to
|
||||
// prevent floating point errors, otherwise round up to the
|
||||
// next integer value.
|
||||
if (fabs(power - NS_round(power)) < 1e-6) {
|
||||
if (fabs(power - NS_round(power)) < 1e-5) {
|
||||
power = NS_round(power);
|
||||
} else if (inverse) {
|
||||
power = floor(power);
|
||||
|
22
ipc/chromium/src/third_party/libevent-dont-use-issetugid-on-android.patch
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
diff --git a/ipc/chromium/src/third_party/libevent/android/event2/event-config.h b/ipc/chromium/src/third_party/libevent/android/event2/event-config.h
|
||||
--- a/ipc/chromium/src/third_party/libevent/android/event2/event-config.h
|
||||
+++ b/ipc/chromium/src/third_party/libevent/android/event2/event-config.h
|
||||
@@ -119,17 +119,17 @@
|
||||
|
||||
/* Define to 1 if you have the `inet_pton' function. */
|
||||
#define _EVENT_HAVE_INET_PTON 1
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#define _EVENT_HAVE_INTTYPES_H 1
|
||||
|
||||
/* Define to 1 if you have the `issetugid' function. */
|
||||
-#define _EVENT_HAVE_ISSETUGID 1
|
||||
+/* #undef _EVENT_HAVE_ISSETUGID */
|
||||
|
||||
/* Define to 1 if you have the `kqueue' function. */
|
||||
/* #undef _EVENT_HAVE_KQUEUE */
|
||||
|
||||
/* Define if the system has zlib */
|
||||
#define _EVENT_HAVE_LIBZ 1
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
@ -15,3 +15,5 @@ These files are taken from libevent-2.0.21-stable built on the development envir
|
||||
4. Apply "openbsd-no-arc4random_addrandom.patch", which fixes the build on OpenBSD (which doesnt provide arc4random_addrandom anymore, see #931354)
|
||||
|
||||
5. Apply "libevent-use-non-deprecated-syscalls.patch", which fixes the build on AArch64 architecture (which does not provide deprecated syscalls)
|
||||
|
||||
6. Apply "libevent-dont-use-issetugid-on-android.patch'. which fixes the build on Android L preview
|
||||
|
@ -124,7 +124,7 @@
|
||||
#define _EVENT_HAVE_INTTYPES_H 1
|
||||
|
||||
/* Define to 1 if you have the `issetugid' function. */
|
||||
#define _EVENT_HAVE_ISSETUGID 1
|
||||
/* #undef _EVENT_HAVE_ISSETUGID */
|
||||
|
||||
/* Define to 1 if you have the `kqueue' function. */
|
||||
/* #undef _EVENT_HAVE_KQUEUE */
|
||||
|
@ -247,7 +247,7 @@ GC(JSContext *cx, unsigned argc, jsval *vp)
|
||||
}
|
||||
|
||||
#ifndef JS_MORE_DETERMINISTIC
|
||||
size_t preBytes = cx->runtime()->gc.bytes;
|
||||
size_t preBytes = cx->runtime()->gc.bytesAllocated();
|
||||
#endif
|
||||
|
||||
if (compartment)
|
||||
@ -259,7 +259,7 @@ GC(JSContext *cx, unsigned argc, jsval *vp)
|
||||
char buf[256] = { '\0' };
|
||||
#ifndef JS_MORE_DETERMINISTIC
|
||||
JS_snprintf(buf, sizeof(buf), "before %lu, after %lu\n",
|
||||
(unsigned long)preBytes, (unsigned long)cx->runtime()->gc.bytes);
|
||||
(unsigned long)preBytes, (unsigned long)cx->runtime()->gc.bytesAllocated());
|
||||
#endif
|
||||
JSString *str = JS_NewStringCopyZ(cx, buf);
|
||||
if (!str)
|
||||
|
@ -37,6 +37,14 @@ enum Heap
|
||||
HeapKinds
|
||||
};
|
||||
|
||||
enum FinalizerKind
|
||||
{
|
||||
NoFinalizer,
|
||||
HasFinalizer,
|
||||
|
||||
FinalizerKinds
|
||||
};
|
||||
|
||||
enum State
|
||||
{
|
||||
StateMutator,
|
||||
@ -47,16 +55,17 @@ enum State
|
||||
typedef uint64_t address;
|
||||
typedef uint8_t AllocKind;
|
||||
typedef uint8_t ClassId;
|
||||
typedef uint64_t TypeId;
|
||||
|
||||
struct AllocInfo
|
||||
{
|
||||
const uint64_t serial;
|
||||
const AllocKind kind;
|
||||
const Heap initialHeap;
|
||||
ClassId classId;
|
||||
TypeId typeId;
|
||||
|
||||
AllocInfo(uint64_t allocCount, uint8_t kind, Heap loc)
|
||||
: serial(allocCount), kind(kind), initialHeap(loc), classId(0)
|
||||
: serial(allocCount), kind(kind), initialHeap(loc), typeId(0)
|
||||
{
|
||||
assert(kind < AllocKinds);
|
||||
assert(initialHeap < HeapKinds);
|
||||
@ -68,20 +77,44 @@ struct ClassInfo
|
||||
const ClassId id;
|
||||
const char *name;
|
||||
const uint32_t flags;
|
||||
const FinalizerKind hasFinalizer;
|
||||
|
||||
ClassInfo(ClassId id, const char *name, uint32_t flags)
|
||||
: id(id), name(name), flags(flags) {}
|
||||
ClassInfo(ClassId id, const char *name, uint32_t flags, FinalizerKind hasFinalizer)
|
||||
: id(id), name(name), flags(flags), hasFinalizer(hasFinalizer) {}
|
||||
};
|
||||
|
||||
struct TypeInfo
|
||||
{
|
||||
const TypeId id;
|
||||
const ClassId classId;
|
||||
const uint32_t flags;
|
||||
const char *name;
|
||||
|
||||
TypeInfo(TypeId id, ClassId classId, uint32_t flags)
|
||||
: id(id), classId(classId), flags(flags), name(nullptr) {}
|
||||
|
||||
const char *getName() {
|
||||
if (name)
|
||||
return name;
|
||||
static char buffer[32];
|
||||
sprintf(buffer, "type %ld", id);
|
||||
return buffer;
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::unordered_map<address, AllocInfo> AllocMap;
|
||||
typedef std::unordered_map<address, ClassId> ClassMap;
|
||||
typedef std::vector<ClassInfo> ClassVector;
|
||||
typedef std::unordered_map<address, TypeId> TypeMap;
|
||||
typedef std::vector<TypeInfo> TypeVector;
|
||||
|
||||
uint64_t thingSizes[AllocKinds];
|
||||
AllocMap nurseryThings;
|
||||
AllocMap tenuredThings;
|
||||
ClassMap classMap;
|
||||
ClassVector classes;
|
||||
TypeMap typeMap;
|
||||
TypeVector types;
|
||||
uint64_t allocCount = 0;
|
||||
|
||||
// Collected data
|
||||
@ -99,94 +132,38 @@ const unsigned LifetimeBinTotal = MaxLifetimeBins;
|
||||
const unsigned AugClasses = MaxClasses + 1;
|
||||
const unsigned ClassTotal = MaxClasses;
|
||||
|
||||
struct CountByHeap
|
||||
{
|
||||
CountByHeap() { memset(this, 0, sizeof(*this)); }
|
||||
void inc(Heap heap) {
|
||||
assert(heap < AugHeapKinds);
|
||||
++count[heap];
|
||||
++count[HeapTotal];
|
||||
}
|
||||
uint64_t get(unsigned heap) {
|
||||
assert(heap < AugHeapKinds);
|
||||
return count[heap];
|
||||
}
|
||||
private:
|
||||
uint64_t count[AugHeapKinds];
|
||||
};
|
||||
struct EmptyArrayTag {};
|
||||
|
||||
struct CountByHeapAndKind
|
||||
template <typename T, size_t length>
|
||||
struct Array
|
||||
{
|
||||
CountByHeapAndKind() {}
|
||||
void inc(Heap heap, AllocKind kind) {
|
||||
assert(kind < AugAllocKinds);
|
||||
count[kind].inc(heap);
|
||||
count[AllocKindTotal].inc(heap);
|
||||
}
|
||||
uint64_t get(unsigned heap, unsigned kind) {
|
||||
assert(kind < AugAllocKinds);
|
||||
return count[kind].get(heap);
|
||||
Array() {}
|
||||
Array(const EmptyArrayTag&) { zero(); }
|
||||
void zero() { memset(&elements, 0, sizeof(elements)); }
|
||||
T &operator[](size_t index) {
|
||||
assert(index < length);
|
||||
return elements[index];
|
||||
}
|
||||
private:
|
||||
CountByHeap count[AugAllocKinds];
|
||||
};
|
||||
|
||||
struct CountByHeapKindAndLifetime
|
||||
{
|
||||
CountByHeapKindAndLifetime() {}
|
||||
void inc(Heap heap, AllocKind kind, unsigned lifetimeBin) {
|
||||
assert(lifetimeBin < MaxLifetimeBins);
|
||||
count[lifetimeBin].inc(heap, kind);
|
||||
count[LifetimeBinTotal].inc(heap, kind);
|
||||
}
|
||||
uint64_t get(unsigned heap, unsigned kind, unsigned lifetimeBin) {
|
||||
assert(lifetimeBin < AugLifetimeBins);
|
||||
return count[lifetimeBin].get(heap, kind);
|
||||
}
|
||||
private:
|
||||
CountByHeapAndKind count[AugLifetimeBins];
|
||||
};
|
||||
|
||||
struct CountByHeapAndClass
|
||||
{
|
||||
CountByHeapAndClass() {}
|
||||
void inc(Heap heap, unsigned classId) {
|
||||
assert(classId < MaxClasses);
|
||||
count[classId].inc(heap);
|
||||
count[ClassTotal].inc(heap);
|
||||
}
|
||||
uint64_t get(unsigned heap, unsigned classId) {
|
||||
assert(classId < AugClasses);
|
||||
return count[classId].get(heap);
|
||||
}
|
||||
private:
|
||||
CountByHeap count[AugClasses];
|
||||
};
|
||||
|
||||
struct CountByHeapClassAndLifetime
|
||||
{
|
||||
CountByHeapClassAndLifetime() {}
|
||||
void inc(Heap heap, unsigned classId, unsigned lifetimeBin) {
|
||||
assert(lifetimeBin < MaxLifetimeBins);
|
||||
count[lifetimeBin].inc(heap, classId);
|
||||
count[LifetimeBinTotal].inc(heap, classId);
|
||||
}
|
||||
uint64_t get(unsigned heap, unsigned classId, unsigned lifetimeBin) {
|
||||
assert(lifetimeBin < AugLifetimeBins);
|
||||
return count[lifetimeBin].get(heap, classId);
|
||||
}
|
||||
private:
|
||||
CountByHeapAndClass count[AugLifetimeBins];
|
||||
T elements[length];
|
||||
};
|
||||
|
||||
unsigned timesliceSize;
|
||||
unsigned lifetimeBins;
|
||||
CountByHeapKindAndLifetime allocTotals;
|
||||
std::vector<CountByHeapKindAndLifetime> allocTotalsBySlice;
|
||||
CountByHeapClassAndLifetime objectTotals;
|
||||
std::vector<uint64_t> gcBytesAllocatedInSlice;
|
||||
std::vector<uint64_t> gcBytesFreedInSlice;
|
||||
|
||||
Array<Array<uint64_t, AllocKinds>, HeapKinds> allocCountByHeapAndKind;
|
||||
Array<Array<uint64_t, MaxLifetimeBins>, HeapKinds> allocCountByHeapAndLifetime;
|
||||
Array<Array<Array<uint64_t, MaxLifetimeBins>, AllocKinds>, HeapKinds> allocCountByHeapKindAndLifetime;
|
||||
Array<uint64_t, MaxClasses> objectCountByClass;
|
||||
std::vector<uint64_t> objectCountByType;
|
||||
Array<Array<uint64_t, MaxClasses>, HeapKinds> objectCountByHeapAndClass;
|
||||
Array<Array<Array<uint64_t, MaxLifetimeBins>, MaxClasses>, HeapKinds> objectCountByHeapClassAndLifetime;
|
||||
Array<Array<uint64_t, MaxLifetimeBins>, FinalizerKinds> heapObjectCountByFinalizerAndLifetime;
|
||||
Array<Array<uint64_t, MaxLifetimeBins>, MaxClasses> finalizedHeapObjectCountByClassAndLifetime;
|
||||
std::vector<Array<Array<uint64_t, MaxLifetimeBins>, HeapKinds> > objectCountByTypeHeapAndLifetime;
|
||||
|
||||
static void
|
||||
die(const char *format, ...)
|
||||
{
|
||||
@ -335,8 +312,8 @@ outputThingCounts(FILE *file)
|
||||
fprintf(file, "Kind, Nursery, Tenured heap\n");
|
||||
for (unsigned i = 0; i < AllocKinds; ++i) {
|
||||
fprintf(file, "%15s, %8" PRIu64 ", %8" PRIu64 "\n", allocKindName(i),
|
||||
allocTotals.get(Nursery, i, LifetimeBinTotal),
|
||||
allocTotals.get(TenuredHeap, i, LifetimeBinTotal));
|
||||
allocCountByHeapAndKind[Nursery][i],
|
||||
allocCountByHeapAndKind[TenuredHeap][i]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -350,9 +327,9 @@ outputObjectCounts(FILE *file)
|
||||
for (unsigned i = 0; i < classes.size(); ++i) {
|
||||
fprintf(file, "%30s, %8" PRIu64 ", %8" PRIu64 ", %8" PRIu64 "\n",
|
||||
classes[i].name,
|
||||
objectTotals.get(Nursery, i, LifetimeBinTotal),
|
||||
objectTotals.get(TenuredHeap, i, LifetimeBinTotal),
|
||||
objectTotals.get(HeapTotal, i, LifetimeBinTotal));
|
||||
objectCountByHeapAndClass[Nursery][i],
|
||||
objectCountByHeapAndClass[TenuredHeap][i],
|
||||
objectCountByClass[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -370,7 +347,45 @@ outputLifetimeByHeap(FILE *file)
|
||||
for (unsigned i = 0; i < lifetimeBins; ++i) {
|
||||
fprintf(file, "%8d", binLimit(i));
|
||||
for (unsigned j = 0; j < HeapKinds; ++j)
|
||||
fprintf(file, ", %8" PRIu64, allocTotals.get(j, AllocKindTotal, i));
|
||||
fprintf(file, ", %8" PRIu64, allocCountByHeapAndLifetime[j][i]);
|
||||
fprintf(file, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
outputLifetimeByHasFinalizer(FILE *file)
|
||||
{
|
||||
fprintf(file, "# Lifetime of heap allocated objects by prescence of finalizer\n");
|
||||
fprintf(file, "# NB invalid unless execution was traced with appropriate zeal\n");
|
||||
fprintf(file, "# Total allocations: %" PRIu64 "\n", allocCount);
|
||||
fprintf(file, "Lifetime, NoFinalizer, HasFinalizer\n");
|
||||
|
||||
for (unsigned i = 0; i < lifetimeBins; ++i) {
|
||||
fprintf(file, "%8d", binLimit(i));
|
||||
for (unsigned j = 0; j < FinalizerKinds; ++j)
|
||||
fprintf(file, ", %8" PRIu64,
|
||||
heapObjectCountByFinalizerAndLifetime[j][i]);
|
||||
fprintf(file, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
outputFinalizedHeapObjectLifetimeByClass(FILE *file)
|
||||
{
|
||||
fprintf(file, "# Lifetime of finalized heap objects by class\n");
|
||||
fprintf(file, "# NB invalid unless execution was traced with appropriate zeal\n");
|
||||
fprintf(file, "# Total allocations: %" PRIu64 "\n", allocCount);
|
||||
fprintf(file, "Lifetime");
|
||||
for (unsigned i = 0; i < classes.size(); ++i)
|
||||
fprintf(file, ", %15s", classes[i].name);
|
||||
fprintf(file, "\n");
|
||||
|
||||
for (unsigned i = 0; i < lifetimeBins; ++i) {
|
||||
fprintf(file, "%8d", binLimit(i));
|
||||
for (unsigned j = 0; j < classes.size(); ++j) {
|
||||
fprintf(file, ", %8" PRIu64,
|
||||
finalizedHeapObjectCountByClassAndLifetime[j][i]);
|
||||
}
|
||||
fprintf(file, "\n");
|
||||
}
|
||||
}
|
||||
@ -391,7 +406,8 @@ outputLifetimeByKind(FILE *file, unsigned initialHeap)
|
||||
for (unsigned i = 0; i < lifetimeBins; ++i) {
|
||||
fprintf(file, "%8d", binLimit(i));
|
||||
for (unsigned j = 0; j < AllocKinds; ++j)
|
||||
fprintf(file, ", %8" PRIu64, allocTotals.get(initialHeap, j, i));
|
||||
fprintf(file, ", %8" PRIu64,
|
||||
allocCountByHeapKindAndLifetime[initialHeap][j][i]);
|
||||
fprintf(file, "\n");
|
||||
}
|
||||
}
|
||||
@ -412,27 +428,43 @@ outputLifetimeByClass(FILE *file, unsigned initialHeap)
|
||||
for (unsigned i = 0; i < lifetimeBins; ++i) {
|
||||
fprintf(file, "%8d", binLimit(i));
|
||||
for (unsigned j = 0; j < classes.size(); ++j)
|
||||
fprintf(file, ", %8" PRIu64, objectTotals.get(initialHeap, j, i));
|
||||
fprintf(file, ", %8" PRIu64,
|
||||
objectCountByHeapClassAndLifetime[initialHeap][j][i]);
|
||||
fprintf(file, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
outputLifetimeBySlice(FILE *file)
|
||||
outputLifetimeByType(FILE *file, unsigned initialHeap)
|
||||
{
|
||||
fprintf(file, "# Lifetime (in log2 bins) by allocation timeslice for all things\n");
|
||||
fprintf(file, "Timeslice");
|
||||
for (unsigned i = 0; i < lifetimeBins; ++i)
|
||||
fprintf(file, ", Lifetime<%d", binLimit(i));
|
||||
assert(initialHeap < AugHeapKinds);
|
||||
|
||||
fprintf(file, "# Lifetime of %s things (in log2 bins) by type\n", heapName(initialHeap));
|
||||
fprintf(file, "# NB invalid unless execution was traced with appropriate zeal\n");
|
||||
fprintf(file, "# Total allocations: %" PRIu64 "\n", allocCount);
|
||||
|
||||
// There are many types but few are frequently used.
|
||||
const size_t minObjectCount = 1;
|
||||
const size_t outputEntries = 10;
|
||||
std::vector<TypeId> topTypes;
|
||||
for (size_t i = 0; i < types.size(); ++i) {
|
||||
if (objectCountByType.at(i) > minObjectCount)
|
||||
topTypes.push_back(i);
|
||||
}
|
||||
std::sort(topTypes.begin(), topTypes.end(),
|
||||
[] (TypeId a, TypeId b) { return objectCountByType.at(a) > objectCountByType.at(b); });
|
||||
size_t count = std::min(outputEntries, topTypes.size());
|
||||
|
||||
fprintf(file, "Lifetime");
|
||||
for (unsigned i = 0; i < count; ++i)
|
||||
fprintf(file, ", %15s", types[topTypes[i]].getName());
|
||||
fprintf(file, "\n");
|
||||
|
||||
uint64_t timesliceCount = allocCount / timesliceSize + 1;
|
||||
for (uint64_t i = 0; i < timesliceCount; ++i) {
|
||||
fprintf(file, "%8" PRIu64, i);
|
||||
for (unsigned j = 0; j < lifetimeBins; ++j) {
|
||||
for (unsigned i = 0; i < lifetimeBins; ++i) {
|
||||
fprintf(file, "%8d", binLimit(i));
|
||||
for (unsigned j = 0; j < count; ++j)
|
||||
fprintf(file, ", %8" PRIu64,
|
||||
allocTotalsBySlice[i].get(HeapTotal, AllocKindTotal, j));
|
||||
}
|
||||
objectCountByTypeHeapAndLifetime.at(topTypes[j])[initialHeap][i]);
|
||||
fprintf(file, "\n");
|
||||
}
|
||||
}
|
||||
@ -446,10 +478,25 @@ processAlloc(const AllocInfo &info, uint64_t finalizeTime)
|
||||
unsigned lifetimeBin = getBin(lifetime);
|
||||
assert(lifetimeBin < lifetimeBins);
|
||||
|
||||
allocTotals.inc(info.initialHeap, info.kind, lifetimeBin);
|
||||
allocTotalsBySlice[timeslice].inc(info.initialHeap, info.kind, lifetimeBin);
|
||||
if (info.kind <= LastObjectAllocKind)
|
||||
objectTotals.inc(info.initialHeap, info.classId, lifetimeBin);
|
||||
++allocCountByHeapAndKind[info.initialHeap][info.kind];
|
||||
++allocCountByHeapAndLifetime[info.initialHeap][lifetimeBin];
|
||||
++allocCountByHeapKindAndLifetime[info.initialHeap][info.kind][lifetimeBin];
|
||||
|
||||
if (info.kind <= LastObjectAllocKind) {
|
||||
const TypeInfo &typeInfo = types[info.typeId];
|
||||
const ClassInfo &classInfo = classes[typeInfo.classId];
|
||||
++objectCountByType.at(typeInfo.id);
|
||||
++objectCountByClass[classInfo.id];
|
||||
++objectCountByHeapAndClass[info.initialHeap][classInfo.id];
|
||||
++objectCountByHeapClassAndLifetime[info.initialHeap][classInfo.id][lifetimeBin];
|
||||
++objectCountByTypeHeapAndLifetime.at(typeInfo.id)[info.initialHeap][lifetimeBin];
|
||||
if (info.initialHeap == TenuredHeap) {
|
||||
FinalizerKind f = classes[classInfo.id].hasFinalizer;
|
||||
++heapObjectCountByFinalizerAndLifetime[f][lifetimeBin];
|
||||
if (f == HasFinalizer)
|
||||
++finalizedHeapObjectCountByClassAndLifetime[classInfo.id][lifetimeBin];
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t size = thingSizes[info.kind];
|
||||
gcBytesAllocatedInSlice[timeslice] += size;
|
||||
@ -534,10 +581,11 @@ expectDataString(FILE *file)
|
||||
}
|
||||
|
||||
static void
|
||||
createClassInfo(const char *name, uint32_t flags, address clasp = 0)
|
||||
createClassInfo(const char *name, uint32_t flags, FinalizerKind hasFinalizer,
|
||||
address clasp = 0)
|
||||
{
|
||||
ClassId id = classes.size();
|
||||
classes.push_back(ClassInfo(id, name, flags));
|
||||
classes.push_back(ClassInfo(id, name, flags, hasFinalizer));
|
||||
if (clasp)
|
||||
classMap.emplace(clasp, id);
|
||||
}
|
||||
@ -546,9 +594,57 @@ static void
|
||||
readClassInfo(FILE *file, address clasp)
|
||||
{
|
||||
assert(clasp);
|
||||
uint32_t flags = expectDataInt(file);
|
||||
char *name = expectDataString(file);
|
||||
createClassInfo(name, flags, clasp);
|
||||
uint32_t flags = expectDataInt(file);
|
||||
FinalizerKind hasFinalizer = expectDataInt(file) != 0 ? HasFinalizer : NoFinalizer;
|
||||
createClassInfo(name, flags, hasFinalizer, clasp);
|
||||
}
|
||||
|
||||
static ClassId
|
||||
lookupClassId(address clasp)
|
||||
{
|
||||
auto i = classMap.find(clasp);
|
||||
assert(i != classMap.end());
|
||||
ClassId id = i->second;
|
||||
assert(id < classes.size());
|
||||
return id;
|
||||
}
|
||||
|
||||
static void
|
||||
createTypeInfo(ClassId classId, uint32_t flags, address typeObject = 0)
|
||||
{
|
||||
TypeId id = types.size();
|
||||
types.push_back(TypeInfo(id, classId, flags));
|
||||
if (typeObject)
|
||||
typeMap.emplace(typeObject, id);
|
||||
objectCountByType.push_back(0);
|
||||
objectCountByTypeHeapAndLifetime.push_back(EmptyArrayTag());
|
||||
}
|
||||
|
||||
static void
|
||||
readTypeInfo(FILE *file, address typeObject)
|
||||
{
|
||||
assert(typeObject);
|
||||
address clasp = expectDataAddress(file);
|
||||
uint32_t flags = expectDataInt(file);
|
||||
createTypeInfo(lookupClassId(clasp), flags, typeObject);
|
||||
}
|
||||
|
||||
static TypeId
|
||||
lookupTypeId(address typeObject)
|
||||
{
|
||||
auto i = typeMap.find(typeObject);
|
||||
assert(i != typeMap.end());
|
||||
TypeId id = i->second;
|
||||
assert(id < types.size());
|
||||
return id;
|
||||
}
|
||||
|
||||
static void
|
||||
setTypeName(address typeObject, const char *name)
|
||||
{
|
||||
TypeId id = lookupTypeId(typeObject);
|
||||
types[id].name = name;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -566,20 +662,15 @@ allocNurseryThing(address thing, AllocKind kind)
|
||||
}
|
||||
|
||||
static void
|
||||
setObjectClass(address obj, address clasp)
|
||||
setObjectType(address obj, address typeObject)
|
||||
{
|
||||
auto i = classMap.find(clasp);
|
||||
assert(i != classMap.end());
|
||||
ClassId id = i->second;
|
||||
assert(id < classes.size());
|
||||
|
||||
auto j = nurseryThings.find(obj);
|
||||
if (j == nurseryThings.end()) {
|
||||
j = tenuredThings.find(obj);
|
||||
if (j == tenuredThings.end())
|
||||
die("Can't find allocation for object %p", obj);
|
||||
}
|
||||
j->second.classId = id;
|
||||
j->second.typeId = lookupTypeId(typeObject);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -655,13 +746,14 @@ processTraceFile(const char *filename)
|
||||
timesliceSize *= 2;
|
||||
|
||||
size_t maxTimeslices = maxTraces / timesliceSize;
|
||||
allocTotalsBySlice.reserve(maxTimeslices);
|
||||
gcBytesAllocatedInSlice.reserve(maxTimeslices);
|
||||
gcBytesFreedInSlice.reserve(maxTimeslices);
|
||||
lifetimeBins = getBin(maxTraces) + 1;
|
||||
assert(lifetimeBins <= MaxLifetimeBins);
|
||||
|
||||
createClassInfo("unknown", 0);
|
||||
createClassInfo("unknown", 0, NoFinalizer);
|
||||
createTypeInfo(0, 0);
|
||||
types[0].name = "unknown";
|
||||
|
||||
State state = StateMutator;
|
||||
while (readTrace(file, trace)) {
|
||||
@ -679,9 +771,17 @@ processTraceFile(const char *filename)
|
||||
assert(state == StateMutator);
|
||||
readClassInfo(file, getTracePayload(trace));
|
||||
break;
|
||||
case TraceEventTypeInfo:
|
||||
assert(state == StateMutator);
|
||||
readTypeInfo(file, getTracePayload(trace));
|
||||
break;
|
||||
case TraceEventTypeNewScript:
|
||||
assert(state == StateMutator);
|
||||
setTypeName(getTracePayload(trace), expectDataString(file));
|
||||
break;
|
||||
case TraceEventCreateObject:
|
||||
assert(state == StateMutator);
|
||||
setObjectClass(getTracePayload(trace), expectDataAddress(file));
|
||||
setObjectType(getTracePayload(trace), expectDataAddress(file));
|
||||
break;
|
||||
case TraceEventMinorGCStart:
|
||||
assert(state == StateMutator);
|
||||
@ -728,7 +828,7 @@ void withOutputFile(const char *base, const char *name, func f)
|
||||
{
|
||||
const size_t bufSize = 256;
|
||||
char filename[bufSize];
|
||||
int r = snprintf(filename, bufSize, "%s%s", base, name);
|
||||
int r = snprintf(filename, bufSize, "%s-%s.csv", base, name);
|
||||
assert(r > 0 && r < bufSize);
|
||||
|
||||
FILE *file = fopen(filename, "w");
|
||||
@ -751,15 +851,22 @@ main(int argc, const char *argv[])
|
||||
processTraceFile(inputFile);
|
||||
|
||||
using namespace std::placeholders;
|
||||
withOutputFile(outputBase, "-bytesAllocatedBySlice.csv", outputGcBytesAllocated);
|
||||
withOutputFile(outputBase, "-bytesUsedBySlice.csv", outputGcBytesUsed);
|
||||
withOutputFile(outputBase, "-thingCounts.csv", outputThingCounts);
|
||||
withOutputFile(outputBase, "-objectCounts.csv", outputObjectCounts);
|
||||
withOutputFile(outputBase, "-lifetimeByClassForNursery.csv",
|
||||
withOutputFile(outputBase, "bytesAllocatedBySlice", outputGcBytesAllocated);
|
||||
withOutputFile(outputBase, "bytesUsedBySlice", outputGcBytesUsed);
|
||||
withOutputFile(outputBase, "thingCounts", outputThingCounts);
|
||||
withOutputFile(outputBase, "objectCounts", outputObjectCounts);
|
||||
withOutputFile(outputBase, "lifetimeByClassForNursery",
|
||||
std::bind(outputLifetimeByClass, _1, Nursery));
|
||||
withOutputFile(outputBase, "-lifetimeByKindForHeap.csv",
|
||||
withOutputFile(outputBase, "lifetimeByKindForHeap",
|
||||
std::bind(outputLifetimeByKind, _1, TenuredHeap));
|
||||
withOutputFile(outputBase, "-lifetimeByHeap.csv", outputLifetimeByHeap);
|
||||
withOutputFile(outputBase, "-lifetimeBySlice.csv", outputLifetimeBySlice);
|
||||
withOutputFile(outputBase, "lifetimeByHeap", outputLifetimeByHeap);
|
||||
withOutputFile(outputBase, "lifetimeByHasFinalizer",
|
||||
outputLifetimeByHasFinalizer);
|
||||
withOutputFile(outputBase, "finalizedHeapObjectlifetimeByClass",
|
||||
outputFinalizedHeapObjectLifetimeByClass);
|
||||
withOutputFile(outputBase, "lifetimeByTypeForNursery",
|
||||
std::bind(outputLifetimeByType, _1, Nursery));
|
||||
withOutputFile(outputBase, "lifetimeByTypeForHeap",
|
||||
std::bind(outputLifetimeByType, _1, TenuredHeap));
|
||||
return 0;
|
||||
}
|
||||
|
@ -34,6 +34,43 @@ class MarkingValidator;
|
||||
struct AutoPrepareForTracing;
|
||||
class AutoTraceSession;
|
||||
|
||||
class ChunkPool
|
||||
{
|
||||
Chunk *emptyChunkListHead;
|
||||
size_t emptyCount;
|
||||
|
||||
public:
|
||||
ChunkPool()
|
||||
: emptyChunkListHead(nullptr),
|
||||
emptyCount(0)
|
||||
{}
|
||||
|
||||
size_t getEmptyCount() const {
|
||||
return emptyCount;
|
||||
}
|
||||
|
||||
/* Must be called with the GC lock taken. */
|
||||
inline Chunk *get(JSRuntime *rt);
|
||||
|
||||
/* Must be called either during the GC or with the GC lock taken. */
|
||||
inline void put(Chunk *chunk);
|
||||
|
||||
/* Must be called with the GC lock taken. */
|
||||
void expireAndFree(JSRuntime *rt, bool releaseAll);
|
||||
|
||||
class Enum {
|
||||
public:
|
||||
Enum(ChunkPool &pool) : pool(pool), chunkp(&pool.emptyChunkListHead) {}
|
||||
bool empty() { return !*chunkp; }
|
||||
Chunk *front();
|
||||
inline void popFront();
|
||||
inline void removeAndPopFront();
|
||||
private:
|
||||
ChunkPool &pool;
|
||||
Chunk **chunkp;
|
||||
};
|
||||
};
|
||||
|
||||
struct ConservativeGCData
|
||||
{
|
||||
/*
|
||||
@ -152,6 +189,10 @@ class GCRuntime
|
||||
void setDeterministic(bool enable);
|
||||
#endif
|
||||
|
||||
size_t bytesAllocated() { return bytes; }
|
||||
size_t maxBytesAllocated() { return maxBytes; }
|
||||
size_t maxMallocBytesAllocated() { return maxBytes; }
|
||||
|
||||
public:
|
||||
// Internal public interface
|
||||
js::gc::State state() { return incrementalState; }
|
||||
@ -291,9 +332,14 @@ class GCRuntime
|
||||
marker.setGCMode(mode);
|
||||
}
|
||||
|
||||
inline void updateOnChunkFree(const ChunkInfo &info);
|
||||
inline void updateOnFreeArenaAlloc(const ChunkInfo &info);
|
||||
inline void updateOnArenaFree(const ChunkInfo &info);
|
||||
inline void updateBytesAllocated(ptrdiff_t size);
|
||||
|
||||
GCChunkSet::Range allChunks() { return chunkSet.all(); }
|
||||
inline Chunk **getAvailableChunkList(Zone *zone);
|
||||
void moveChunkToFreePool(Chunk *chunk);
|
||||
bool hasChunk(Chunk *chunk) { return chunkSet.has(chunk); }
|
||||
|
||||
#ifdef JS_GC_ZEAL
|
||||
void startVerifyPreBarriers();
|
||||
@ -309,6 +355,16 @@ class GCRuntime
|
||||
Chunk *pickChunk(Zone *zone, AutoMaybeStartBackgroundAllocation &maybeStartBackgroundAllocation);
|
||||
inline void arenaAllocatedDuringGC(JS::Zone *zone, ArenaHeader *arena);
|
||||
|
||||
/*
|
||||
* Return the list of chunks that can be released outside the GC lock.
|
||||
* Must be called either during the GC or with the GC lock taken.
|
||||
*/
|
||||
Chunk *expireChunkPool(bool releaseAll);
|
||||
void expireAndFreeChunkPool(bool releaseAll);
|
||||
void freeChunkList(Chunk *chunkListHead);
|
||||
void prepareToFreeChunk(ChunkInfo &info);
|
||||
void releaseChunk(Chunk *chunk);
|
||||
|
||||
inline bool wantBackgroundAllocation() const;
|
||||
|
||||
bool initZeal();
|
||||
@ -358,7 +414,7 @@ class GCRuntime
|
||||
void markAllGrayReferences();
|
||||
#endif
|
||||
|
||||
public: // Internal state, public for now
|
||||
public:
|
||||
JSRuntime *rt;
|
||||
|
||||
/* Embedders can use this zone however they wish. */
|
||||
@ -369,6 +425,16 @@ class GCRuntime
|
||||
|
||||
js::gc::SystemPageAllocator pageAllocator;
|
||||
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
js::Nursery nursery;
|
||||
js::gc::StoreBuffer storeBuffer;
|
||||
#endif
|
||||
|
||||
js::gcstats::Statistics stats;
|
||||
|
||||
js::GCMarker marker;
|
||||
|
||||
private:
|
||||
/*
|
||||
* Set of all GC chunks with at least one allocated thing. The
|
||||
* conservative GC uses it to quickly check if a possible GC thing points
|
||||
@ -387,15 +453,6 @@ class GCRuntime
|
||||
js::gc::Chunk *userAvailableChunkListHead;
|
||||
js::gc::ChunkPool chunkPool;
|
||||
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
js::Nursery nursery;
|
||||
js::gc::StoreBuffer storeBuffer;
|
||||
#endif
|
||||
|
||||
js::gcstats::Statistics stats;
|
||||
|
||||
js::GCMarker marker;
|
||||
|
||||
js::RootedValueMap rootsHash;
|
||||
|
||||
/* This is updated by both the main and GC helper threads. */
|
||||
@ -404,7 +461,6 @@ class GCRuntime
|
||||
size_t maxBytes;
|
||||
size_t maxMallocBytes;
|
||||
|
||||
private:
|
||||
/*
|
||||
* Number of the committed arenas in all GC chunks including empty chunks.
|
||||
*/
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
using namespace js;
|
||||
using namespace js::gc;
|
||||
using namespace js::types;
|
||||
|
||||
JS_STATIC_ASSERT(AllocKinds == FINALIZE_LIMIT);
|
||||
JS_STATIC_ASSERT(LastObjectAllocKind == FINALIZE_OBJECT_LAST);
|
||||
@ -24,6 +25,7 @@ JS_STATIC_ASSERT(LastObjectAllocKind == FINALIZE_OBJECT_LAST);
|
||||
static FILE *gcTraceFile = nullptr;
|
||||
|
||||
static HashSet<const Class *, DefaultHasher<const Class *>, SystemAllocPolicy> tracedClasses;
|
||||
static HashSet<const TypeObject *, DefaultHasher<const TypeObject *>, SystemAllocPolicy> tracedTypes;
|
||||
|
||||
static inline void
|
||||
WriteWord(uint64_t data)
|
||||
@ -77,19 +79,23 @@ TraceString(const char* string)
|
||||
bool
|
||||
js::gc::InitTrace(GCRuntime &gc)
|
||||
{
|
||||
/* This currently does not support multiple runtimes. */
|
||||
MOZ_ALWAYS_TRUE(!gcTraceFile);
|
||||
|
||||
char *filename = getenv("JS_GC_TRACE");
|
||||
if (!filename)
|
||||
return true;
|
||||
|
||||
if (!tracedClasses.init())
|
||||
if (!tracedClasses.init() || !tracedTypes.init()) {
|
||||
FinishTrace();
|
||||
return false;
|
||||
|
||||
/* This currently does not support multiple runtimes. */
|
||||
JS_ASSERT(!gcTraceFile);
|
||||
}
|
||||
|
||||
gcTraceFile = fopen(filename, "w");
|
||||
if (!gcTraceFile)
|
||||
if (!gcTraceFile) {
|
||||
FinishTrace();
|
||||
return false;
|
||||
}
|
||||
|
||||
TraceEvent(TraceEventInit, 0, TraceFormatVersion);
|
||||
|
||||
@ -103,8 +109,12 @@ js::gc::InitTrace(GCRuntime &gc)
|
||||
void
|
||||
js::gc::FinishTrace()
|
||||
{
|
||||
if (gcTraceFile)
|
||||
if (gcTraceFile) {
|
||||
fclose(gcTraceFile);
|
||||
gcTraceFile = nullptr;
|
||||
}
|
||||
tracedClasses.finish();
|
||||
tracedTypes.finish();
|
||||
}
|
||||
|
||||
bool
|
||||
@ -138,9 +148,44 @@ MaybeTraceClass(const Class *clasp)
|
||||
return;
|
||||
|
||||
TraceEvent(TraceEventClassInfo, uint64_t(clasp));
|
||||
TraceInt(clasp->flags);
|
||||
TraceString(clasp->name);
|
||||
tracedClasses.put(clasp);
|
||||
TraceInt(clasp->flags);
|
||||
TraceInt(clasp->finalize != nullptr);
|
||||
|
||||
MOZ_ALWAYS_TRUE(tracedClasses.put(clasp));
|
||||
}
|
||||
|
||||
static void
|
||||
MaybeTraceType(TypeObject *type)
|
||||
{
|
||||
if (tracedTypes.has(type))
|
||||
return;
|
||||
|
||||
MaybeTraceClass(type->clasp());
|
||||
TraceEvent(TraceEventTypeInfo, uint64_t(type));
|
||||
TraceAddress(type->clasp());
|
||||
TraceInt(type->flags());
|
||||
|
||||
MOZ_ALWAYS_TRUE(tracedTypes.put(type));
|
||||
}
|
||||
|
||||
void
|
||||
js::gc::TraceTypeNewScript(TypeObject *type)
|
||||
{
|
||||
const size_t bufLength = 128;
|
||||
static char buffer[bufLength];
|
||||
JS_ASSERT(type->hasNewScript());
|
||||
JSAtom *funName = type->newScript()->fun->displayAtom();
|
||||
if (!funName)
|
||||
return;
|
||||
|
||||
size_t length = funName->length();
|
||||
MOZ_ALWAYS_TRUE(length < bufLength);
|
||||
CopyChars(reinterpret_cast<Latin1Char *>(buffer), *funName);
|
||||
buffer[length] = 0;
|
||||
|
||||
TraceEvent(TraceEventTypeNewScript, uint64_t(type));
|
||||
TraceString(buffer);
|
||||
}
|
||||
|
||||
void
|
||||
@ -149,10 +194,10 @@ js::gc::TraceCreateObject(JSObject* object)
|
||||
if (!gcTraceFile)
|
||||
return;
|
||||
|
||||
const Class *clasp = object->type()->clasp();
|
||||
MaybeTraceClass(clasp);
|
||||
TypeObject *type = object->type();
|
||||
MaybeTraceType(type);
|
||||
TraceEvent(TraceEventCreateObject, uint64_t(object));
|
||||
TraceAddress(clasp);
|
||||
TraceAddress(type);
|
||||
}
|
||||
|
||||
void
|
||||
@ -183,6 +228,10 @@ js::gc::TraceMajorGCStart()
|
||||
void
|
||||
js::gc::TraceTenuredFinalize(Cell *thing)
|
||||
{
|
||||
if (!gcTraceFile)
|
||||
return;
|
||||
if (thing->tenuredGetAllocKind() == FINALIZE_TYPE_OBJECT)
|
||||
tracedTypes.remove(static_cast<const TypeObject *>(thing));
|
||||
TraceEvent(TraceEventTenuredFinalize, uint64_t(thing));
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,7 @@ extern void TraceMinorGCEnd();
|
||||
extern void TraceMajorGCStart();
|
||||
extern void TraceTenuredFinalize(Cell *thing);
|
||||
extern void TraceMajorGCEnd();
|
||||
extern void TraceTypeNewScript(js::types::TypeObject *type);
|
||||
|
||||
#else
|
||||
|
||||
@ -44,6 +45,7 @@ inline void TraceMinorGCEnd() {}
|
||||
inline void TraceMajorGCStart() {}
|
||||
inline void TraceTenuredFinalize(Cell *thing) {}
|
||||
inline void TraceMajorGCEnd() {}
|
||||
inline void TraceTypeNewScript(js::types::TypeObject *type) {}
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -23,6 +23,8 @@ enum GCTraceEvent {
|
||||
TraceEventNurseryAlloc,
|
||||
TraceEventTenuredAlloc,
|
||||
TraceEventClassInfo,
|
||||
TraceEventTypeInfo,
|
||||
TraceEventTypeNewScript,
|
||||
TraceEventCreateObject,
|
||||
TraceEventMinorGCStart,
|
||||
TraceEventPromoteToTenured,
|
||||
|
@ -834,13 +834,6 @@ struct Chunk
|
||||
|
||||
void decommitAllArenas(JSRuntime *rt);
|
||||
|
||||
/* Must be called with the GC lock taken. */
|
||||
static inline void release(JSRuntime *rt, Chunk *chunk);
|
||||
static inline void releaseList(JSRuntime *rt, Chunk *chunkListHead);
|
||||
|
||||
/* Must be called with the GC lock taken. */
|
||||
inline void prepareToBeFreed(JSRuntime *rt);
|
||||
|
||||
/*
|
||||
* Assuming that the info.prevp points to the next field of the previous
|
||||
* chunk in a doubly-linked list, get that chunk.
|
||||
|
@ -85,7 +85,7 @@ js::IterateChunks(JSRuntime *rt, void *data, IterateChunkCallback chunkCallback)
|
||||
{
|
||||
AutoPrepareForTracing prep(rt, SkipAtoms);
|
||||
|
||||
for (js::GCChunkSet::Range r = rt->gc.chunkSet.all(); !r.empty(); r.popFront())
|
||||
for (js::GCChunkSet::Range r = rt->gc.allChunks(); !r.empty(); r.popFront())
|
||||
chunkCallback(rt, data, r.front());
|
||||
}
|
||||
|
||||
|
@ -884,7 +884,7 @@ js::Nursery::collect(JSRuntime *rt, JS::gcreason::Reason reason, TypeObjectList
|
||||
// We ignore gcMaxBytes when allocating for minor collection. However, if we
|
||||
// overflowed, we disable the nursery. The next time we allocate, we'll fail
|
||||
// because gcBytes >= gcMaxBytes.
|
||||
if (rt->gc.bytes >= rt->gc.maxBytes)
|
||||
if (rt->gc.bytesAllocated() >= rt->gc.maxBytesAllocated())
|
||||
disable();
|
||||
|
||||
TIME_END(total);
|
||||
|
@ -187,7 +187,7 @@ IsAddressableGCThing(JSRuntime *rt, uintptr_t w,
|
||||
|
||||
Chunk *chunk = Chunk::fromAddress(addr);
|
||||
|
||||
if (!rt->gc.chunkSet.has(chunk))
|
||||
if (!rt->gc.hasChunk(chunk))
|
||||
return CGCT_NOTCHUNK;
|
||||
|
||||
/*
|
||||
|
@ -531,7 +531,7 @@ Statistics::beginGC()
|
||||
sccTimes.clearAndFree();
|
||||
nonincrementalReason = nullptr;
|
||||
|
||||
preBytes = runtime->gc.bytes;
|
||||
preBytes = runtime->gc.bytesAllocated();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -49,7 +49,7 @@ JS::Zone::Zone(JSRuntime *rt)
|
||||
JS_ASSERT(reinterpret_cast<JS::shadow::Zone *>(this) ==
|
||||
static_cast<JS::shadow::Zone *>(this));
|
||||
|
||||
setGCMaxMallocBytes(rt->gc.maxMallocBytes * 0.9);
|
||||
setGCMaxMallocBytes(rt->gc.maxMallocBytesAllocated() * 0.9);
|
||||
}
|
||||
|
||||
Zone::~Zone()
|
||||
|
@ -9,6 +9,7 @@ assertAsmTypeFail(USE_ASM + 'function f(){} return 0');
|
||||
assertAsmTypeFail(USE_ASM + 'function f() 0; return 0');
|
||||
assertAsmTypeFail(USE_ASM + 'function f(){} return g');
|
||||
assertAsmTypeFail(USE_ASM + 'function f(){} function f(){} return f');
|
||||
assertAsmTypeFail(USE_ASM + 'function f(){}; function g(){}; return {f, g}');
|
||||
assertAsmTypeFail(USE_ASM + 'var f=0; function f(){} return f');
|
||||
assertAsmTypeFail('glob', USE_ASM + 'var f=glob.Math.imul; function f(){} return f');
|
||||
assertAsmTypeFail('glob','foreign', USE_ASM + 'var f=foreign.foo; function f(){} return f');
|
||||
|
1
js/src/jit-test/tests/basic/bug1035325.js
Normal file
@ -0,0 +1 @@
|
||||
print("\uDBFF\uDFFF"); // don't crash/assert
|
@ -274,8 +274,8 @@ FunctionStatementList(ParseNode *fn)
|
||||
static inline bool
|
||||
IsNormalObjectField(ExclusiveContext *cx, ParseNode *pn)
|
||||
{
|
||||
JS_ASSERT(pn->isKind(PNK_COLON) || pn->isKind(PNK_SHORTHAND));
|
||||
return pn->getOp() == JSOP_INITPROP &&
|
||||
return pn->isKind(PNK_COLON) &&
|
||||
pn->getOp() == JSOP_INITPROP &&
|
||||
BinaryLeft(pn)->isKind(PNK_NAME) &&
|
||||
BinaryLeft(pn)->name() != cx->names().proto;
|
||||
}
|
||||
@ -288,9 +288,9 @@ ObjectNormalFieldName(ExclusiveContext *cx, ParseNode *pn)
|
||||
}
|
||||
|
||||
static inline ParseNode *
|
||||
ObjectFieldInitializer(ParseNode *pn)
|
||||
ObjectNormalFieldInitializer(ExclusiveContext *cx, ParseNode *pn)
|
||||
{
|
||||
JS_ASSERT(pn->isKind(PNK_COLON) || pn->isKind(PNK_SHORTHAND));
|
||||
JS_ASSERT(IsNormalObjectField(cx, pn));
|
||||
return BinaryRight(pn);
|
||||
}
|
||||
|
||||
@ -5795,7 +5795,7 @@ CheckModuleExportObject(ModuleCompiler &m, ParseNode *object)
|
||||
|
||||
PropertyName *fieldName = ObjectNormalFieldName(m.cx(), pn);
|
||||
|
||||
ParseNode *initNode = ObjectFieldInitializer(pn);
|
||||
ParseNode *initNode = ObjectNormalFieldInitializer(m.cx(), pn);
|
||||
if (!initNode->isKind(PNK_NAME))
|
||||
return m.fail(initNode, "initializer of exported object literal must be name of function");
|
||||
|
||||
|
@ -351,7 +351,9 @@ JitFrameIterator::machineState() const
|
||||
for (GeneralRegisterBackwardIterator iter(reader.allGprSpills()); iter.more(); iter++)
|
||||
machine.setRegisterLocation(*iter, --spill);
|
||||
|
||||
double *floatSpill = reinterpret_cast<double *>(spill);
|
||||
uint8_t *spillAlign = alignDoubleSpillWithOffset(reinterpret_cast<uint8_t *>(spill), 0);
|
||||
|
||||
double *floatSpill = reinterpret_cast<double *>(spillAlign);
|
||||
for (FloatRegisterBackwardIterator iter(reader.allFloatSpills()); iter.more(); iter++)
|
||||
machine.setRegisterLocation(*iter, --floatSpill);
|
||||
|
||||
@ -1055,6 +1057,43 @@ JitActivationIterator::jitStackRange(uintptr_t *&min, uintptr_t *&end)
|
||||
end = reinterpret_cast<uintptr_t *>(frames.prevFp());
|
||||
}
|
||||
|
||||
#ifdef JS_CODEGEN_MIPS
|
||||
uint8_t *
|
||||
alignDoubleSpillWithOffset(uint8_t *pointer, int32_t offset)
|
||||
{
|
||||
uint32_t address = reinterpret_cast<uint32_t>(pointer);
|
||||
address = (address - offset) & ~(StackAlignment - 1);
|
||||
return reinterpret_cast<uint8_t *>(address);
|
||||
}
|
||||
|
||||
static void
|
||||
MarkJitExitFrameCopiedArguments(JSTracer *trc, const VMFunction *f, IonExitFooterFrame *footer)
|
||||
{
|
||||
uint8_t *doubleArgs = reinterpret_cast<uint8_t *>(footer);
|
||||
doubleArgs = alignDoubleSpillWithOffset(doubleArgs, sizeof(intptr_t));
|
||||
if (f->outParam == Type_Handle)
|
||||
doubleArgs -= sizeof(Value);
|
||||
doubleArgs -= f->doubleByRefArgs() * sizeof(double);
|
||||
|
||||
for (uint32_t explicitArg = 0; explicitArg < f->explicitArgs; explicitArg++) {
|
||||
if (f->argProperties(explicitArg) == VMFunction::DoubleByRef) {
|
||||
// Arguments with double size can only have RootValue type.
|
||||
if (f->argRootType(explicitArg) == VMFunction::RootValue)
|
||||
gc::MarkValueRoot(trc, reinterpret_cast<Value*>(doubleArgs), "ion-vm-args");
|
||||
else
|
||||
JS_ASSERT(f->argRootType(explicitArg) == VMFunction::RootNone);
|
||||
doubleArgs += sizeof(double);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
static void
|
||||
MarkJitExitFrameCopiedArguments(JSTracer *trc, const VMFunction *f, IonExitFooterFrame *footer)
|
||||
{
|
||||
// This is NO-OP on other platforms.
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
MarkJitExitFrame(JSTracer *trc, const JitFrameIterator &frame)
|
||||
{
|
||||
@ -1201,6 +1240,8 @@ MarkJitExitFrame(JSTracer *trc, const JitFrameIterator &frame)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MarkJitExitFrameCopiedArguments(trc, f, footer);
|
||||
}
|
||||
|
||||
static void
|
||||
|