Merge m-c to fx-team. a=merge

This commit is contained in:
Ryan VanderMeulen 2014-06-06 17:34:33 -04:00
commit 7d7d32966e
156 changed files with 3064 additions and 2325 deletions

View File

@ -22,4 +22,5 @@
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
# don't change CLOBBER for WebIDL changes any more.
Bug 978238 needs clobber because of changes to js.msg (see bug 1019955).
Bug 904723 (Array.from) needs a clobber once again because of changes to js.msg
and self-hosted code (see bug 1019955).

View File

@ -426,7 +426,7 @@ nsAccessibilityService::TreeViewChanged(nsIPresShell* aPresShell,
Accessible* accessible = document->GetAccessible(aContent);
if (accessible) {
XULTreeAccessible* treeAcc = accessible->AsXULTree();
if (treeAcc)
if (treeAcc)
treeAcc->TreeViewChanged(aView);
}
}
@ -1175,7 +1175,7 @@ nsAccessibilityService::CreateAccessibleByType(nsIContent* aContent,
nsRefPtr<Accessible> accessible = new OuterDocAccessible(aContent, aDoc);
return accessible.forget();
}
nsRefPtr<Accessible> accessible;
#ifdef MOZ_XUL
// XUL controls
@ -1558,18 +1558,22 @@ nsAccessibilityService::CreateAccessibleByFrameType(nsIFrame* aFrame,
break;
case eHTMLTableRowType: {
// Accessible HTML table row must be a child of tbody/tfoot/thead of
// accessible HTML table or must be a child of accessible of HTML table.
if (aContext->IsTable()) {
// Accessible HTML table row may be a child of tbody/tfoot/thead of
// accessible HTML table or a direct child of accessible of HTML table.
Accessible* table = aContext->IsTable() ?
aContext :
(aContext->Parent()->IsTable() ? aContext->Parent() : nullptr);
if (table) {
nsIContent* parentContent = aContent->GetParent();
nsIFrame* parentFrame = parentContent->GetPrimaryFrame();
if (parentFrame->GetType() == nsGkAtoms::tableRowGroupFrame) {
if (parentFrame->GetType() != nsGkAtoms::tableOuterFrame) {
parentContent = parentContent->GetParent();
parentFrame = parentContent->GetPrimaryFrame();
}
if (parentFrame->GetType() == nsGkAtoms::tableOuterFrame &&
aContext->GetContent() == parentContent) {
table->GetContent() == parentContent) {
newAcc = new HTMLTableRowAccessible(aContent, document);
}
}
@ -1649,7 +1653,7 @@ NS_GetAccessibilityService(nsIAccessibilityService** aResult)
{
NS_ENSURE_TRUE(aResult, NS_ERROR_NULL_POINTER);
*aResult = nullptr;
if (nsAccessibilityService::gAccessibilityService) {
NS_ADDREF(*aResult = nsAccessibilityService::gAccessibilityService);
return NS_OK;

View File

@ -345,7 +345,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=410052
<td>6</td>
</tr>
<tr>
<td colspan="3">7</td>
<td colspan="3">7</td>
</tr>
</tbody>

View File

@ -138,7 +138,7 @@
/////////////////////////////////////////////////////////////////////////
// table4 (display: table-row)
accTree =
accTree =
{ TABLE: [
{ ROW: [
{ CELL: [
@ -148,6 +148,20 @@
};
testAccessibleTree("table4", accTree);
/////////////////////////////////////////////////////////////////////////
// table5 (intermediate accessible for tbody)
accTree =
{ TABLE: [
{ TEXT_CONTAINER: [
{ ROW: [
{ CELL: [
{ TEXT_LEAF: [ ] }
] }
] }
] } ]
};
testAccessibleTree("table5", accTree);
SimpleTest.finish();
}
@ -242,5 +256,13 @@
<td>cell1</td>
</div>
</table>
<table id="table5">
<tbody style="display:block;overflow:auto;">
<tr>
<td>bla</td>
</tr>
</tbody>
</table>
</body>
</html>

View File

@ -19,13 +19,13 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="857129928b6e56a809cee9d5445effb8fa9f1c2c"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="3f3963fe6e165c0a7afc6222e1137d0862c70b30"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="531cf670e485649c69746e46d567929fcd54cbc5"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="8e4420c0c5c8e8c8e58a000278a7129403769f96"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="9100fa82fc355f5201e23e400fc6b40e875304ed"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="a819a94a572c7b32556435491ed8eaab841a95ff"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="89812422efe8df364ddf364b4740030496181277"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6a68dde81fcbe3c6bbd45bafdeb94c3f269bdc20"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
<project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>

View File

@ -17,10 +17,10 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="857129928b6e56a809cee9d5445effb8fa9f1c2c"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="3f3963fe6e165c0a7afc6222e1137d0862c70b30"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="531cf670e485649c69746e46d567929fcd54cbc5"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="89812422efe8df364ddf364b4740030496181277"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6a68dde81fcbe3c6bbd45bafdeb94c3f269bdc20"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<!-- Stock Android things -->
@ -126,11 +126,11 @@
<!-- Emulator specific things -->
<project name="android-development" path="development" remote="b2g" revision="dab55669da8f48b6e57df95d5af9f16b4a87b0b1"/>
<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="c3ee0c875393607430086f942950d1b3f496ab0e"/>
<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="0a2b7e94dce4989a3740fea6f6e3152978216c88"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="cba8ebe395652e62a911d885cd6ac1bb4ad3ed57"/>
<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="dd94b2e17a146cb782d71933d25dcaa9c060e6ce"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="832f4acaf481a19031e479a40b03d9ce5370ddee"/>
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="d0aa65b140a45016975ed0ecf35f280dd336e1d3"/>
<project name="android-sdk" path="sdk" remote="b2g" revision="8b1365af38c9a653df97349ee53a3f5d64fd590a"/>
</manifest>

View File

@ -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="857129928b6e56a809cee9d5445effb8fa9f1c2c"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="3f3963fe6e165c0a7afc6222e1137d0862c70b30"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="531cf670e485649c69746e46d567929fcd54cbc5"/>
<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="89812422efe8df364ddf364b4740030496181277"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6a68dde81fcbe3c6bbd45bafdeb94c3f269bdc20"/>
<!-- Stock Android things -->
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>

View File

@ -19,13 +19,13 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="857129928b6e56a809cee9d5445effb8fa9f1c2c"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="3f3963fe6e165c0a7afc6222e1137d0862c70b30"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="531cf670e485649c69746e46d567929fcd54cbc5"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="8e4420c0c5c8e8c8e58a000278a7129403769f96"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="9100fa82fc355f5201e23e400fc6b40e875304ed"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="a819a94a572c7b32556435491ed8eaab841a95ff"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="89812422efe8df364ddf364b4740030496181277"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6a68dde81fcbe3c6bbd45bafdeb94c3f269bdc20"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
<project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>

View File

@ -17,10 +17,10 @@
</project>
<project name="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="857129928b6e56a809cee9d5445effb8fa9f1c2c"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="3f3963fe6e165c0a7afc6222e1137d0862c70b30"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="531cf670e485649c69746e46d567929fcd54cbc5"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="89812422efe8df364ddf364b4740030496181277"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6a68dde81fcbe3c6bbd45bafdeb94c3f269bdc20"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<!-- Stock Android things -->
@ -119,7 +119,7 @@
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="e8a318f7690092e639ba88891606f4183e846d3f"/>
<project name="device/qcom/common" path="device/qcom/common" revision="34ed8345250bb97262d70a052217a92e83444ede"/>
<project name="device-flame" path="device/t2m/flame" remote="b2g" revision="95423152b66d4a41032414fc3fb8c3d9a0636c7d"/>
<project name="kernel/msm" path="kernel" revision="e3895712aa9ddb955f0fdac880aa553556e64c41"/>
<project name="kernel/msm" path="kernel" revision="ccfff630163ca9a0530701fa93b501c34042d06c"/>
<project name="platform/bootable/recovery" path="bootable/recovery" revision="f2914eacee9120680a41463708bb6ee8291749fc"/>
<project name="platform/external/bluetooth/bluedroid" path="external/bluetooth/bluedroid" revision="81c4a859d75d413ad688067829d21b7ba9205f81"/>
<project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="f0689ac1914cdbc59e53bdc9edd9013dc157c299"/>

View File

@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
"revision": "e32dee285e41ff8be7779ab4adb3db81a5b36570",
"revision": "99abcedfe288d6d29352b2add96b5ace90543985",
"repo_path": "/integration/gaia-central"
}

View File

@ -17,12 +17,12 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="857129928b6e56a809cee9d5445effb8fa9f1c2c"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="3f3963fe6e165c0a7afc6222e1137d0862c70b30"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="531cf670e485649c69746e46d567929fcd54cbc5"/>
<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="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="89812422efe8df364ddf364b4740030496181277"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6a68dde81fcbe3c6bbd45bafdeb94c3f269bdc20"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
<project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>

View File

@ -15,7 +15,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="857129928b6e56a809cee9d5445effb8fa9f1c2c"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="3f3963fe6e165c0a7afc6222e1137d0862c70b30"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="531cf670e485649c69746e46d567929fcd54cbc5"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -17,10 +17,10 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="857129928b6e56a809cee9d5445effb8fa9f1c2c"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="3f3963fe6e165c0a7afc6222e1137d0862c70b30"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="531cf670e485649c69746e46d567929fcd54cbc5"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="89812422efe8df364ddf364b4740030496181277"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6a68dde81fcbe3c6bbd45bafdeb94c3f269bdc20"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<!-- Stock Android things -->

View File

@ -17,12 +17,12 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="857129928b6e56a809cee9d5445effb8fa9f1c2c"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="3f3963fe6e165c0a7afc6222e1137d0862c70b30"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="531cf670e485649c69746e46d567929fcd54cbc5"/>
<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="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="89812422efe8df364ddf364b4740030496181277"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6a68dde81fcbe3c6bbd45bafdeb94c3f269bdc20"/>
<project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>

View File

@ -44,6 +44,8 @@
<!ENTITY translation.errorTranslating.label "There has been an error translating this page.">
<!ENTITY translation.tryAgain.button "Try Again">
<!ENTITY translation.serviceUnavailable.label "Translation is not available at the moment. Please try again later.">
<!ENTITY translation.options.menu "Options">
<!-- LOCALIZATION NOTE (translation.options.neverForSite.accesskey,
- translation.options.preferences.accesskey):

View File

@ -4046,7 +4046,7 @@ CanvasRenderingContext2D::PutImageData_explicit(int32_t x, int32_t y, uint32_t w
int32_t dirtyWidth, int32_t dirtyHeight)
{
if (w == 0 || h == 0) {
return NS_ERROR_DOM_SYNTAX_ERR;
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
IntRect dirtyRect;
@ -4100,7 +4100,7 @@ CanvasRenderingContext2D::PutImageData_explicit(int32_t x, int32_t y, uint32_t w
uint32_t len = w * h * 4;
if (dataLen != len) {
return NS_ERROR_DOM_SYNTAX_ERR;
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
nsRefPtr<gfxImageSurface> imgsurf = new gfxImageSurface(gfxIntSize(w, h),

View File

@ -230,7 +230,8 @@ VideoData* VideoData::Create(VideoInfo& aInfo,
// The following situations could be triggered by invalid input
if (aPicture.width <= 0 || aPicture.height <= 0) {
NS_WARNING("Empty picture rect");
// In debug mode, makes the error more noticeable
MOZ_ASSERT(false, "Empty picture rect");
return nullptr;
}
if (!ValidatePlane(aBuffer.mPlanes[0]) || !ValidatePlane(aBuffer.mPlanes[1]) ||

View File

@ -13053,3 +13053,29 @@ nsDocShell::SetInvisible(bool aInvisible)
{
mInvisible = aInvisible;
}
void
nsDocShell::SetOpener(nsITabParent* aOpener)
{
mOpener = do_GetWeakReference(aOpener);
}
nsITabParent*
nsDocShell::GetOpener()
{
nsCOMPtr<nsITabParent> opener(do_QueryReferent(mOpener));
return opener;
}
void
nsDocShell::SetOpenedRemote(nsITabParent* aOpenedRemote)
{
mOpenedRemote = do_GetWeakReference(aOpenedRemote);
}
nsITabParent*
nsDocShell::GetOpenedRemote()
{
nsCOMPtr<nsITabParent> openedRemote(do_QueryReferent(mOpenedRemote));
return openedRemote;
}

View File

@ -42,6 +42,7 @@
#include "nsIWebShellServices.h"
#include "nsILinkHandler.h"
#include "nsIClipboardCommands.h"
#include "nsITabParent.h"
#include "nsCRT.h"
#include "prtime.h"
#include "nsRect.h"
@ -902,6 +903,8 @@ private:
nsTObserverArray<nsWeakPtr> mReflowObservers;
nsTObserverArray<nsWeakPtr> mScrollObservers;
nsCString mOriginalUriString;
nsWeakPtr mOpener;
nsWeakPtr mOpenedRemote;
// Separate function to do the actual name (i.e. not _top, _self etc.)
// searching for FindItemWithName.

View File

@ -42,10 +42,11 @@ interface nsIVariant;
interface nsIPrivacyTransitionObserver;
interface nsIReflowObserver;
interface nsIScrollObserver;
interface nsITabParent;
typedef unsigned long nsLoadFlags;
[scriptable, builtinclass, uuid(3ca96c12-b69d-4b54-83c5-25a18d32a22b)]
[scriptable, builtinclass, uuid(9c65a466-9814-48f8-a4ca-c4600b03b15d)]
interface nsIDocShell : nsIDocShellTreeItem
{
/**
@ -958,4 +959,20 @@ interface nsIDocShell : nsIDocShellTreeItem
* docshell.device_size_is_page_size pref.
*/
[infallible] attribute boolean deviceSizeIsPageSize;
/**
* Regarding setOpener / getOpener - We can't use XPIDL's "attribute"
* for notxpcom, so we're relegated to using explicit gets / sets. This
* should be fine, considering that these methods should only ever be
* called from native code.
*/
[noscript,notxpcom,nostdcall] void setOpener(in nsITabParent aOpener);
[noscript,notxpcom,nostdcall] nsITabParent getOpener();
/**
* See the documentation for setOpener and getOpener about why we
* don't use attribute here instead.
*/
[noscript,notxpcom,nostdcall] void setOpenedRemote(in nsITabParent aOpenedRemote);
[noscript,notxpcom,nostdcall] nsITabParent getOpenedRemote();
};

View File

@ -11644,7 +11644,7 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
// !aCalledNoScript.
rv = pwwatch->OpenWindow2(this, url.get(), name_ptr, options_ptr,
/* aCalledFromScript = */ true,
aDialog, aNavigate, argv,
aDialog, aNavigate, nullptr, argv,
getter_AddRefs(domReturn));
} else {
// Force a system caller here so that the window watcher won't screw us
@ -11664,7 +11664,7 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
rv = pwwatch->OpenWindow2(this, url.get(), name_ptr, options_ptr,
/* aCalledFromScript = */ false,
aDialog, aNavigate, aExtraArgument,
aDialog, aNavigate, nullptr, aExtraArgument,
getter_AddRefs(domReturn));
}

View File

@ -13,15 +13,18 @@ namespace mozilla {
namespace dom {
bool
CallbackInterface::GetCallableProperty(JSContext* cx, const char* aPropName,
CallbackInterface::GetCallableProperty(JSContext* cx, JS::Handle<jsid> aPropId,
JS::MutableHandle<JS::Value> aCallable)
{
if (!JS_GetProperty(cx, CallbackPreserveColor(), aPropName, aCallable)) {
if (!JS_GetPropertyById(cx, CallbackPreserveColor(), aPropId, aCallable)) {
return false;
}
if (!aCallable.isObject() ||
!JS_ObjectIsCallable(cx, &aCallable.toObject())) {
nsPrintfCString description("Property '%s'", aPropName);
char* propName =
JS_EncodeString(cx, JS_FORGET_STRING_FLATNESS(JSID_TO_FLAT_STRING(aPropId)));
nsPrintfCString description("Property '%s'", propName);
JS_free(cx, propName);
ThrowErrorMessage(cx, MSG_NOT_CALLABLE, description.get());
return false;
}

View File

@ -31,7 +31,7 @@ public:
}
protected:
bool GetCallableProperty(JSContext* cx, const char* aPropName,
bool GetCallableProperty(JSContext* cx, JS::Handle<jsid> aPropId,
JS::MutableHandle<JS::Value> aCallable);
};

View File

@ -242,7 +242,7 @@ class CGNativePropertyHooks(CGThing):
// would require a run-time load for proper initialization, which would
// then induce static constructors. Lots of static constructors.
extern const NativePropertyHooks sNativePropertyHooks[];
""").rstrip() # BOGUS strip newline from the last line here (!)
""")
def define(self):
if self.descriptor.workers:
@ -377,8 +377,7 @@ JS_NULL_OBJECT_OPS
nullptr, /* setElement */
nullptr, /* getGenericAttributes */
nullptr, /* setGenericAttributes */
nullptr, /* deleteProperty */
nullptr, /* deleteElement */
nullptr, /* deleteGeneric */
nullptr, /* watch */
nullptr, /* unwatch */
nullptr, /* slice */
@ -401,9 +400,8 @@ JS_NULL_OBJECT_OPS
newResolveHook = "JS_ResolveStub"
enumerateHook = "JS_EnumerateStub"
return fill( # BOGUS extra blank line at the top
return fill(
"""
static const DOMJSClass Class = {
{ "${name}",
${flags},
@ -463,9 +461,8 @@ class CGDOMProxyJSClass(CGThing):
if self.descriptor.interface.identifier.name == "HTMLAllCollection":
flags.append("JSCLASS_EMULATES_UNDEFINED")
callHook = LEGACYCALLER_HOOK_NAME if self.descriptor.operations["LegacyCaller"] else 'nullptr'
return fill( # BOGUS extra blank line at the top
return fill(
"""
static const DOMJSClass Class = {
PROXY_CLASS_DEF("${name}",
0, /* extra slots */
@ -637,9 +634,8 @@ class CGInterfaceObjectJSClass(CGThing):
if len(self.descriptor.interface.namedConstructors) > 0:
slotCount += (" + %i /* slots for the named constructors */" %
len(self.descriptor.interface.namedConstructors))
return fill( # BOGUS extra newline at the top
return fill(
"""
static const DOMIfaceAndProtoJSClass InterfaceObjectClass = {
{
"Function",
@ -1420,8 +1416,7 @@ class CGAbstractClassHook(CGAbstractStaticMethod):
args)
def definition_body_prologue(self):
return ("\n" # BOGUS extra blank line at start of function
"%s* self = UnwrapDOMObject<%s>(obj);\n" %
return ("%s* self = UnwrapDOMObject<%s>(obj);\n" %
(self.descriptor.nativeType, self.descriptor.nativeType))
def definition_body(self):
@ -1542,9 +1537,8 @@ class CGClassConstructor(CGAbstractStaticMethod):
else:
ctorName = self.descriptor.interface.identifier.name
preamble = fill( # BOGUS extra blank line at beginning of function body
preamble = fill(
"""
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
JS::Rooted<JSObject*> obj(cx, &args.callee());
$*{chromeOnlyCheck}
@ -1566,7 +1560,7 @@ class CGClassConstructor(CGAbstractStaticMethod):
callGenerator = CGMethodCall(nativeName, True, self.descriptor,
self._ctor, isConstructor=True,
constructorName=ctorName)
return preamble + callGenerator.define()
return preamble + "\n" + callGenerator.define()
# Encapsulate the constructor in a helper method to share genConstructorBody with CGJSImplMethod.
@ -1708,9 +1702,7 @@ class CGClassHasInstanceHook(CGAbstractStaticMethod):
return self.generate_code()
def generate_code(self):
# BOGUS extra blank line at start of function
header = dedent("""
if (!vp.isObject()) {
*bp = false;
return true;
@ -1739,8 +1731,7 @@ class CGClassHasInstanceHook(CGAbstractStaticMethod):
nsCOMPtr<nsIDOM${name}> qiResult = do_QueryInterface(native);
*bp = !!qiResult;
return true;
""", # BOGUS extra blank line at end of function
""",
nativeType=self.descriptor.nativeType,
name=self.descriptor.interface.identifier.name))
@ -2356,8 +2347,7 @@ class CGNativeProperties(CGList):
return ""
def define(self):
# BOGUSly strip off a newline
return CGList.define(self).rstrip()
return CGList.define(self)
class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
@ -2586,15 +2576,13 @@ class CGGetPerInterfaceObject(CGAbstractMethod):
self.id = idPrefix + "id::" + self.descriptor.name
def definition_body(self):
# BOGUS extra blank line at the beginning of the code below
# BOGUS - should be a blank line between an if-block and following comment below
return fill(
"""
/* Make sure our global is sane. Hopefully we can remove this sometime */
if (!(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL)) {
return JS::NullPtr();
}
/* Check to see whether the interface objects are already installed */
ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(aGlobal);
if (!protoAndIfaceCache.EntrySlotIfExists(${id})) {
@ -2622,12 +2610,11 @@ class CGGetProtoObjectMethod(CGGetPerInterfaceObject):
"prototypes::")
def definition_body(self):
# BOGUS extra blank line at start of method
return dedent("""
/* Get the interface prototype object for this class. This will create the
object as needed. */
bool aDefineOnGlobal = true;
""") + CGGetPerInterfaceObject.definition_body(self)
@ -2642,11 +2629,10 @@ class CGGetConstructorObjectMethod(CGGetPerInterfaceObject):
extraArgs=[Argument("bool", "aDefineOnGlobal", "true")])
def definition_body(self):
# BOGUS extra blank line at start of method
return dedent("""
/* Get the interface object for this class. This will create the object as
needed. */
""") + CGGetPerInterfaceObject.definition_body(self)
@ -2822,8 +2808,7 @@ def InitUnforgeableProperties(descriptor, properties):
if descriptor.proxy:
unforgeableProperties = CGGeneric(
"// Unforgeable properties on proxy-based bindings are stored in an object held\n"
"// by the interface prototype object.\n"
"\n") # BOGUS extra blank line
"// by the interface prototype object.\n")
else:
unforgeableProperties = CGWrapper(
InitUnforgeablePropertiesOnObject(descriptor, "obj", properties, "nullptr"),
@ -2859,7 +2844,7 @@ def InitMemberSlots(descriptor, wrapperCache):
ClearWrapper.
"""
if not descriptor.interface.hasMembersInSlots():
return "\n" # BOGUS blank line only if this returns empty
return ""
if wrapperCache:
clearWrapper = " aCache->ClearWrapper();\n"
else:
@ -4221,8 +4206,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
declType = CGGeneric("nsRefPtr<%s>" % name)
else:
declType = CGGeneric("OwningNonNull<%s>" % name)
# BOGUS extra blank line here turns out to be at the end of a block:
conversion = indent(CGCallbackTempRoot(name).define()) + "\n"
conversion = indent(CGCallbackTempRoot(name).define())
template = wrapObjectTemplate(conversion, type,
"${declName} = nullptr;\n",
@ -4365,8 +4349,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
if (!${objRef}.Init(&$${val}.toObject())) {
$*{badType}
}
""", # BOGUS extra blank line
""",
objRef=objRef,
badType=onFailureBadType(failureCode, type.name).define())
template = wrapObjectTemplate(template, type, "${declName}.SetNull();\n",
@ -4455,8 +4438,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
$*{convert}
$*{assign}
}
""", # BOGUS extra newline
""",
convert=getConversionCode("str"),
assign=assignString),
declType=declType,
@ -4596,7 +4578,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
assert(isinstance(defaultValue, IDLNullValue))
haveObject = "${haveValue} && " + haveObject
template = CGIfElseWrapper(haveObject,
CGGeneric(conversion + "\n"), # BOGUS extra blank line
CGGeneric(conversion),
CGGeneric("${declName} = nullptr;\n")).define()
else:
template = conversion
@ -6359,9 +6341,8 @@ class CGMethodCall(CGThing):
requiredArgs = requiredArgCount(signature)
if requiredArgs > 0:
code = fill( # BOGUS extra blank line
code = fill(
"""
if (args.length() < ${requiredArgs}) {
return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${methodName}");
}
@ -6704,13 +6685,12 @@ class CGMethodCall(CGThing):
overloadCGThings.append(
CGSwitch("argcount",
argCountCases,
# BOGUS extra blank line at end of default block
CGGeneric('return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "%s");\n\n' %
CGGeneric('return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "%s");\n' %
methodName)))
overloadCGThings.append(
CGGeneric('MOZ_CRASH("We have an always-returning default case");\n'
'return false;\n'))
self.cgRoot = CGWrapper(CGList(overloadCGThings), pre="\n")
self.cgRoot = CGList(overloadCGThings)
def define(self):
return self.cgRoot.define()
@ -6727,6 +6707,10 @@ class CGGetterCall(CGPerSignatureCall):
getter=True)
class FakeIdentifier():
def __init__(self, name):
self.name = name
class FakeArgument():
"""
A class that quacks like an IDLArgument. This is used to make
@ -6746,10 +6730,7 @@ class FakeArgument():
self.enforceRange = False
self.clamp = False
class FakeIdentifier():
def __init__(self):
self.name = name
self.identifier = FakeIdentifier()
self.identifier = FakeIdentifier(name)
def allowTreatNonCallableAsNull(self):
return self._allowTreatNonCallableAsNull
@ -7859,9 +7840,8 @@ class CGEnum(CGThing):
return len(self.enum.values()) + 1
def declare(self):
decl = fill( # BOGUS extra newline at top
decl = fill(
"""
MOZ_BEGIN_ENUM_CLASS(${name}, uint32_t)
$*{enums}
EndGuard_
@ -7875,9 +7855,8 @@ class CGEnum(CGThing):
return decl + "\n" + strings.declare()
def define(self):
strings = fill( # BOGUS extra newline at top
strings = fill(
"""
extern const EnumEntry ${name}[${count}] = {
$*{entries}
{ nullptr, 0 }
@ -7887,8 +7866,6 @@ class CGEnum(CGThing):
count=self.nEnumStrings(),
entries=''.join('{"%s", %d},\n' % (val, len(val))
for val in self.enum.values()))
# BOGUS - CGNamespace automatically indents; the extra indent() below causes
# the output to be indented 4 spaces.
return CGNamespace(self.stringsNamespace(),
CGGeneric(define=indent(strings))).define()
@ -8266,12 +8243,10 @@ class CGUnionStruct(CGThing):
selfName = CGUnionStruct.unionTypeName(self.type, self.ownsMembers)
if self.ownsMembers:
if traceCases:
# BOGUS blank line in default case
traceBody = CGSwitch("mType", traceCases,
default=CGGeneric("\n")).define()
default=CGGeneric("")).define()
else:
# BOGUS blank line in method
traceBody = "\n"
traceBody = ""
methods.append(ClassMethod("TraceUnion", "void",
[Argument("JSTracer*", "trc")],
body=traceBody))
@ -8716,7 +8691,7 @@ class ClassDestructor(ClassItem):
""",
decorators=self.getDecorators(False),
className=cgClass.getNameString(),
body=self.getBody() or "\n") # BOGUS extra blank line if empty
body=self.getBody())
class ClassMember(ClassItem):
@ -8958,8 +8933,7 @@ class CGResolveOwnProperty(CGAbstractStaticMethod):
"bool", args)
def definition_body(self):
# BOGUS extra blank line at end of function
return "return js::GetProxyHandler(obj)->getOwnPropertyDescriptor(cx, wrapper, id, desc);\n\n"
return "return js::GetProxyHandler(obj)->getOwnPropertyDescriptor(cx, wrapper, id, desc);\n"
class CGResolveOwnPropertyViaNewresolve(CGAbstractBindingMethod):
@ -9017,8 +8991,7 @@ class CGEnumerateOwnProperties(CGAbstractStaticMethod):
"EnumerateOwnProperties", "bool", args)
def definition_body(self):
# BOGUS extra newline
return "return js::GetProxyHandler(obj)->getOwnPropertyNames(cx, wrapper, props);\n\n"
return "return js::GetProxyHandler(obj)->getOwnPropertyNames(cx, wrapper, props);\n"
class CGEnumerateOwnPropertiesViaGetOwnPropertyNames(CGAbstractBindingMethod):
@ -9525,11 +9498,10 @@ class CGDOMJSProxyHandler_defineProperty(ClassMethod):
" return false;\n"
"}\n"
"if (hasUnforgeable) {\n"
" *defined = true;" # SUPER BOGUS missing newline
" *defined = true;\n"
" bool unused;\n"
" return js_DefineOwnProperty(cx, ${holder}, id, desc, &unused);\n"
"}\n"
"\n") # BOGUS extra blank line at end of block or method
"}\n")
set += CallOnUnforgeableHolder(self.descriptor,
defineOnUnforgeable,
"xpc::WrapperFactory::IsXrayWrapper(proxy)")
@ -9546,8 +9518,7 @@ class CGDOMJSProxyHandler_defineProperty(ClassMethod):
$*{callSetter}
return true;
""", # BOGUS extra blank line at end of method
""",
callSetter=CGProxyNamedSetter(self.descriptor).define())
else:
if self.descriptor.supportsNamedProperties():
@ -9652,12 +9623,10 @@ class CGDOMJSProxyHandler_delete(ClassMethod):
delete += (namedBody +
"if (found) {\n"
" return true;\n"
"}\n\n") # BOGUS extra blank line
"}\n")
if not self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
delete = CGIfWrapper(CGGeneric(delete),
"!HasPropertyOnPrototype(cx, proxy, id)").define()
else:
delete += "\n" # BOGUS extra blank line
delete += dedent("""
@ -9790,8 +9759,7 @@ class CGDOMJSProxyHandler_hasOwn(ClassMethod):
"\n" +
"*bp = found;\n")
if not self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
# BOGUS extra blank line at end of block
named = CGIfWrapper(CGGeneric(named + "return true;\n\n"),
named = CGIfWrapper(CGGeneric(named + "return true;\n"),
"!HasPropertyOnPrototype(cx, proxy, id)").define()
named += "*bp = false;\n"
else:
@ -10475,7 +10443,29 @@ class CGNamespacedEnum(CGThing):
def define(self):
return ""
def initIdsClassMethod(identifiers, atomCacheName):
idinit = ['!atomsCache->%s.init(cx, "%s")' %
(CGDictionary.makeIdName(id),
id)
for id in identifiers]
idinit.reverse()
body = fill(
"""
MOZ_ASSERT(!*reinterpret_cast<jsid**>(atomsCache));
// Initialize these in reverse order so that any failure leaves the first one
// uninitialized.
if (${idinit}) {
return false;
}
return true;
""",
idinit=" ||\n ".join(idinit))
return ClassMethod("InitIds", "bool", [
Argument("JSContext*", "cx"),
Argument("%s*" % atomCacheName, "atomsCache")
], static=True, body=body, visibility="private")
class CGDictionary(CGThing):
def __init__(self, dictionary, descriptorProvider):
self.dictionary = dictionary
@ -10640,8 +10630,6 @@ class CGDictionary(CGThing):
if self.memberInfo:
body += "\n".join(self.getMemberDefinition(m).define()
for m in self.memberInfo)
else:
body += "\n" # BOGUS extra blank line
body += "\nreturn true;\n"
return ClassMethod("ToObject", "bool", [
@ -10651,29 +10639,8 @@ class CGDictionary(CGThing):
def initIdsMethod(self):
assert self.needToInitIds
idinit = ['!atomsCache->%s.init(cx, "%s")' %
(CGDictionary.makeIdName(m.identifier.name),
m.identifier.name)
for m in self.dictionary.members]
idinit.reverse()
body = fill(
"""
MOZ_ASSERT(!*reinterpret_cast<jsid**>(atomsCache));
// Initialize these in reverse order so that any failure leaves the first one
// uninitialized.
if (${idinit}) {
return false;
}
return true;
""",
idinit=" ||\n ".join(idinit))
return ClassMethod("InitIds", "bool", [
Argument("JSContext*", "cx"),
Argument("%sAtoms*" % self.makeClassName(self.dictionary),
"atomsCache"),
], static=True, body=body, visibility="private")
return initIdsClassMethod([m.identifier.name for m in self.dictionary.members],
"%sAtoms" % self.makeClassName(self.dictionary))
def traceDictionaryMethod(self):
body = ""
@ -10687,8 +10654,6 @@ class CGDictionary(CGThing):
if memberTraces:
body += "\n".join(memberTraces)
else:
body += "\n" # BOGUS extra newline
return ClassMethod("TraceDictionary", "void", [
Argument("JSTracer*", "trc"),
@ -10721,7 +10686,7 @@ class CGDictionary(CGThing):
"operator=", "void",
[Argument("const %s&" % self.makeClassName(self.dictionary),
"aOther")],
body=body.define() or "\n") # BOGUS blank line when empty
body=body.define())
def getStructs(self):
d = self.dictionary
@ -11217,6 +11182,9 @@ class CGForwardDeclarations(CGWrapper):
# We just about always need NativePropertyHooks
builder.addInMozillaDom("NativePropertyHooks")
builder.addInMozillaDom("ProtoAndIfaceCache")
# Add the atoms cache type, even if we don't need it.
for d in descriptors:
builder.add(d.nativeType + "Atoms", isStruct=True)
for callback in mainCallbacks:
forwardDeclareForType(callback)
@ -11230,6 +11198,7 @@ class CGForwardDeclarations(CGWrapper):
for d in callbackInterfaces:
builder.add(d.nativeType)
builder.add(d.nativeType + "Atoms", isStruct=True)
for t in getTypesFromDescriptor(d):
forwardDeclareForType(t)
@ -11283,7 +11252,7 @@ class CGBindingRoot(CGThing):
dictionaries = config.getDictionaries(webIDLFile=webIDLFile)
bindingHeaders["nsCxPusher.h"] = dictionaries
bindingHeaders["AtomList.h"] = any(
hasNonEmptyDictionaries = any(
len(dict.members) > 0 for dict in dictionaries)
mainCallbacks = config.getCallbacks(webIDLFile=webIDLFile,
workers=False)
@ -11294,6 +11263,7 @@ class CGBindingRoot(CGThing):
jsImplemented = config.getDescriptors(webIDLFile=webIDLFile,
isJSImplemented=True)
bindingHeaders["nsPIDOMWindow.h"] = jsImplemented
bindingHeaders["AtomList.h"] = hasNonEmptyDictionaries or jsImplemented or callbackDescriptors
def addHeaderBasedOnTypes(header, typeChecker):
bindingHeaders[header] = (
@ -12569,7 +12539,7 @@ class CGCallback(CGClass):
# cheat and have CallbackMember compute all those things for us.
realMethods = []
for method in methods:
if not method.needThisHandling:
if not isinstance(method, CallbackMember) or not method.needThisHandling:
realMethods.append(method)
else:
realMethods.extend(self.getMethodImpls(method))
@ -12709,13 +12679,21 @@ class CGCallbackInterface(CGCallback):
if len(sigs) != 1:
raise TypeError("We only handle one constructor. See bug 869268.")
methods.append(CGJSImplInitOperation(sigs[0], descriptor))
if any(m.isAttr() or m.isMethod() for m in iface.members) or (iface.isJSImplemented() and iface.ctor()):
methods.append(initIdsClassMethod([descriptor.binaryNameFor(m.identifier.name)
for m in iface.members
if m.isAttr() or m.isMethod()] +
(["__init"] if iface.isJSImplemented() and iface.ctor() else []),
iface.identifier.name + "Atoms"))
CGCallback.__init__(self, iface, descriptor, "CallbackInterface",
methods, getters=getters, setters=setters)
class FakeMember():
def __init__(self):
def __init__(self, name=None):
self.treatNullAs = "Default"
if name is not None:
self.identifier = FakeIdentifier(name)
def isStatic(self):
return False
@ -12826,10 +12804,6 @@ class CallbackMember(CGNativeMember):
self.getRetvalInfo(self.retvalType,
False)[2]).substitute(replacements)
type = convertType.define()
if type == "":
type = "\n" # BOGUS extra blank line
if assignRetval == "":
assignRetval = "\n" # BOGUS extra blank line
return type + assignRetval
def getArgConversions(self):
@ -12839,7 +12813,7 @@ class CallbackMember(CGNativeMember):
argConversions = [self.getArgConversion(i, arg)
for i, arg in enumerate(self.originalSig[1])]
if not argConversions:
return "\n\n" # BOGUS extra blank line
return "\n"
# Do them back to front, so our argc modifications will work
# correctly, because we examine trailing arguments first.
@ -13072,12 +13046,15 @@ class CallbackOperationBase(CallbackMethod):
def getCallableDecl(self):
getCallableFromProp = fill(
"""
if (!GetCallableProperty(cx, "${methodName}", &callable)) {
${atomCacheName}* atomsCache = GetAtomCache<${atomCacheName}>(cx);
if ((!*reinterpret_cast<jsid**>(atomsCache) && !InitIds(cx, atomsCache)) ||
!GetCallableProperty(cx, atomsCache->${methodAtomName}, &callable)) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return${errorReturn};
}
""",
methodName=self.methodName,
methodAtomName=CGDictionary.makeIdName(self.methodName),
atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms",
errorReturn=self.getDefaultRetval())
if not self.singleOperation:
return 'JS::Rooted<JS::Value> callable(cx);\n' + getCallableFromProp
@ -13146,12 +13123,15 @@ class CallbackGetter(CallbackAccessor):
return fill(
"""
JS::Rooted<JSObject *> callback(cx, mCallback);
if (!JS_GetProperty(cx, callback, "${attrName}", &rval)) {
${atomCacheName}* atomsCache = GetAtomCache<${atomCacheName}>(cx);
if ((!*reinterpret_cast<jsid**>(atomsCache) && !InitIds(cx, atomsCache)) ||
!JS_GetPropertyById(cx, callback, atomsCache->${attrAtomName}, &rval)) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return${errorReturn};
}
""",
attrName=self.descriptorProvider.binaryNameFor(self.attrName),
atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms",
attrAtomName=CGDictionary.makeIdName(self.descriptorProvider.binaryNameFor(self.attrName)),
errorReturn=self.getDefaultRetval())
@ -13171,12 +13151,15 @@ class CallbackSetter(CallbackAccessor):
return fill(
"""
MOZ_ASSERT(argv.length() == 1);
if (!JS_SetProperty(cx, CallbackPreserveColor(), "${attrName}", argv[0])) {
${atomCacheName}* atomsCache = GetAtomCache<${atomCacheName}>(cx);
if ((!*reinterpret_cast<jsid**>(atomsCache) && !InitIds(cx, atomsCache)) ||
!JS_SetPropertyById(cx, CallbackPreserveColor(), atomsCache->${attrAtomName}, argv[0])) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return${errorReturn};
}
""",
attrName=self.descriptorProvider.binaryNameFor(self.attrName),
atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms",
attrAtomName=CGDictionary.makeIdName(self.descriptorProvider.binaryNameFor(self.attrName)),
errorReturn=self.getDefaultRetval())
def getArgcDecl(self):
@ -13211,21 +13194,38 @@ class GlobalGenRoots():
structs = []
def memberToAtomCacheMember(binaryNameFor, m):
binaryMemberName = binaryNameFor(m.identifier.name)
return ClassMember(CGDictionary.makeIdName(binaryMemberName),
"InternedStringId", visibility="public")
def buildAtomCacheStructure(idlobj, binaryNameFor, members):
classMembers = [memberToAtomCacheMember(binaryNameFor, m)
for m in members]
structName = idlobj.identifier.name + "Atoms"
return (structName,
CGWrapper(CGClass(structName,
bases=None,
isStruct=True,
members=classMembers), post='\n'))
for dict in dictionaries:
dictMembers = dict.members
if len(dictMembers) == 0:
if len(dict.members) == 0:
continue
classMembers = [ClassMember(CGDictionary.makeIdName(m.identifier.name),
"InternedStringId",
visibility="public") for m in dictMembers]
structs.append(buildAtomCacheStructure(dict, lambda x: x, dict.members))
structName = dict.identifier.name + "Atoms"
structs.append((structName,
CGWrapper(CGClass(structName,
bases=None,
isStruct=True,
members=classMembers), post='\n')))
for d in (config.getDescriptors(isJSImplemented=True) +
config.getDescriptors(isCallback=True)):
members = [m for m in d.interface.members if m.isAttr() or m.isMethod()]
if d.interface.isJSImplemented() and d.interface.ctor():
# We'll have an __init() method.
members.append(FakeMember('__init'))
if len(members) == 0:
continue
structs.append(buildAtomCacheStructure(d.interface,
lambda x: d.binaryNameFor(x),
members))
structs.sort()
generatedStructs = [struct for structName, struct in structs]

View File

@ -43,6 +43,11 @@ public:
init(aValue);
}
void operator=(T& aValue)
{
init(&aValue);
}
void operator=(const already_AddRefed<T>& aValue)
{
init(aValue);

View File

@ -3436,14 +3436,13 @@ BluetoothDBusService::ToggleCalls(BluetoothReplyRunnable* aRunnable)
class OnUpdateSdpRecordsRunnable : public nsRunnable
{
public:
OnUpdateSdpRecordsRunnable(const nsAString& aObjectPath,
OnUpdateSdpRecordsRunnable(const nsAString& aDeviceAddress,
BluetoothProfileManagerBase* aManager)
: mManager(aManager)
: mDeviceAddress(aDeviceAddress)
, mManager(aManager)
{
MOZ_ASSERT(!aObjectPath.IsEmpty());
MOZ_ASSERT(!aDeviceAddress.IsEmpty());
MOZ_ASSERT(aManager);
mDeviceAddress = GetAddressFromObjectPath(aObjectPath);
}
nsresult
@ -3456,6 +3455,12 @@ public:
return NS_OK;
}
void
GetDeviceAddress(nsAString& aRetDeviceAddress)
{
aRetDeviceAddress = mDeviceAddress;
}
private:
nsString mDeviceAddress;
BluetoothProfileManagerBase* mManager;
@ -3641,31 +3646,74 @@ public:
MOZ_ASSERT(sDBusConnection);
MOZ_ASSERT(!sAdapterPath.IsEmpty());
const nsString objectPath =
GetObjectPathFromAddress(sAdapterPath, mDeviceAddress);
// We first guess that the device doesn't exist at all. So we use BlueZ
// API "CreateDevice" to create an object path for the BluetoothDevice
// object. "CreateDevice" will connect to the remote device and retrieve
// SDP records of the target.
NS_ConvertUTF16toUTF8 address(mDeviceAddress);
const char* cAddress = address.get();
// I choose to use raw pointer here because this is going to be passed as an
// argument into SendWithReply() at once.
OnUpdateSdpRecordsRunnable* callbackRunnable =
new OnUpdateSdpRecordsRunnable(objectPath, mBluetoothProfileManager);
new OnUpdateSdpRecordsRunnable(mDeviceAddress, mBluetoothProfileManager);
sDBusConnection->SendWithReply(DiscoverServicesCallback,
(void*)callbackRunnable, -1,
BLUEZ_DBUS_BASE_IFC,
NS_ConvertUTF16toUTF8(objectPath).get(),
DBUS_DEVICE_IFACE,
"DiscoverServices",
DBUS_TYPE_STRING, &EmptyCString(),
DBUS_TYPE_INVALID);
sDBusConnection->SendWithReply(
CreateDeviceCallback, callbackRunnable, -1,
BLUEZ_DBUS_BASE_IFC,
NS_ConvertUTF16toUTF8(sAdapterPath).get(),
DBUS_ADAPTER_IFACE,
"CreateDevice",
DBUS_TYPE_STRING, &cAddress,
DBUS_TYPE_INVALID);
}
protected:
static void CreateDeviceCallback(DBusMessage* aMsg, void* aData)
{
MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
nsAutoString errorString;
OnUpdateSdpRecordsRunnable* r =
static_cast<OnUpdateSdpRecordsRunnable*>(aData);
if (IsDBusMessageError(aMsg, nullptr, errorString)) {
// If the device already exists it comes here. If we want to refresh its
// SDP records then we have to do "DiscoverServices"
BT_LOGR("%s", NS_ConvertUTF16toUTF8(errorString).get());
nsString deviceAddress;
r->GetDeviceAddress(deviceAddress);
const nsString objectPath =
GetObjectPathFromAddress(sAdapterPath, deviceAddress);
sDBusConnection->SendWithReply(DiscoverServicesCallback,
aData, -1,
BLUEZ_DBUS_BASE_IFC,
NS_ConvertUTF16toUTF8(objectPath).get(),
DBUS_DEVICE_IFACE,
"DiscoverServices",
DBUS_TYPE_STRING, &EmptyCString(),
DBUS_TYPE_INVALID);
return;
}
NS_DispatchToMainThread(r);
}
static void DiscoverServicesCallback(DBusMessage* aMsg, void* aData)
{
MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
nsRefPtr<OnUpdateSdpRecordsRunnable> r(
static_cast<OnUpdateSdpRecordsRunnable*>(aData));
nsAutoString errorStr;
if (IsDBusMessageError(aMsg, nullptr, errorStr)) {
BT_LOGR("%s", NS_ConvertUTF16toUTF8(errorStr).get());
}
OnUpdateSdpRecordsRunnable* r =
static_cast<OnUpdateSdpRecordsRunnable*>(aData);
NS_DispatchToMainThread(r);
}
@ -3680,8 +3728,7 @@ BluetoothDBusService::UpdateSdpRecords(const nsAString& aDeviceAddress,
{
MOZ_ASSERT(NS_IsMainThread());
Task* task = new UpdateSdpRecordsTask(aDeviceAddress, aManager);
DispatchToDBusThread(task);
DispatchToDBusThread(new UpdateSdpRecordsTask(aDeviceAddress, aManager));
return true;
}

View File

@ -1,6 +1,4 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=2 sts=2 et filetype=javascript
* This Source Code Form is subject to the terms of the Mozilla Public
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -36,7 +34,14 @@ const BDADDR_ALL = "ff:ff:ff:ff:ff:ff";
const BDADDR_LOCAL = "ff:ff:ff:00:00:00";
// A user friendly name for remote BT device.
const REMOTE_DEVICE_NAME = "Remote BT Device";
const REMOTE_DEVICE_NAME = "Remote_BT_Device";
// A system message signature of pairing request event
const BT_PAIRING_REQ = "bluetooth-pairing-request";
// Passkey and pincode used to reply pairing requst
const BT_PAIRING_PASSKEY = 123456;
const BT_PAIRING_PINCODE = "ABCDEFG";
let Promise =
SpecialPowers.Cu.import("resource://gre/modules/Promise.jsm").Promise;
@ -80,6 +85,33 @@ function runEmulatorCmdSafe(aCommand) {
return deferred.promise;
}
/**
* Wrap DOMRequest onsuccess/onerror events to Promise resolve/reject.
*
* Fulfill params: A DOMEvent.
* Reject params: A DOMEvent.
*
* @param aRequest
* A DOMRequest instance.
*
* @return A deferred promise.
*/
function wrapDomRequestAsPromise(aRequest) {
let deferred = Promise.defer();
ok(aRequest instanceof DOMRequest,
"aRequest is instanceof " + aRequest.constructor);
aRequest.onsuccess = function(aEvent) {
deferred.resolve(aEvent);
};
aRequest.onerror = function(aEvent) {
deferred.reject(aEvent);
};
return deferred.promise;
}
/**
* Add a Bluetooth remote device to scatternet and set its properties.
*
@ -188,13 +220,11 @@ function setEmulatorDeviceProperty(aAddress, aPropertyName, aValue) {
function getEmulatorDeviceProperty(aAddress, aPropertyName) {
let cmd = "bt property " + aAddress + " " + aPropertyName;
return runEmulatorCmdSafe(cmd)
.then(function(aResults) {
return aResults[0];
});
.then(aResults => aResults[0]);
}
/**
* Start dicovering Bluetooth devices.
* Start discovering Bluetooth devices.
*
* Allows the device's adapter to start seeking for remote devices.
*
@ -202,32 +232,28 @@ function getEmulatorDeviceProperty(aAddress, aPropertyName) {
* Reject params: a DOMError
*
* @param aAdapter
* A BluetoothAdapter which is used to interact with local BT dev
* A BluetoothAdapter which is used to interact with local BT device.
*
* @return A deferred promise.
*/
function startDiscovery(aAdapter) {
let deferred = Promise.defer();
let request = aAdapter.startDiscovery();
request.onsuccess = function () {
log(" Start discovery - Success");
// TODO (bug 892207): Make Bluetooth APIs available for 3rd party apps.
// Currently, discovering state wouldn't change immediately here.
// We would turn on this check when the redesigned API are landed.
// is(aAdapter.discovering, true, "BluetoothAdapter.discovering");
deferred.resolve();
}
request.onerror = function (aEvent) {
ok(false, "Start discovery - Fail");
deferred.reject(aEvent.target.error);
}
return deferred.promise;
return wrapDomRequestAsPromise(request)
.then(function resolve() {
// TODO (bug 892207): Make Bluetooth APIs available for 3rd party apps.
// Currently, discovering state wouldn't change immediately here.
// We would turn on this check when the redesigned API are landed.
// is(aAdapter.discovering, false, "BluetoothAdapter.discovering");
log(" Start discovery - Success");
}, function reject(aEvent) {
ok(false, "Start discovery - Fail");
throw aEvent.target.error;
});
}
/**
* Stop dicovering Bluetooth devices.
* Stop discovering Bluetooth devices.
*
* Allows the device's adapter to stop seeking for remote devices.
*
@ -240,24 +266,184 @@ function startDiscovery(aAdapter) {
* @return A deferred promise.
*/
function stopDiscovery(aAdapter) {
let request = aAdapter.stopDiscovery();
return wrapDomRequestAsPromise(request)
.then(function resolve() {
// TODO (bug 892207): Make Bluetooth APIs available for 3rd party apps.
// Currently, discovering state wouldn't change immediately here.
// We would turn on this check when the redesigned API are landed.
// is(aAdapter.discovering, false, "BluetoothAdapter.discovering");
log(" Stop discovery - Success");
}, function reject(aEvent) {
ok(false, "Stop discovery - Fail");
throw aEvent.target.error;
});
}
/**
* Wait for 'devicefound' event of specified devices.
*
* Resolve if that every specified devices has been found. Never reject.
*
* Fulfill params: an array which contains addresses of remote devices.
*
* @param aAdapter
* A BluetoothAdapter which is used to interact with local BT device.
* @param aRemoteAddresses
* An array which contains addresses of remote devices.
*
* @return A deferred promise.
*/
function waitForDevicesFound(aAdapter, aRemoteAddresses) {
let deferred = Promise.defer();
let request = aAdapter.stopDiscovery();
request.onsuccess = function () {
log(" Stop discovery - Success");
// TODO (bug 892207): Make Bluetooth APIs available for 3rd party apps.
// Currently, discovering state wouldn't change immediately here.
// We would turn on this check when the redesigned API are landed.
// is(aAdapter.discovering, false, "BluetoothAdapter.discovering");
deferred.resolve();
}
request.onerror = function (aEvent) {
ok(false, "Stop discovery - Fail");
deferred.reject(aEvent.target.error);
}
var addrArray = [];
aAdapter.addEventListener("devicefound", function onevent(aEvent) {
if(aRemoteAddresses.indexOf(aEvent.device.address) != -1) {
addrArray.push(aEvent.device.address);
}
if(addrArray.length == aRemoteAddresses.length) {
aAdapter.removeEventListener("devicefound", onevent);
ok(true, "BluetoothAdapter has found all remote devices.");
deferred.resolve(addrArray);
}
});
return deferred.promise;
}
/**
* Start discovering Bluetooth devices and wait for 'devicefound' events.
*
* Allows the device's adapter to start seeking for remote devices and wait for
* the 'devicefound' events of specified devices.
*
* Fulfill params: an array of addresses of found devices.
*
* @param aAdapter
* A BluetoothAdapter which is used to interact with local BT device.
* @param aRemoteAddresses
* An array which contains addresses of remote devices.
*
* @return A deferred promise.
*/
function startDiscoveryAndWaitDevicesFound(aAdapter, aRemoteAddresses) {
let promises = [];
promises.push(waitForDevicesFound(aAdapter, aRemoteAddresses));
promises.push(startDiscovery(aAdapter));
return Promise.all(promises)
.then(aResults => aResults[0]);
}
/**
* Start pairing a remote device.
*
* Start pairing a remote device with the device's adapter.
*
* Fulfill params: (none)
* Reject params: a DOMError
*
* @param aAdapter
* A BluetoothAdapter which is used to interact with local BT device.
* @param aDeviceAddress
* The string of remote Bluetooth address with format xx:xx:xx:xx:xx:xx.
*
* @return A deferred promise.
*/
function pair(aAdapter, aDeviceAddress) {
let request = aAdapter.pair(aDeviceAddress);
return wrapDomRequestAsPromise(request)
.then(function resolve() {
log(" Pair - Success");
}, function reject(aEvent) {
ok(false, "Pair - Fail");
throw aEvent.target.error;
});
}
/**
* Remove the paired device from the paired device list.
*
* Remove the paired device from the paired device list of the device's adapter.
*
* Fulfill params: (none)
* Reject params: a DOMError
*
* @param aAdapter
* A BluetoothAdapter which is used to interact with local BT device.
* @param aDeviceAddress
* The string of remote Bluetooth address with format xx:xx:xx:xx:xx:xx.
*
* @return A deferred promise.
*/
function unpair(aAdapter, aDeviceAddress) {
let request = aAdapter.unpair(aDeviceAddress);
return wrapDomRequestAsPromise(request)
.then(function resolve() {
log(" Unpair - Success");
}, function reject(aEvent) {
ok(false, "Unpair - Fail");
throw aEvent.target.error;
});
}
/**
* Start pairing a remote device and wait for "pairedstatuschanged" event.
*
* Start pairing a remote device with the device's adapter and wait for
* "pairedstatuschanged" event.
*
* Fulfill params: an array of promise results contains the fulfilled params of
* 'waitForAdapterEvent' and 'pair'.
*
* @param aAdapter
* A BluetoothAdapter which is used to interact with local BT device.
* @param aDeviceAddress
* The string of remote Bluetooth address with format xx:xx:xx:xx:xx:xx.
*
* @return A deferred promise.
*/
function pairDeviceAndWait(aAdapter, aDeviceAddress) {
let promises = [];
promises.push(waitForAdapterEvent(aAdapter, "pairedstatuschanged"));
promises.push(pair(aAdapter, aDeviceAddress));
return Promise.all(promises);
}
/**
* Get paried Bluetooth devices.
*
* The getPairedDevices method is used to retrieve the full list of all devices
* paired with the device's adapter.
*
* Fulfill params: a shallow copy of the Array of paired BluetoothDevice
* objects.
* Reject params: a DOMError
*
* @param aAdapter
* A BluetoothAdapter which is used to interact with local BT device.
*
* @return A deferred promise.
*/
function getPairedDevices(aAdapter) {
let request = aAdapter.getPairedDevices();
return wrapDomRequestAsPromise(request)
.then(function resolve() {
log(" getPairedDevices - Success");
let pairedDevices = request.result.slice();
return pairedDevices;
}, function reject(aEvent) {
ok(false, "getPairedDevices - Fail");
throw aEvent.target.error;
});
}
/**
* Get mozSettings value specified by @aKey.
*
@ -274,19 +460,16 @@ function stopDiscovery(aAdapter) {
* @return A deferred promise.
*/
function getSettings(aKey) {
let deferred = Promise.defer();
let request = navigator.mozSettings.createLock().get(aKey);
request.addEventListener("success", function(aEvent) {
ok(true, "getSettings(" + aKey + ")");
deferred.resolve(aEvent.target.result[aKey]);
});
request.addEventListener("error", function() {
ok(false, "getSettings(" + aKey + ")");
deferred.reject();
});
return deferred.promise;
return wrapDomRequestAsPromise(request)
.then(function resolve(aEvent) {
ok(true, "getSettings(" + aKey + ")");
return aEvent.target.result[aKey];
}, function reject(aEvent) {
ok(false, "getSettings(" + aKey + ")");
throw aEvent.target.error;
});
}
/**
@ -303,19 +486,15 @@ function getSettings(aKey) {
* @return A deferred promise.
*/
function setSettings(aSettings) {
let deferred = Promise.defer();
let request = navigator.mozSettings.createLock().set(aSettings);
request.addEventListener("success", function() {
ok(true, "setSettings(" + JSON.stringify(aSettings) + ")");
deferred.resolve();
});
request.addEventListener("error", function() {
ok(false, "setSettings(" + JSON.stringify(aSettings) + ")");
deferred.reject();
});
return deferred.promise;
return wrapDomRequestAsPromise(request)
.then(function resolve() {
ok(true, "setSettings(" + JSON.stringify(aSettings) + ")");
}, function reject(aEvent) {
ok(false, "setSettings(" + JSON.stringify(aSettings) + ")");
throw aEvent.target.error;
});
}
/**
@ -438,7 +617,7 @@ function waitForManagerEvent(aEventName) {
* Fulfill params: the DOMEvent passed.
*
* @param aAdapter
* The BluetoothAdapter you want to use.
* A BluetoothAdapter which is used to interact with local BT device.
* @param aEventName
* The name of the EventHandler.
*
@ -463,7 +642,8 @@ function waitForAdapterEvent(aAdapter, aEventName) {
*
* Resolve if that named event occurs. Reject if we can't set settings.
*
* Fulfill params: the DOMEvent passed.
* Fulfill params: an array of promise results contains the fulfill params of
* 'waitForManagerEvent' and 'setBluetoothEnabled'.
* Reject params: (none)
*
* @return A deferred promise.

View File

@ -3,8 +3,10 @@ b2g = true
browser = false
qemu = true
[test_navigate_to_default_url.py]
[test_dom_BluetoothManager_enabled.js]
[test_dom_BluetoothManager_adapteradded.js]
[test_dom_BluetoothAdapter_setters.js]
[test_dom_BluetoothAdapter_getters.js]
[test_dom_BluetoothAdapter_discovery.js]
[test_dom_BluetoothAdapter_pair.js]

View File

@ -1,13 +1,11 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=2 sts=2 et filetype=javascript
* This Source Code Form is subject to the terms of the Mozilla Public
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
///////////////////////////////////////////////////////////////////////////////
// Test Purpose:
// To verify that discovery process of BluetoothAdapter is correct.
// Use B2G emulator commands to add/remote remote devices to simulate
// Use B2G emulator commands to add/remove remote devices to simulate
// discovering behavior.
//
// Test Coverage:
@ -24,15 +22,9 @@ MARIONETTE_HEAD_JS = 'head.js';
startBluetoothTest(true, function testCaseMain(aAdapter) {
log("Testing the discovery process of BluetoothAdapter ...");
// The properties of remote device.
let theProperties = {
"name": REMOTE_DEVICE_NAME,
"discoverable": true
};
return Promise.resolve()
.then(() => removeEmulatorRemoteDevice(BDADDR_ALL))
.then(() => addEmulatorRemoteDevice(/*theProperties*/ null))
.then(() => addEmulatorRemoteDevice(null))
.then(function(aRemoteAddress) {
let promises = [];
promises.push(waitForAdapterEvent(aAdapter, "devicefound"));

View File

@ -1,6 +1,4 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=2 sts=2 et filetype=javascript
* This Source Code Form is subject to the terms of the Mozilla Public
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

View File

@ -0,0 +1,66 @@
/* 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 pairing process of BluetoothAdapter is correct.
// Use B2G emulator commands to add/remove remote devices to simulate
// discovering behavior. With current emulator implemation, the pair method
// between adapter and remote device would be 'confirmation'.
//
// Test Coverage:
// - BluetoothAdapter.startDiscovery()
// - BluetoothAdapter.stopDiscovery()
// - BluetoothAdapter.pair()
// - BluetoothAdapter.unpair()
// - BluetoothAdapter.onpairedstatuschanged()
// - BluetoothAdapter.setPairingConfirmation()
//
///////////////////////////////////////////////////////////////////////////////
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
function replyPairingReq(aAdapter, aPairingEvent) {
switch (aPairingEvent.method) {
case 'confirmation':
log("The pairing passkey is " + aPairingEvent.passkey);
aAdapter.setPairingConfirmation(aPairingEvent.address, true);
break;
case 'pincode':
let pincode = BT_PAIRING_PINCODE;
aAdapter.setPinCode(aPairingEvent.address, pincode);
break;
case 'passkey':
let passkey = BT_PAIRING_PASSKEY;
aAdapter.setPasskey(aPairingEvent.address, passkey);
break;
default:
ok(false, "Unsupported pairing method. [" + aPairingEvent.method + "]");
}
}
startBluetoothTest(true, function testCaseMain(aAdapter) {
log("Testing the pairing process of BluetoothAdapter ...");
// listens to the system message BT_PAIRING_REQ
navigator.mozSetMessageHandler(BT_PAIRING_REQ,
(evt) => replyPairingReq(aAdapter, evt));
return Promise.resolve()
.then(() => removeEmulatorRemoteDevice(BDADDR_ALL))
.then(() => addEmulatorRemoteDevice())
.then((aRemoteAddress) =>
startDiscoveryAndWaitDevicesFound(aAdapter, [aRemoteAddress]))
.then((aRemoteAddresses) =>
stopDiscovery(aAdapter).then(() => aRemoteAddresses))
// 'aRemoteAddresses' is an arrary which contains addresses of discovered
// remote devices.
// Pairs with the first device in 'aRemoteAddresses' to test the functionality
// of BluetoothAdapter.pair and BluetoothAdapter.onpairedstatuschanged.
.then((aRemoteAddresses) => pairDeviceAndWait(aAdapter, aRemoteAddresses.pop()))
.then(() => getPairedDevices(aAdapter))
.then((aPairedDevices) => unpair(aAdapter, aPairedDevices.pop().address))
.then(() => removeEmulatorRemoteDevice(BDADDR_ALL));
});

View File

@ -1,6 +1,4 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=2 sts=2 et filetype=javascript
* This Source Code Form is subject to the terms of the Mozilla Public
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

View File

@ -1,6 +1,4 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=2 sts=2 et filetype=javascript
* This Source Code Form is subject to the terms of the Mozilla Public
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

View File

@ -1,6 +1,4 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=2 sts=2 et filetype=javascript
* This Source Code Form is subject to the terms of the Mozilla Public
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

View File

@ -0,0 +1,12 @@
# 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/.
from marionette_test import MarionetteTestCase
class testNavigateToDefault(MarionetteTestCase):
def test_navigate_to_default_url(self):
try:
self.marionette.navigate("app://system.gaiamobile.org/index.html")
except:
self.assertTrue(Flase, "Can not navigate to system app.")

View File

@ -200,7 +200,11 @@ TabParent* sEventCapturer;
TabParent *TabParent::mIMETabParent = nullptr;
NS_IMPL_ISUPPORTS(TabParent, nsITabParent, nsIAuthPromptProvider, nsISecureBrowserUI)
NS_IMPL_ISUPPORTS(TabParent,
nsITabParent,
nsIAuthPromptProvider,
nsISecureBrowserUI,
nsISupportsWeakReference)
TabParent::TabParent(ContentParent* aManager, const TabContext& aContext, uint32_t aChromeFlags)
: TabContext(aContext)

View File

@ -17,6 +17,7 @@
#include "nsISecureBrowserUI.h"
#include "nsITabParent.h"
#include "nsIXULBrowserWindow.h"
#include "nsWeakReference.h"
#include "Units.h"
#include "js/TypeDecls.h"
@ -50,6 +51,7 @@ class TabParent : public PBrowserParent
, public nsITabParent
, public nsIAuthPromptProvider
, public nsISecureBrowserUI
, public nsSupportsWeakReference
, public TabContext
{
typedef mozilla::dom::ClonedMessageData ClonedMessageData;

View File

@ -17,10 +17,11 @@
#include "nsIWindowCreator.idl"
interface nsITabParent;
interface nsIURI;
interface nsIWebBrowserChrome;
[scriptable, uuid(f673ec81-a4b0-11d6-964b-eb5a2bf216fc)]
[scriptable, uuid(e28f810b-9b49-4927-a4be-62a74fadfe21)]
interface nsIWindowCreator2 : nsIWindowCreator {
@ -44,6 +45,8 @@ interface nsIWindowCreator2 : nsIWindowCreator {
may use the URL to help determine what sort of window
to open or whether to cancel window creation. It will not
load the URL.
@param aOpeningTab The TabParent that is trying to open this new chrome
window. Can be nullptr.
@param cancel Return |true| to reject window creation. If true the
implementation has determined the window should not
be created at all. The caller should not default
@ -54,5 +57,6 @@ interface nsIWindowCreator2 : nsIWindowCreator {
in uint32_t chromeFlags,
in uint32_t contextFlags,
in nsIURI uri,
in nsITabParent aOpeningTab,
out boolean cancel);
};

View File

@ -15,8 +15,9 @@ interface nsISimpleEnumerator;
interface nsIWebBrowserChrome;
interface nsIDocShellTreeItem;
interface nsIArray;
interface nsITabParent;
[uuid(00788A84-152F-4BD8-A814-FD8EB545DB29)]
[uuid(0f2d9d75-c46b-4114-802e-83b4655e61d2)]
interface nsPIWindowWatcher : nsISupports
{
@ -51,6 +52,9 @@ interface nsPIWindowWatcher : nsISupports
@param aDialog use dialog defaults (see nsIDOMWindow::openDialog)
@param aNavigate true if we should navigate the new window to the
specified URL.
@param aOpeningTab the nsITabParent that is opening the new window. The
nsITabParent is a remote tab belonging to aParent. Can
be nullptr if this window is not being opened from a tab.
@param aArgs Window argument
@return the new window
@ -65,7 +69,8 @@ interface nsPIWindowWatcher : nsISupports
nsIDOMWindow openWindow2(in nsIDOMWindow aParent, in string aUrl,
in string aName, in string aFeatures,
in boolean aCalledFromScript, in boolean aDialog,
in boolean aNavigate, in nsISupports aArgs);
in boolean aNavigate, in nsITabParent aOpeningTab,
in nsISupports aArgs);
/**
* Find a named docshell tree item amongst all windows registered

View File

@ -342,7 +342,7 @@ nsWindowWatcher::OpenWindow(nsIDOMWindow *aParent,
return OpenWindowInternal(aParent, aUrl, aName, aFeatures,
/* calledFromJS = */ false, dialog,
/* navigate = */ true, argv, _retval);
/* navigate = */ true, nullptr, argv, _retval);
}
struct SizeSpec {
@ -394,6 +394,7 @@ nsWindowWatcher::OpenWindow2(nsIDOMWindow *aParent,
bool aCalledFromScript,
bool aDialog,
bool aNavigate,
nsITabParent *aOpeningTab,
nsISupports *aArguments,
nsIDOMWindow **_retval)
{
@ -414,7 +415,7 @@ nsWindowWatcher::OpenWindow2(nsIDOMWindow *aParent,
return OpenWindowInternal(aParent, aUrl, aName, aFeatures,
aCalledFromScript, dialog,
aNavigate, argv, _retval);
aNavigate, aOpeningTab, argv, _retval);
}
nsresult
@ -425,6 +426,7 @@ nsWindowWatcher::OpenWindowInternal(nsIDOMWindow *aParent,
bool aCalledFromJS,
bool aDialog,
bool aNavigate,
nsITabParent *aOpeningTab,
nsIArray *argv,
nsIDOMWindow **_retval)
{
@ -698,7 +700,7 @@ nsWindowWatcher::OpenWindowInternal(nsIDOMWindow *aParent,
bool cancel = false;
rv = windowCreator2->CreateChromeWindow2(parentChrome, chromeFlags,
contextFlags, uriToLoad,
&cancel,
aOpeningTab, &cancel,
getter_AddRefs(newChrome));
if (NS_SUCCEEDED(rv) && cancel) {
newChrome = 0; // just in case

View File

@ -15,6 +15,7 @@
#include "nsIWindowCreator.h" // for stupid compilers
#include "nsIWindowWatcher.h"
#include "nsIPromptFactory.h"
#include "nsITabParent.h"
#include "nsPIWindowWatcher.h"
#include "nsTArray.h"
#include "js/TypeDecls.h"
@ -77,6 +78,7 @@ protected:
bool aCalledFromJS,
bool aDialog,
bool aNavigate,
nsITabParent *aOpeningTab,
nsIArray *argv,
nsIDOMWindow **_retval);

View File

@ -11,6 +11,7 @@
#include "ScaledFontBase.h"
#include "BorrowedContext.h"
#include "FilterNodeSoftware.h"
#include "mozilla/Scoped.h"
#include "cairo.h"
#include "cairo-tee.h"
@ -27,6 +28,7 @@
#ifdef CAIRO_HAS_XLIB_SURFACE
#include "cairo-xlib.h"
#include "cairo-xlib-xrender.h"
#endif
#ifdef CAIRO_HAS_WIN32_SURFACE
@ -36,6 +38,9 @@
#include <algorithm>
namespace mozilla {
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedCairoSurface, cairo_surface_t, cairo_surface_destroy);
namespace gfx {
cairo_surface_t *DrawTargetCairo::mDummySurface;
@ -1057,9 +1062,106 @@ DrawTargetCairo::CreateSourceSurfaceFromData(unsigned char *aData,
return source_surf;
}
#ifdef CAIRO_HAS_XLIB_SURFACE
static cairo_user_data_key_t gDestroyPixmapKey;
struct DestroyPixmapClosure {
DestroyPixmapClosure(Drawable d, Screen *s) : mPixmap(d), mScreen(s) {}
~DestroyPixmapClosure() {
XFreePixmap(DisplayOfScreen(mScreen), mPixmap);
}
Drawable mPixmap;
Screen *mScreen;
};
static void
DestroyPixmap(void *data)
{
delete static_cast<DestroyPixmapClosure*>(data);
}
#endif
TemporaryRef<SourceSurface>
DrawTargetCairo::OptimizeSourceSurface(SourceSurface *aSurface) const
{
#ifdef CAIRO_HAS_XLIB_SURFACE
if (cairo_surface_get_type(mSurface) != CAIRO_SURFACE_TYPE_XLIB) {
return aSurface;
}
IntSize size = aSurface->GetSize();
if (!size.width || !size.height) {
return aSurface;
}
// Although the dimension parameters in the xCreatePixmapReq wire protocol are
// 16-bit unsigned integers, the server's CreatePixmap returns BadAlloc if
// either dimension cannot be represented by a 16-bit *signed* integer.
#define XLIB_IMAGE_SIDE_SIZE_LIMIT 0x7fff
if (size.width > XLIB_IMAGE_SIDE_SIZE_LIMIT ||
size.height > XLIB_IMAGE_SIDE_SIZE_LIMIT) {
return aSurface;
}
SurfaceFormat format = aSurface->GetFormat();
Screen *screen = cairo_xlib_surface_get_screen(mSurface);
Display *dpy = DisplayOfScreen(screen);
XRenderPictFormat* xrenderFormat = nullptr;
switch (format) {
case SurfaceFormat::B8G8R8A8:
xrenderFormat = XRenderFindStandardFormat(dpy, PictStandardARGB32);
break;
case SurfaceFormat::B8G8R8X8:
xrenderFormat = XRenderFindStandardFormat(dpy, PictStandardRGB24);
break;
case SurfaceFormat::A8:
xrenderFormat = XRenderFindStandardFormat(dpy, PictStandardA8);
break;
default:
return aSurface;
}
if (!xrenderFormat) {
return aSurface;
}
Drawable pixmap = XCreatePixmap(dpy, RootWindowOfScreen(screen),
size.width, size.height,
xrenderFormat->depth);
if (!pixmap) {
return aSurface;
}
ScopedDeletePtr<DestroyPixmapClosure> closure(
new DestroyPixmapClosure(pixmap, screen));
ScopedCairoSurface csurf(
cairo_xlib_surface_create_with_xrender_format(dpy, pixmap,
screen, xrenderFormat,
size.width, size.height));
if (!csurf || cairo_surface_status(csurf)) {
return aSurface;
}
cairo_surface_set_user_data(csurf, &gDestroyPixmapKey,
closure.forget(), DestroyPixmap);
RefPtr<DrawTargetCairo> dt = new DrawTargetCairo();
if (!dt->Init(csurf, size, &format)) {
return aSurface;
}
dt->CopySurface(aSurface,
IntRect(0, 0, size.width, size.height),
IntPoint(0, 0));
dt->Flush();
RefPtr<SourceSurface> surf =
new SourceSurfaceCairo(csurf, size, format);
return surf;
#endif
return aSurface;
}

View File

@ -14,8 +14,6 @@ Nicolas Silva
## Definitions
* Client and Host: In Gecko's compositing architecture, the client process is the producer, while the host process is the consumer side, where compositing takes place.
## Use cases
Drawing web content into a surface and share it with the compositor process to display it on the screen without copies.
@ -31,32 +29,9 @@ Drawing web content into a surface and share it with the compositor process to d
## TextureClient and TextureHost
TextureClient and TextureHost are the closest abstractions we currently have to MozSurface.
Inline documentation about TextureClient and TextureHost can be found in:
TextureClient and TextureHost are the closest abstractions we currently have to MozSurface. The current plan is to evolve TextureClient into MozSurface. In its current state, TextureClient doesn't meet all the requirements and desisgn decisions of MozSurface yet.
* [gfx/layers/client/TextureClient.h](http://dxr.mozilla.org/mozilla-central/source/gfx/layers/client/TextureClient.h)
* [gfx/layers/composite/TextureHost.h](http://dxr.mozilla.org/mozilla-central/source/gfx/layers/composite/TextureHost.h)
TextureClient is the client-side handle on a MozSurface, while TextureHost is the equivalent host-side representation. There can only be one TextureClient for a given TextureHost, and one TextureHost for a given TextureClient. Likewise, there can only be one shared object for a given TextureClient/TextureHost pair.
A MozSurface containing data that is shared between a client process and a host process exists in the following form:
```
.
Client process . Host process
.
________________ ______________ ______________
| | | | | |
| TextureClient +----+ <SharedData> +----+ TextureHost |
|________________| |______________| |______________|
.
.
.
Figure 1) A Surface as seen by the client and the host processes
```
The above figure is a logical representation, not a class diagram.
`<SharedData>` is a placeholder for whichever platform specific surface type we are sharing, for example a Gralloc buffer on Gonk or a D3D11 texture on Windows.
In particular, TextureClient/TextureHost are designed around cross-process sharing specifically. See the SharedMozSurface design document for more information about TextureClient and TextureHost.
## Locking semantics
@ -109,67 +84,6 @@ MozSurface (TextureClient/TextureHost in its current form) defines ownership rul
These rules rely on the fact that the underlying shared data is strictly owned by the MozSurface. This means that keeping direct references to the shared data is illegal and unsafe.
## Deallocation protocol
The shared data is accessible by both the client-side and the host-side of the MozSurface. A deallocation protocol must be defined to handle which side deallocates the data, and to ensure that it doesn't cause any race condition.
The client side, which contains the web content's logic, always "decides" when a surface is needed or not. So the life time of a MozSurface is driven by the reference count of it's client-side handle (TextureClient).
When a TextureClient's reference count reaches zero, a "Remove" message is sent in order to let the host side that the shared data is not accessible on the client side and that it si safe for it to be deleted. The host side responds with a "Delete" message.
```
client side . host side
.
(A) Client: Send Remove -. .
\ .
\ . ... can receive and send ...
\
Can receive `--> (B) Host: Receive Remove
Can't send |
.-- (C) Host: Send Delete
/
/ . ... can't receive nor send ...
/ .
(D) Client: Receive Delete <--' .
.
Figure 2) MozSurface deallocation handshake
```
This handshake protocol is twofold:
* It defines where and when it is possible to deallocate the shared data without races
* It makes it impossible for asynchronous messages to race with the destruction of the MozSurface.
### Deallocating on the host side
In the common case, the shared data is deallocated asynchronously on the host side. In this case the deallocation takes place at the point (C) of figure 2.
### Deallocating on the client side
In some rare cases, for instance if the underlying implementation requires it, the shared data must be deallocated on the client side. In such cases, deallocation happens at the point (D) of figure 2.
In some exceptional cases, this needs to happen synchronously, meaning that the client-side thread will block until the Delete message is received. This is supported but it is terrible for performance, so it should be avoided as much as possible.
Currently this is needed when shutting down a hardware-decoded video stream with libstagefright on Gonk, because the libstagefright unfortunately assumes it has full ownership over the shared data (gralloc buffers) and crashes if there are still users of the buffers.
### Sharing state
The above deallocation protocol of a MozSurface applies to the common case that is when the surface is shared between two processes. A Surface can also be deallocated while it is not shared.
The sharing state of a MozSurface can be one of the following:
* (1) Uninitialized (it doesn't have any shared data)
* (2) Local (it isn't shared with the another thread/process)
* (3) Shared (the state you would expect it to be most of the time)
* (4) Invalid (when for some rare cases we needed to force the deallocation of the shared data before the destruction of the TextureClient object).
Surfaces can move from state N to state N+1 and be deallocated in any of these states. It could be possible to move from Shared to Local, but we currently don't have a use case for it.
The deallocation protocol above, applies to the Shared state (3).
In the other cases:
* (1) Unitilialized: There is nothing to do.
* (2) Local: The shared data is deallocated by the client side without need for a handshake, since it is not shared with other threads.
* (4) Invalid: There is nothing to do (deallocation has already happenned).
## Internal buffers / direct texturing
Some MozSurface implementations use CPU-side shared memory to share the texture data accross processes, and require a GPU texture upload when interfacing with a TextureSource. In this case we say that the surface has an internal buffer (because it is implicitly equivalent to double buffering where the shared data is the back buffer and the GPU side texture is the front buffer). We also say that it doesn't do "direct texturing" meaning that we don't draw directly into the GPU-side texture.
@ -183,18 +97,6 @@ While direct texturing is usually the most efficient way, it is not always avail
## Alternative solutions
### Sending ownership back and forth between the client and host sides through message passing, intead of sharing.
The current design of MozSurface makes the surface accessible from both sides at the same time, forcing us to do Locking and have a hand shake around deallocating the shared data, while using pure message passing and making the surface accessible only from one side at a time would avoid these complications.
Using pure message passing was actually the first approach we tried when we created the first version of TextureClient and TextureHost. This strategy failed in several places, partly because of some legacy in Gecko's architecture, and partly because of some of optimizations we do to avoid copying surfaces.
We need a given surface to be accessible on both the client and host for the following reasons:
* Gecko can at any time require read access on the client side to a surface that is shared with the host process, for example to build a temporary layer manager and generate a screenshot. This is mostly a legacy problem.
* We do some copy-on-write optimizations on surfaces that are shared with the compositor in order to keep invalid regions as small as possible. Out tiling implementation is an example of that.
* Our buffer rotation code on scrollable non-tiled layers also requires a synchronization on the client side between the front and back buffers, while the front buffer is used on the host side.
## Backends
We have MozSurface implementaions (classes inheriting from TextureClient/TextureHost) for OpenGL, Software, D3D9, and D3D11 backends.
@ -210,14 +112,6 @@ TODO - How can we make MozSurface more testable and what should we test?
## Future work
### Rename TextureClient/TextureHost
The current terminology is very confusing.
### Unify TextureClient and TextureHost
TextureClient and TextureHost should live under a common interface to better hide the IPC details. The base classe should only expose the non-ipc related methods such as Locking, access through a DrawTarget, access to a TextureSource.
### Using a MozSurface as a source for Drawing
MozSurface should be able to expose a borrowed Moz2D SourceSurface that is valid between Lock and Unlock similarly to how it exposes a DrawTarget.

147
gfx/doc/SharedMozSurface.md Normal file
View File

@ -0,0 +1,147 @@
Shared MozSurface {#mozsurface}
==========
**This document is work in progress. Some information may be missing or incomplete.**
Shared MozSurfaces represent an important use case of MozSurface, anything that is in the MozSurface design document also applies to shared MozSurfaces.
## Goals
We need to be able to safely and efficiently render web content into surfaces that may be shared accross processes.
MozSurface is a cross-process and backend-independent Surface API and not a stream API.
## Owner
Nicolas Silva
## Definitions
* Client and Host: In Gecko's compositing architecture, the client process is the producer, while the host process is the consumer side, where compositing takes place.
## Use cases
Drawing web content into a surface and share it with the compositor process to display it on the screen without copies.
## Requirement
Shared MozSurfaces represent an important use case of MozSurface, it has the same requirements as MozSurface.
## TextureClient and TextureHost
TextureClient and TextureHost are the closest abstractions we currently have to MozSurface.
Inline documentation about TextureClient and TextureHost can be found in:
* [gfx/layers/client/TextureClient.h](http://dxr.mozilla.org/mozilla-central/source/gfx/layers/client/TextureClient.h)
* [gfx/layers/composite/TextureHost.h](http://dxr.mozilla.org/mozilla-central/source/gfx/layers/composite/TextureHost.h)
TextureClient is the client-side handle on a MozSurface, while TextureHost is the equivalent host-side representation. There can only be one TextureClient for a given TextureHost, and one TextureHost for a given TextureClient. Likewise, there can only be one shared object for a given TextureClient/TextureHost pair.
A MozSurface containing data that is shared between a client process and a host process exists in the following form:
```
.
Client process . Host process
.
________________ ______________ ______________
| | | | | |
| TextureClient +----+ <SharedData> +----+ TextureHost |
|________________| |______________| |______________|
.
.
.
Figure 1) A Surface as seen by the client and the host processes
```
The above figure is a logical representation, not a class diagram.
`<SharedData>` is a placeholder for whichever platform specific surface type we are sharing, for example a Gralloc buffer on Gonk or a D3D11 texture on Windows.
## Deallocation protocol
The shared data is accessible by both the client-side and the host-side of the MozSurface. A deallocation protocol must be defined to handle which side deallocates the data, and to ensure that it doesn't cause any race condition.
The client side, which contains the web content's logic, always "decides" when a surface is needed or not. So the life time of a MozSurface is driven by the reference count of it's client-side handle (TextureClient).
When a TextureClient's reference count reaches zero, a "Remove" message is sent in order to let the host side that the shared data is not accessible on the client side and that it si safe for it to be deleted. The host side responds with a "Delete" message.
```
client side . host side
.
(A) Client: Send Remove -. .
\ .
\ . ... can receive and send ...
\
Can receive `--> (B) Host: Receive Remove
Can't send |
.-- (C) Host: Send Delete
/
/ . ... can't receive nor send ...
/ .
(D) Client: Receive Delete <--' .
.
Figure 2) MozSurface deallocation handshake
```
This handshake protocol is twofold:
* It defines where and when it is possible to deallocate the shared data without races
* It makes it impossible for asynchronous messages to race with the destruction of the MozSurface.
### Deallocating on the host side
In the common case, the shared data is deallocated asynchronously on the host side. In this case the deallocation takes place at the point (C) of figure 2.
### Deallocating on the client side
In some rare cases, for instance if the underlying implementation requires it, the shared data must be deallocated on the client side. In such cases, deallocation happens at the point (D) of figure 2.
In some exceptional cases, this needs to happen synchronously, meaning that the client-side thread will block until the Delete message is received. This is supported but it is terrible for performance, so it should be avoided as much as possible.
Currently this is needed when shutting down a hardware-decoded video stream with libstagefright on Gonk, because the libstagefright unfortunately assumes it has full ownership over the shared data (gralloc buffers) and crashes if there are still users of the buffers.
### Sharing state
The above deallocation protocol of a MozSurface applies to the common case that is when the surface is shared between two processes. A Surface can also be deallocated while it is not shared.
The sharing state of a MozSurface can be one of the following:
* (1) Uninitialized (it doesn't have any shared data)
* (2) Local (it isn't shared with the another thread/process)
* (3) Shared (the state you would expect it to be most of the time)
* (4) Invalid (when for some rare cases we needed to force the deallocation of the shared data before the destruction of the TextureClient object).
Surfaces can move from state N to state N+1 and be deallocated in any of these states. It could be possible to move from Shared to Local, but we currently don't have a use case for it.
The deallocation protocol above, applies to the Shared state (3).
In the other cases:
* (1) Unitilialized: There is nothing to do.
* (2) Local: The shared data is deallocated by the client side without need for a handshake, since it is not shared with other threads.
* (4) Invalid: There is nothing to do (deallocation has already happenned).
## Alternative solutions
### Sending ownership back and forth between the client and host sides through message passing, intead of sharing.
The current design of MozSurface makes the surface accessible from both sides at the same time, forcing us to do Locking and have a hand shake around deallocating the shared data, while using pure message passing and making the surface accessible only from one side at a time would avoid these complications.
Using pure message passing was actually the first approach we tried when we created the first version of TextureClient and TextureHost. This strategy failed in several places, partly because of some legacy in Gecko's architecture, and partly because of some of optimizations we do to avoid copying surfaces.
We need a given surface to be accessible on both the client and host for the following reasons:
* Gecko can at any time require read access on the client side to a surface that is shared with the host process, for example to build a temporary layer manager and generate a screenshot. This is mostly a legacy problem.
* We do some copy-on-write optimizations on surfaces that are shared with the compositor in order to keep invalid regions as small as possible. Out tiling implementation is an example of that.
* Our buffer rotation code on scrollable non-tiled layers also requires a synchronization on the client side between the front and back buffers, while the front buffer is used on the host side.
## Testing
TODO - How can we make shared MozSurfaces more testable and what should we test?
## Future work
### Rename TextureClient/TextureHost
The current terminology is very confusing.
### Unify TextureClient and TextureHost
TextureClient and TextureHost should live under a common interface to better hide the IPC details. The base classe should only expose the non-ipc related methods such as Locking, access through a DrawTarget, access to a TextureSource.
## Comparison with other APIs

View File

@ -15,6 +15,10 @@
#include <math.h>
#include "GeckoProfiler.h"
#ifdef MOZ_WIDGET_GONK
#include "mozilla/layers/GrallocTextureHost.h"
#endif
#define TEST_STEPS 1000
#define DURATION_THRESHOLD 30
#define THRESHOLD_ABORT_COUNT 5
@ -46,6 +50,45 @@ private:
const char* mTestName;
};
static void
DrawFrameTrivialQuad(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep, const EffectChain& effects) {
for (size_t i = 0; i < aStep * 10; i++) {
const gfx::Rect& rect = gfx::Rect(i % (int)aScreenRect.width,
(int)(i / aScreenRect.height),
1, 1);
const gfx::Rect& clipRect = aScreenRect;
float opacity = 1.f;
gfx::Matrix transform2d;
gfx::Matrix4x4 transform = gfx::Matrix4x4::From2D(transform2d);
aCompositor->DrawQuad(rect, clipRect, effects, opacity, transform);
}
}
static void
DrawFrameStressQuad(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep, const EffectChain& effects)
{
for (size_t i = 0; i < aStep * 10; i++) {
const gfx::Rect& rect = gfx::Rect(aScreenRect.width * SimplePseudoRandom(i, 0),
aScreenRect.height * SimplePseudoRandom(i, 1),
aScreenRect.width * SimplePseudoRandom(i, 2),
aScreenRect.height * SimplePseudoRandom(i, 3));
const gfx::Rect& clipRect = aScreenRect;
float opacity = 1.f;
gfx::Matrix transform2d;
transform2d = transform2d.Rotate(SimplePseudoRandom(i, 4) * 70.f);
gfx::Matrix4x4 transform = gfx::Matrix4x4::From2D(transform2d);
aCompositor->DrawQuad(rect, clipRect, effects, opacity, transform);
}
}
class EffectSolidColorBench : public BenchTest {
public:
EffectSolidColorBench()
@ -80,30 +123,22 @@ public:
{}
void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
for (size_t i = 0; i < aStep * 10; i++) {
EffectChain effects;
effects.mPrimaryEffect = CreateEffect(aStep);
DrawFrameTrivialQuad(aCompositor, aScreenRect, aStep, effects);
}
TemporaryRef<Effect> CreateEffect(size_t i) {
float red;
float tmp;
red = modf(i * 0.03f, &tmp);
EffectChain effects;
gfxRGBA color(red, 0.4f, 0.4f, 1.0f);
effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(color.r,
color.g,
color.b,
color.a));
const gfx::Rect& rect = gfx::Rect(i % (int)aScreenRect.width,
(int)(i / aScreenRect.height),
1, 1);
const gfx::Rect& clipRect = aScreenRect;
float opacity = 1.f;
gfx::Matrix transform2d;
gfx::Matrix4x4 transform = gfx::Matrix4x4::From2D(transform2d);
aCompositor->DrawQuad(rect, clipRect, effects, opacity, transform);
}
return new EffectSolidColor(gfx::Color(color.r,
color.g,
color.b,
color.a));
}
};
@ -114,32 +149,22 @@ public:
{}
void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
for (size_t i = 0; i < aStep * 10; i++) {
EffectChain effects;
effects.mPrimaryEffect = CreateEffect(aStep);
DrawFrameStressQuad(aCompositor, aScreenRect, aStep, effects);
}
TemporaryRef<Effect> CreateEffect(size_t i) {
float red;
float tmp;
red = modf(i * 0.03f, &tmp);
EffectChain effects;
gfxRGBA color(red, 0.4f, 0.4f, 1.0f);
effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(color.r,
color.g,
color.b,
color.a));
const gfx::Rect& rect = gfx::Rect(aScreenRect.width * SimplePseudoRandom(i, 0),
aScreenRect.height * SimplePseudoRandom(i, 1),
aScreenRect.width * SimplePseudoRandom(i, 2),
aScreenRect.height * SimplePseudoRandom(i, 3));
const gfx::Rect& clipRect = aScreenRect;
float opacity = 0.3f;
gfx::Matrix transform2d;
transform2d = transform2d.Rotate(SimplePseudoRandom(i, 4) * 70.f);
gfx::Matrix4x4 transform = gfx::Matrix4x4::From2D(transform2d);
aCompositor->DrawQuad(rect, clipRect, effects, opacity, transform);
}
return new EffectSolidColor(gfx::Color(color.r,
color.g,
color.b,
color.a));
}
};
@ -177,14 +202,194 @@ public:
}
};
class TrivialTexturedQuadBench : public BenchTest {
public:
TrivialTexturedQuadBench()
: BenchTest("Trvial Textured Quad (10s 256x256 quads)")
{}
uint32_t* mBuf;
RefPtr<DataSourceSurface> mSurface;
RefPtr<DataTextureSource> mTexture;
virtual void Setup(Compositor* aCompositor, size_t aStep) {
int bytesPerPixel = 4;
size_t w = 256;
size_t h = 256;
mBuf = (uint32_t *) malloc(w * h * sizeof(uint32_t));
for (size_t i = 0; i < w * h; i++) {
mBuf[i] = 0xFF00008F;
}
mSurface = Factory::CreateWrappingDataSourceSurface(
reinterpret_cast<uint8_t*>(mBuf), w * bytesPerPixel, IntSize(w, h), SurfaceFormat::B8G8R8A8);
mTexture = aCompositor->CreateDataTextureSource();
mTexture->Update(mSurface);
}
void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
EffectChain effects;
effects.mPrimaryEffect = CreateEffect(aStep);
DrawFrameTrivialQuad(aCompositor, aScreenRect, aStep, effects);
}
virtual void Teardown(Compositor* aCompositor) {
mSurface = nullptr;
mTexture = nullptr;
free(mBuf);
}
TemporaryRef<Effect> CreateEffect(size_t i) {
RefPtr<TexturedEffect> effect = CreateTexturedEffect(SurfaceFormat::B8G8R8A8, mTexture, Filter::POINT);
return effect;
}
};
class StressTexturedQuadBench : public BenchTest {
public:
StressTexturedQuadBench()
: BenchTest("Stress Textured Quad (10s 256x256 quads)")
{}
uint32_t* mBuf;
RefPtr<DataSourceSurface> mSurface;
RefPtr<DataTextureSource> mTexture;
virtual void Setup(Compositor* aCompositor, size_t aStep) {
int bytesPerPixel = 4;
size_t w = 256;
size_t h = 256;
mBuf = (uint32_t *) malloc(w * h * sizeof(uint32_t));
for (size_t i = 0; i < w * h; i++) {
mBuf[i] = 0xFF00008F;
}
mSurface = Factory::CreateWrappingDataSourceSurface(
reinterpret_cast<uint8_t*>(mBuf), w * bytesPerPixel, IntSize(w, h), SurfaceFormat::B8G8R8A8);
mTexture = aCompositor->CreateDataTextureSource();
mTexture->Update(mSurface);
}
void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
EffectChain effects;
effects.mPrimaryEffect = CreateEffect(aStep);
DrawFrameStressQuad(aCompositor, aScreenRect, aStep, effects);
}
virtual void Teardown(Compositor* aCompositor) {
mSurface = nullptr;
mTexture = nullptr;
free(mBuf);
}
virtual TemporaryRef<Effect> CreateEffect(size_t i) {
RefPtr<TexturedEffect> effect = CreateTexturedEffect(SurfaceFormat::B8G8R8A8, mTexture, Filter::POINT);
return effect;
}
};
#ifdef MOZ_WIDGET_GONK
class TrivialGrallocQuadBench : public BenchTest {
public:
TrivialGrallocQuadBench()
: BenchTest("Travial Gralloc Quad (10s 256x256 quads)")
{}
uint32_t* mBuf;
android::sp<android::GraphicBuffer> mGralloc;
RefPtr<TextureSource> mTexture;
virtual void Setup(Compositor* aCompositor, size_t aStep) {
mBuf = nullptr;
int w = 256;
int h = 256;
int32_t format = android::PIXEL_FORMAT_RGBA_8888;;
mGralloc = new android::GraphicBuffer(w, h,
format,
android::GraphicBuffer::USAGE_SW_READ_OFTEN |
android::GraphicBuffer::USAGE_SW_WRITE_OFTEN |
android::GraphicBuffer::USAGE_HW_TEXTURE);
mTexture = new mozilla::layers::GrallocTextureSourceOGL((CompositorOGL*)aCompositor, mGralloc.get(), SurfaceFormat::B8G8R8A8);
}
void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
EffectChain effects;
effects.mPrimaryEffect = CreateEffect(aStep);
DrawFrameTrivialQuad(aCompositor, aScreenRect, aStep, effects);
}
virtual void Teardown(Compositor* aCompositor) {
mGralloc = nullptr;
mTexture = nullptr;
free(mBuf);
}
virtual TemporaryRef<Effect> CreateEffect(size_t i) {
RefPtr<TexturedEffect> effect = CreateTexturedEffect(SurfaceFormat::B8G8R8A8, mTexture, Filter::POINT);
return effect;
}
};
class StressGrallocQuadBench : public BenchTest {
public:
StressGrallocQuadBench()
: BenchTest("Stress Gralloc Quad (10s 256x256 quads)")
{}
uint32_t* mBuf;
android::sp<android::GraphicBuffer> mGralloc;
RefPtr<TextureSource> mTexture;
virtual void Setup(Compositor* aCompositor, size_t aStep) {
mBuf = nullptr;
int w = 256;
int h = 256;
int32_t format = android::PIXEL_FORMAT_RGBA_8888;;
mGralloc = new android::GraphicBuffer(w, h,
format,
android::GraphicBuffer::USAGE_SW_READ_OFTEN |
android::GraphicBuffer::USAGE_SW_WRITE_OFTEN |
android::GraphicBuffer::USAGE_HW_TEXTURE);
mTexture = new mozilla::layers::GrallocTextureSourceOGL((CompositorOGL*)aCompositor, mGralloc.get(), SurfaceFormat::B8G8R8A8);
}
void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
EffectChain effects;
effects.mPrimaryEffect = CreateEffect(aStep);
DrawFrameStressQuad(aCompositor, aScreenRect, aStep, effects);
}
virtual void Teardown(Compositor* aCompositor) {
mGralloc = nullptr;
mTexture = nullptr;
free(mBuf);
}
virtual TemporaryRef<Effect> CreateEffect(size_t i) {
RefPtr<TexturedEffect> effect = CreateTexturedEffect(SurfaceFormat::B8G8R8A8, mTexture, Filter::POINT);
return effect;
}
};
#endif
static void RunCompositorBench(Compositor* aCompositor, const gfx::Rect& aScreenRect)
{
std::vector<BenchTest*> tests;
tests.push_back(new EffectSolidColorBench());
tests.push_back(new UploadBench());
tests.push_back(new EffectSolidColorTrivialBench());
tests.push_back(new EffectSolidColorStressBench());
tests.push_back(new UploadBench());
tests.push_back(new TrivialTexturedQuadBench());
tests.push_back(new StressTexturedQuadBench());
#ifdef MOZ_WIDGET_GONK
tests.push_back(new TrivialGrallocQuadBench());
tests.push_back(new StressGrallocQuadBench());
#endif
for (size_t i = 0; i < tests.size(); i++) {
BenchTest* test = tests[i];

View File

@ -197,7 +197,7 @@ CreateSamplingRestrictedDrawable(gfxDrawable* aDrawable,
const gfxMatrix& aUserSpaceToImageSpace,
const gfxRect& aSourceRect,
const gfxRect& aSubimage,
const gfxImageFormat aFormat)
const SurfaceFormat aFormat)
{
PROFILER_LABEL("gfxUtils", "CreateSamplingRestricedDrawable",
js::ProfileEntry::Category::GRAPHICS);
@ -233,9 +233,9 @@ CreateSamplingRestrictedDrawable(gfxDrawable* aDrawable,
nsRefPtr<gfxASurface> temp = image->GetSubimage(needed);
drawable = new gfxSurfaceDrawable(temp, size, gfxMatrix().Translate(-needed.TopLeft()));
} else {
mozilla::RefPtr<mozilla::gfx::DrawTarget> target =
RefPtr<DrawTarget> target =
gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(ToIntSize(size),
ImageFormatToSurfaceFormat(aFormat));
aFormat);
if (!target) {
return nullptr;
}
@ -403,7 +403,7 @@ gfxUtils::DrawPixelSnapped(gfxContext* aContext,
const gfxRect& aSourceRect,
const gfxRect& aImageRect,
const gfxRect& aFill,
const gfxImageFormat aFormat,
const SurfaceFormat aFormat,
GraphicsFilter aFilter,
uint32_t aImageFlags)
{

View File

@ -65,7 +65,7 @@ public:
const gfxRect& aSourceRect,
const gfxRect& aImageRect,
const gfxRect& aFill,
const gfxImageFormat aFormat,
const mozilla::gfx::SurfaceFormat aFormat,
GraphicsFilter aFilter,
uint32_t aImageFlags = imgIContainer::FLAG_NONE);

View File

@ -159,11 +159,11 @@ void nsGIFDecoder2::BeginGIF()
//******************************************************************************
void nsGIFDecoder2::BeginImageFrame(uint16_t aDepth)
{
gfxImageFormat format;
gfx::SurfaceFormat format;
if (mGIFStruct.is_transparent)
format = gfxImageFormat::ARGB32;
format = gfx::SurfaceFormat::B8G8R8A8;
else
format = gfxImageFormat::RGB24;
format = gfx::SurfaceFormat::B8G8R8X8;
MOZ_ASSERT(HasSize());
@ -188,7 +188,7 @@ void nsGIFDecoder2::BeginImageFrame(uint16_t aDepth)
format);
} else {
// Our preallocated frame matches up, with the possible exception of alpha.
if (format == gfxImageFormat::RGB24) {
if (format == gfx::SurfaceFormat::B8G8R8X8) {
GetCurrentFrame()->SetHasNoAlpha();
}
}

View File

@ -140,7 +140,7 @@ nsPNGDecoder::~nsPNGDecoder()
// CreateFrame() is used for both simple and animated images
void nsPNGDecoder::CreateFrame(png_uint_32 x_offset, png_uint_32 y_offset,
int32_t width, int32_t height,
gfxImageFormat format)
gfx::SurfaceFormat format)
{
// Our first full frame is automatically created by the image decoding
// infrastructure. Just use it as long as it matches up.
@ -150,7 +150,7 @@ void nsPNGDecoder::CreateFrame(png_uint_32 x_offset, png_uint_32 y_offset,
NeedNewFrame(mNumFrames, x_offset, y_offset, width, height, format);
} else if (mNumFrames == 0) {
// Our preallocated frame matches up, with the possible exception of alpha.
if (format == gfxImageFormat::RGB24) {
if (format == gfx::SurfaceFormat::B8G8R8X8) {
GetCurrentFrame()->SetHasNoAlpha();
}
}
@ -629,9 +629,9 @@ nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr)
#endif
if (channels == 1 || channels == 3)
decoder->format = gfxImageFormat::RGB24;
decoder->format = gfx::SurfaceFormat::B8G8R8X8;
else if (channels == 2 || channels == 4)
decoder->format = gfxImageFormat::ARGB32;
decoder->format = gfx::SurfaceFormat::B8G8R8A8;
#ifdef PNG_APNG_SUPPORTED
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL))
@ -745,7 +745,7 @@ nsPNGDecoder::row_callback(png_structp png_ptr, png_bytep new_row,
}
switch (decoder->format) {
case gfxImageFormat::RGB24:
case gfx::SurfaceFormat::B8G8R8X8:
{
// counter for while() loops below
uint32_t idx = iwidth;
@ -772,7 +772,7 @@ nsPNGDecoder::row_callback(png_structp png_ptr, png_bytep new_row,
}
}
break;
case gfxImageFormat::ARGB32:
case gfx::SurfaceFormat::B8G8R8A8:
{
if (!decoder->mDisablePremultipliedAlpha) {
for (uint32_t x=width; x>0; --x) {

View File

@ -33,7 +33,7 @@ public:
void CreateFrame(png_uint_32 x_offset, png_uint_32 y_offset,
int32_t width, int32_t height,
gfxImageFormat format);
gfx::SurfaceFormat format);
void EndImageFrame();
// Check if PNG is valid ICO (32bpp RGBA)
@ -76,7 +76,7 @@ public:
qcms_profile *mInProfile;
qcms_transform *mTransform;
gfxImageFormat format;
gfx::SurfaceFormat format;
// For size decodes
uint8_t mSizeBytes[8]; // Space for width and height, both 4 bytes

View File

@ -255,7 +255,7 @@ ClippedImage::GetFrameInternal(const nsIntSize& aViewportSize,
gfxRect imageRect(0, 0, mClip.width, mClip.height);
gfxUtils::DrawPixelSnapped(ctx, drawable, gfxMatrix(),
imageRect, imageRect, imageRect, imageRect,
gfxImageFormat::ARGB32,
SurfaceFormat::B8G8R8A8,
GraphicsFilter::FILTER_FAST);
// Cache the resulting surface.
@ -340,7 +340,7 @@ ClippedImage::Draw(gfxContext* aContext,
gfxRect subimage(aSubimage.x, aSubimage.y, aSubimage.width, aSubimage.height);
gfxUtils::DrawPixelSnapped(aContext, drawable, aUserSpaceToImageSpace,
subimage, sourceRect, imageRect, aFill,
gfxImageFormat::ARGB32, aFilter);
SurfaceFormat::B8G8R8A8, aFilter);
return NS_OK;
}

View File

@ -203,8 +203,6 @@ Decoder::AllocateFrame()
MOZ_ASSERT(mNeedsNewFrame);
MOZ_ASSERT(NS_IsMainThread());
MarkFrameDirty();
nsresult rv;
imgFrame* frame = nullptr;
if (mNewFrameData.mPaletteDepth) {
@ -421,7 +419,7 @@ Decoder::PostDecoderError(nsresult aFailureCode)
void
Decoder::NeedNewFrame(uint32_t framenum, uint32_t x_offset, uint32_t y_offset,
uint32_t width, uint32_t height,
gfxImageFormat format,
gfx::SurfaceFormat format,
uint8_t palette_depth /* = 0 */)
{
// Decoders should never call NeedNewFrame without yielding back to Write().
@ -434,15 +432,5 @@ Decoder::NeedNewFrame(uint32_t framenum, uint32_t x_offset, uint32_t y_offset,
mNeedsNewFrame = true;
}
void
Decoder::MarkFrameDirty()
{
MOZ_ASSERT(NS_IsMainThread());
if (mCurrentFrame) {
mCurrentFrame->ApplyDirtToSurfaces();
}
}
} // namespace image
} // namespace mozilla

View File

@ -151,7 +151,7 @@ public:
// will be called again with nullptr and 0 as arguments.
void NeedNewFrame(uint32_t frameNum, uint32_t x_offset, uint32_t y_offset,
uint32_t width, uint32_t height,
gfxImageFormat format,
gfx::SurfaceFormat format,
uint8_t palette_depth = 0);
virtual bool NeedsNewFrame() const { return mNeedsNewFrame; }
@ -160,10 +160,6 @@ public:
// status code from that attempt. Clears mNewFrameData.
virtual nsresult AllocateFrame();
// Called when a chunk of decoding has been done and the frame needs to be
// marked as dirty. Must be called only on the main thread.
void MarkFrameDirty();
imgFrame* GetCurrentFrame() const { return mCurrentFrame; }
protected:
@ -250,7 +246,7 @@ private:
NewFrameData(uint32_t num, uint32_t offsetx, uint32_t offsety,
uint32_t width, uint32_t height,
gfxImageFormat format, uint8_t paletteDepth)
gfx::SurfaceFormat format, uint8_t paletteDepth)
: mFrameNum(num)
, mOffsetX(offsetx)
, mOffsetY(offsety)
@ -264,7 +260,7 @@ private:
uint32_t mOffsetY;
uint32_t mWidth;
uint32_t mHeight;
gfxImageFormat mFormat;
gfx::SurfaceFormat mFormat;
uint8_t mPaletteDepth;
};
NewFrameData mNewFrameData;

View File

@ -261,7 +261,7 @@ FrameBlender::DoBlend(nsIntRect* aDirtyRect,
if (!mAnim->compositingFrame) {
mAnim->compositingFrame.SetFrame(new imgFrame());
nsresult rv = mAnim->compositingFrame->Init(0, 0, mSize.width, mSize.height,
gfxImageFormat::ARGB32);
gfx::SurfaceFormat::B8G8R8A8);
if (NS_FAILED(rv)) {
mAnim->compositingFrame.SetFrame(nullptr);
return false;
@ -391,7 +391,7 @@ FrameBlender::DoBlend(nsIntRect* aDirtyRect,
if (!mAnim->compositingPrevFrame) {
mAnim->compositingPrevFrame.SetFrame(new imgFrame());
nsresult rv = mAnim->compositingPrevFrame->Init(0, 0, mSize.width, mSize.height,
gfxImageFormat::ARGB32);
gfx::SurfaceFormat::B8G8R8A8);
if (NS_FAILED(rv)) {
mAnim->compositingPrevFrame.SetFrame(nullptr);
return false;

View File

@ -100,13 +100,10 @@ OrientedImage::GetFrame(uint32_t aWhichFrame,
// Determine an appropriate format for the surface.
gfx::SurfaceFormat surfaceFormat;
gfxImageFormat imageFormat;
if (InnerImage()->FrameIsOpaque(aWhichFrame)) {
surfaceFormat = gfx::SurfaceFormat::B8G8R8X8;
imageFormat = gfxImageFormat::ARGB32;
} else {
surfaceFormat = gfx::SurfaceFormat::B8G8R8A8;
imageFormat = gfxImageFormat::ARGB32;
}
// Create a surface to draw into.
@ -131,7 +128,7 @@ OrientedImage::GetFrame(uint32_t aWhichFrame,
gfxRect imageRect(0, 0, width, height);
gfxUtils::DrawPixelSnapped(ctx, drawable, OrientationMatrix(nsIntSize(width, height)),
imageRect, imageRect, imageRect, imageRect,
imageFormat, GraphicsFilter::FILTER_FAST);
surfaceFormat, GraphicsFilter::FILTER_FAST);
return target->Snapshot();
}

View File

@ -212,33 +212,27 @@ public:
bool success = false;
if (!dstLocked) {
bool srcLocked = NS_SUCCEEDED(srcFrame->LockImageData());
srcSurface = srcFrame->GetSurface();
dstLocked = NS_SUCCEEDED(dstFrame->LockImageData());
dstSurface = dstFrame->GetSurface();
nsRefPtr<gfxASurface> dstASurf;
nsRefPtr<gfxASurface> srcASurf;
success = srcLocked && NS_SUCCEEDED(srcFrame->GetSurface(getter_AddRefs(srcASurf)));
success = success && dstLocked && NS_SUCCEEDED(dstFrame->GetSurface(getter_AddRefs(dstASurf)));
success = success && srcLocked && dstLocked && srcASurf && dstASurf;
success = srcLocked && dstLocked && srcSurface && dstSurface;
if (success) {
srcSurface = srcASurf->GetAsImageSurface();
dstSurface = dstASurf->GetAsImageSurface();
srcData = srcSurface->Data();
dstData = dstSurface->Data();
srcStride = srcSurface->Stride();
dstStride = dstSurface->Stride();
srcFormat = mozilla::gfx::ImageFormatToSurfaceFormat(srcFrame->GetFormat());
srcData = srcFrame->GetImageData();
dstData = dstFrame->GetImageData();
srcStride = srcFrame->GetImageBytesPerRow();
dstStride = dstFrame->GetImageBytesPerRow();
srcFormat = srcFrame->GetFormat();
}
// We have references to the Thebes surfaces, so we don't need to leave
// We have references to the surfaces, so we don't need to leave
// the source frame (that we don't own) locked. We'll unlock the
// destination frame in ReleaseSurfaces(), below.
if (srcLocked) {
success = NS_SUCCEEDED(srcFrame->UnlockImageData()) && success;
}
success = success && srcSurface && dstSurface;
}
return success;
@ -272,8 +266,8 @@ public:
// These values may only be touched on the main thread.
WeakPtr<RasterImage> weakImage;
nsAutoPtr<imgFrame> dstFrame;
nsRefPtr<gfxImageSurface> srcSurface;
nsRefPtr<gfxImageSurface> dstSurface;
RefPtr<SourceSurface> srcSurface;
RefPtr<SourceSurface> dstSurface;
// Below are the values that may be touched on the scaling thread.
gfxSize scale;
@ -283,7 +277,7 @@ public:
gfxIntSize dstSize;
uint32_t srcStride;
uint32_t dstStride;
mozilla::gfx::SurfaceFormat srcFormat;
SurfaceFormat srcFormat;
bool dstLocked;
bool done;
// This boolean is accessed from both threads simultaneously without locking.
@ -335,7 +329,7 @@ public:
// outputs.
request->dstFrame = new imgFrame();
nsresult rv = request->dstFrame->Init(0, 0, request->dstSize.width, request->dstSize.height,
gfxImageFormat::ARGB32);
SurfaceFormat::B8G8R8A8);
if (NS_FAILED(rv) || !request->GetSurfaces(aSrcFrame)) {
return;
@ -701,10 +695,6 @@ RasterImage::GetDrawableImgFrame(uint32_t framenum)
if (frame && frame->GetCompositingFailed())
return nullptr;
if (frame) {
frame->ApplyDirtToSurfaces();
}
return frame;
}
@ -831,34 +821,31 @@ RasterImage::GetFirstFrameDelay()
return mFrameBlender.GetTimeoutForFrame(0);
}
nsresult
TemporaryRef<SourceSurface>
RasterImage::CopyFrame(uint32_t aWhichFrame,
uint32_t aFlags,
gfxImageSurface **_retval)
uint32_t aFlags)
{
if (aWhichFrame > FRAME_MAX_VALUE)
return NS_ERROR_INVALID_ARG;
return nullptr;
if (mError)
return NS_ERROR_FAILURE;
return nullptr;
// Disallowed in the API
if (mInDecoder && (aFlags & imgIContainer::FLAG_SYNC_DECODE))
return NS_ERROR_FAILURE;
return nullptr;
nsresult rv;
if (!ApplyDecodeFlags(aFlags, aWhichFrame))
return NS_ERROR_NOT_AVAILABLE;
return nullptr;
// If requested, synchronously flush any data we have lying around to the decoder
if (aFlags & FLAG_SYNC_DECODE) {
rv = SyncDecode();
CONTAINER_ENSURE_SUCCESS(rv);
CONTAINER_ENSURE_TRUE(NS_SUCCEEDED(rv), nullptr);
}
NS_ENSURE_ARG_POINTER(_retval);
// Get the frame. If it's not there, it's probably the caller's fault for
// not waiting for the data to be loaded from the network or not passing
// FLAG_SYNC_DECODE
@ -866,28 +853,43 @@ RasterImage::CopyFrame(uint32_t aWhichFrame,
0 : GetCurrentImgFrameIndex();
imgFrame *frame = GetDrawableImgFrame(frameIndex);
if (!frame) {
*_retval = nullptr;
return NS_ERROR_FAILURE;
return nullptr;
}
nsRefPtr<gfxPattern> pattern;
frame->GetPattern(getter_AddRefs(pattern));
nsIntRect intframerect = frame->GetRect();
gfxRect framerect(intframerect.x, intframerect.y, intframerect.width, intframerect.height);
// Create a 32-bit image surface of our size, but draw using the frame's
// rect, implicitly padding the frame out to the image's size.
nsRefPtr<gfxImageSurface> imgsurface = new gfxImageSurface(gfxIntSize(mSize.width, mSize.height),
gfxImageFormat::ARGB32);
gfxContext ctx(imgsurface);
ctx.SetOperator(gfxContext::OPERATOR_SOURCE);
ctx.Rectangle(framerect);
ctx.Translate(framerect.TopLeft());
ctx.SetPattern(pattern);
ctx.Fill();
imgsurface.forget(_retval);
return NS_OK;
IntSize size(mSize.width, mSize.height);
RefPtr<DataSourceSurface> surf =
Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
DataSourceSurface::MappedSurface mapping;
DebugOnly<bool> success =
surf->Map(DataSourceSurface::MapType::WRITE, &mapping);
NS_ASSERTION(success, "Failed to map surface");
RefPtr<DrawTarget> target =
Factory::CreateDrawTargetForData(BackendType::CAIRO,
mapping.mData,
size,
mapping.mStride,
SurfaceFormat::B8G8R8A8);
nsIntRect intframerect = frame->GetRect();
Rect rect(intframerect.x, intframerect.y,
intframerect.width, intframerect.height);
if (frame->IsSinglePixel()) {
target->FillRect(rect, ColorPattern(frame->SinglePixelColor()),
DrawOptions(1.0f, CompositionOp::OP_SOURCE));
} else {
RefPtr<SourceSurface> srcsurf = frame->GetSurface();
Rect srcrect(0, 0, intframerect.width, intframerect.height);
target->DrawSurface(srcsurf, srcrect, rect);
}
target->Flush();
surf->Unmap();
return surf;
}
//******************************************************************************
@ -928,7 +930,7 @@ RasterImage::GetFrame(uint32_t aWhichFrame,
return nullptr;
}
nsRefPtr<gfxASurface> framesurf;
RefPtr<SourceSurface> framesurf;
// If this frame covers the entire image, we can just reuse its existing
// surface.
@ -936,7 +938,7 @@ RasterImage::GetFrame(uint32_t aWhichFrame,
if (framerect.x == 0 && framerect.y == 0 &&
framerect.width == mSize.width &&
framerect.height == mSize.height) {
frame->GetSurface(getter_AddRefs(framesurf));
framesurf = frame->GetSurface();
if (!framesurf && !frame->IsSinglePixel()) {
// No reason to be optimized away here - the OS threw out the data
if (!(aFlags & FLAG_SYNC_DECODE))
@ -957,27 +959,10 @@ RasterImage::GetFrame(uint32_t aWhichFrame,
// The image doesn't have a surface because it's been optimized away. Create
// one.
if (!framesurf) {
nsRefPtr<gfxImageSurface> imgsurf;
CopyFrame(aWhichFrame, aFlags, getter_AddRefs(imgsurf));
framesurf = imgsurf;
framesurf = CopyFrame(aWhichFrame, aFlags);
}
RefPtr<SourceSurface> result;
// As far as Moz2D is concerned, SourceSurface contains premultiplied alpha.
// If we're abusing it to contain non-premultiplied alpha then we want to
// avoid having Moz2D do any conversions on it (like copy to another
// surface). Hence why we try to wrap framesurf's data here for
// FLAG_DECODE_NO_PREMULTIPLY_ALPHA.
if ((aFlags & FLAG_WANT_DATA_SURFACE) != 0 ||
(aFlags & FLAG_DECODE_NO_PREMULTIPLY_ALPHA) != 0) {
result = gfxPlatform::GetPlatform()->GetWrappedDataSourceSurface(framesurf);
}
if (!result) {
result = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(nullptr,
framesurf);
}
return result.forget();
return framesurf;
}
already_AddRefed<layers::Image>
@ -1182,7 +1167,7 @@ nsresult
RasterImage::InternalAddFrame(uint32_t framenum,
int32_t aX, int32_t aY,
int32_t aWidth, int32_t aHeight,
gfxImageFormat aFormat,
SurfaceFormat aFormat,
uint8_t aPaletteDepth,
uint8_t **imageData,
uint32_t *imageLength,
@ -1319,7 +1304,7 @@ RasterImage::SetSize(int32_t aWidth, int32_t aHeight, Orientation aOrientation)
nsresult
RasterImage::EnsureFrame(uint32_t aFrameNum, int32_t aX, int32_t aY,
int32_t aWidth, int32_t aHeight,
gfxImageFormat aFormat,
SurfaceFormat aFormat,
uint8_t aPaletteDepth,
uint8_t **imageData, uint32_t *imageLength,
uint32_t **paletteData, uint32_t *paletteLength,
@ -1394,7 +1379,7 @@ RasterImage::EnsureFrame(uint32_t aFrameNum, int32_t aX, int32_t aY,
nsresult
RasterImage::EnsureFrame(uint32_t aFramenum, int32_t aX, int32_t aY,
int32_t aWidth, int32_t aHeight,
gfxImageFormat aFormat,
SurfaceFormat aFormat,
uint8_t** imageData, uint32_t* imageLength,
imgFrame** aFrame)
{
@ -2112,7 +2097,7 @@ RasterImage::InitDecoder(bool aDoSizeDecode)
// frame. By default, we create an ARGB frame with no offset. If decoders
// need a different type, they need to ask for it themselves.
mDecoder->NeedNewFrame(0, 0, 0, mSize.width, mSize.height,
gfxImageFormat::ARGB32);
SurfaceFormat::B8G8R8A8);
mDecoder->AllocateFrame();
}
mDecoder->Init();
@ -2571,7 +2556,6 @@ RasterImage::ScalingDone(ScaleRequest* request, ScaleStatus status)
imgFrame *scaledFrame = request->dstFrame.forget();
scaledFrame->ImageUpdated(scaledFrame->GetRect());
scaledFrame->ApplyDirtToSurfaces();
if (mStatusTracker) {
mStatusTracker->FrameChanged(&request->srcRect);
@ -2609,7 +2593,7 @@ RasterImage::DrawWithPreDownscaleIfNeeded(imgFrame *aFrame,
imageSpaceToUserSpace.Invert();
gfxSize scale = imageSpaceToUserSpace.ScaleFactors(true);
nsIntRect subimage = aSubimage;
nsRefPtr<gfxASurface> surf;
RefPtr<SourceSurface> surf;
if (CanScale(aFilter, scale, aFlags) && !frame->IsSinglePixel()) {
// If scale factor is still the same that we scaled for and
@ -2623,7 +2607,7 @@ RasterImage::DrawWithPreDownscaleIfNeeded(imgFrame *aFrame,
bool needScaleReq;
if (mScaleResult.status == SCALE_DONE && mScaleResult.scale == scale) {
// Grab and hold the surface to make sure the OS didn't destroy it
mScaleResult.frame->GetSurface(getter_AddRefs(surf));
surf = mScaleResult.frame->GetSurface();
needScaleReq = !surf;
if (surf) {
frame = mScaleResult.frame;
@ -3098,8 +3082,6 @@ RasterImage::FinishedSomeDecoding(eShutdownIntent aIntent /* = eShutdownIntent_D
nsresult rv = NS_OK;
if (image->mDecoder) {
image->mDecoder->MarkFrameDirty();
if (request && request->mChunkCount && !image->mDecoder->IsSizeDecode()) {
Telemetry::Accumulate(Telemetry::IMAGE_DECODE_CHUNKS, request->mChunkCount);
}

View File

@ -206,7 +206,7 @@ public:
*/
nsresult EnsureFrame(uint32_t aFramenum, int32_t aX, int32_t aY,
int32_t aWidth, int32_t aHeight,
gfxImageFormat aFormat,
gfx::SurfaceFormat aFormat,
uint8_t aPaletteDepth,
uint8_t** imageData,
uint32_t* imageLength,
@ -220,7 +220,7 @@ public:
*/
nsresult EnsureFrame(uint32_t aFramenum, int32_t aX, int32_t aY,
int32_t aWidth, int32_t aHeight,
gfxImageFormat aFormat,
gfx::SurfaceFormat aFormat,
uint8_t** imageData,
uint32_t* imageLength,
imgFrame** aFrame);
@ -557,9 +557,8 @@ private:
const nsIntRect &aSubimage,
uint32_t aFlags);
nsresult CopyFrame(uint32_t aWhichFrame,
uint32_t aFlags,
gfxImageSurface **_retval);
TemporaryRef<gfx::SourceSurface> CopyFrame(uint32_t aWhichFrame,
uint32_t aFlags);
/**
* Deletes and nulls out the frame in mFrames[framenum].
@ -587,7 +586,7 @@ private:
uint32_t **paletteData, uint32_t *paletteLength,
imgFrame** aRetFrame);
nsresult InternalAddFrame(uint32_t framenum, int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight,
gfxImageFormat aFormat, uint8_t aPaletteDepth,
gfx::SurfaceFormat aFormat, uint8_t aPaletteDepth,
uint8_t **imageData, uint32_t *imageLength,
uint32_t **paletteData, uint32_t *paletteLength,
imgFrame** aRetFrame);

View File

@ -913,7 +913,7 @@ VectorImage::CreateDrawableAndShow(const SVGDrawingParameters& aParams)
ThebesIntRect(aParams.imageRect),
ThebesIntRect(aParams.imageRect),
ThebesIntRect(aParams.imageRect),
gfxImageFormat::ARGB32,
SurfaceFormat::B8G8R8A8,
GraphicsFilter::FILTER_NEAREST, aParams.flags);
// Attempt to cache the resulting surface.
@ -940,7 +940,7 @@ VectorImage::Show(gfxDrawable* aDrawable, const SVGDrawingParameters& aParams)
aParams.userSpaceToImageSpace,
aParams.subimage, aParams.sourceRect,
ThebesIntRect(aParams.imageRect), aParams.fill,
gfxImageFormat::ARGB32,
SurfaceFormat::B8G8R8A8,
aParams.filter, aParams.flags);
MOZ_ASSERT(mRenderingObserver, "Should have a rendering observer by now");

View File

@ -22,21 +22,13 @@ static bool gDisableOptimize = false;
#include "mozilla/MemoryReporting.h"
#include "nsMargin.h"
#include "mozilla/CheckedInt.h"
#if defined(XP_WIN)
#include "gfxWindowsPlatform.h"
/* Whether to use the windows surface; only for desktop win32 */
#define USE_WIN_SURFACE 1
#endif
#include "mozilla/gfx/Tools.h"
using namespace mozilla;
using namespace mozilla::gfx;
using namespace mozilla::image;
static cairo_user_data_key_t kVolatileBuffer;
static UserDataKey kVolatileBuffer;
static void
VolatileBufferRelease(void *vbuf)
@ -44,32 +36,31 @@ VolatileBufferRelease(void *vbuf)
delete static_cast<VolatileBufferPtr<unsigned char>*>(vbuf);
}
gfxImageSurface *
LockedImageSurface::CreateSurface(VolatileBuffer *vbuf,
const gfxIntSize& size,
gfxImageFormat format)
static TemporaryRef<DataSourceSurface>
CreateLockedSurface(VolatileBuffer *vbuf,
const IntSize& size,
SurfaceFormat format)
{
VolatileBufferPtr<unsigned char> *vbufptr =
new VolatileBufferPtr<unsigned char>(vbuf);
MOZ_ASSERT(!vbufptr->WasBufferPurged(), "Expected image data!");
long stride = gfxImageSurface::ComputeStride(size, format);
gfxImageSurface *img = new gfxImageSurface(*vbufptr, size, stride, format);
if (!img || img->CairoStatus()) {
delete img;
int32_t stride = size.width * BytesPerPixel(format);
RefPtr<DataSourceSurface> surf =
Factory::CreateWrappingDataSourceSurface(*vbufptr, stride, size, format);
if (!surf) {
delete vbufptr;
return nullptr;
}
img->SetData(&kVolatileBuffer, vbufptr, VolatileBufferRelease);
return img;
surf->AddUserData(&kVolatileBuffer, vbufptr, VolatileBufferRelease);
return surf;
}
TemporaryRef<VolatileBuffer>
LockedImageSurface::AllocateBuffer(const gfxIntSize& size,
gfxImageFormat format)
static TemporaryRef<VolatileBuffer>
AllocateBufferForImage(const IntSize& size, SurfaceFormat format)
{
long stride = gfxImageSurface::ComputeStride(size, format);
int32_t stride = size.width * BytesPerPixel(format);
RefPtr<VolatileBuffer> buf = new VolatileBuffer();
if (buf->Init(stride * size.height,
1 << gfxAlphaRecovery::GoodAlignmentLog2()))
@ -109,51 +100,19 @@ static bool AllowedImageSize(int32_t aWidth, int32_t aHeight)
return true;
}
// Returns whether we should, at this time, use image surfaces instead of
// optimized platform-specific surfaces.
static bool ShouldUseImageSurfaces()
{
#if defined(USE_WIN_SURFACE)
static const DWORD kGDIObjectsHighWaterMark = 7000;
if (gfxWindowsPlatform::GetPlatform()->GetRenderMode() ==
gfxWindowsPlatform::RENDER_DIRECT2D) {
return true;
}
// at 7000 GDI objects, stop allocating normal images to make sure
// we never hit the 10k hard limit.
// GetCurrentProcess() just returns (HANDLE)-1, it's inlined afaik
DWORD count = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS);
if (count == 0 ||
count > kGDIObjectsHighWaterMark)
{
// either something's broken (count == 0),
// or we hit our high water mark; disable
// image allocations for a bit.
return true;
}
#endif
return false;
}
imgFrame::imgFrame() :
mDecoded(0, 0, 0, 0),
mDirtyMutex("imgFrame::mDirty"),
mDecodedMutex("imgFrame::mDecoded"),
mPalettedImageData(nullptr),
mSinglePixelColor(0),
mTimeout(100),
mDisposalMethod(0), /* imgIContainer::kDisposeNotSpecified */
mLockCount(0),
mBlendMethod(1), /* imgIContainer::kBlendOver */
mSinglePixel(false),
mFormatChanged(false),
mCompositingFailed(false),
mNonPremult(false),
mDiscardable(false),
mInformedDiscardTracker(false),
mDirty(false)
mInformedDiscardTracker(false)
{
static bool hasCheckedOptimize = false;
if (!hasCheckedOptimize) {
@ -175,7 +134,7 @@ imgFrame::~imgFrame()
}
nsresult imgFrame::Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight,
gfxImageFormat aFormat, uint8_t aPaletteDepth /* = 0 */)
SurfaceFormat aFormat, uint8_t aPaletteDepth /* = 0 */)
{
// assert for properties that should be verified by decoders, warn for properties related to bad content
if (!AllowedImageSize(aWidth, aHeight)) {
@ -208,55 +167,22 @@ nsresult imgFrame::Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight,
NS_WARNING("Exceed the hard limit of decode image size");
return NS_ERROR_OUT_OF_MEMORY;
}
// For Windows, we must create the device surface first (if we're
// going to) so that the image surface can wrap it. Can't be done
// the other way around.
#ifdef USE_WIN_SURFACE
if (!ShouldUseImageSurfaces()) {
mWinSurface = new gfxWindowsSurface(gfxIntSize(mSize.width, mSize.height), mFormat);
if (mWinSurface && mWinSurface->CairoStatus() == 0) {
// no error
mImageSurface = mWinSurface->GetAsImageSurface();
} else {
mWinSurface = nullptr;
}
}
#endif
// For other platforms, space for the image surface is first allocated in
// a volatile buffer and then wrapped by a LockedImageSurface.
// This branch is also used on Windows if we're not using device surfaces
// or if we couldn't create one.
if (!mImageSurface) {
mVBuf = LockedImageSurface::AllocateBuffer(mSize, mFormat);
mVBuf = AllocateBufferForImage(mSize, mFormat);
if (!mVBuf) {
return NS_ERROR_OUT_OF_MEMORY;
}
mImageSurface = LockedImageSurface::CreateSurface(mVBuf, mSize, mFormat);
mImageSurface = CreateLockedSurface(mVBuf, mSize, mFormat);
}
if (!mImageSurface || mImageSurface->CairoStatus()) {
mImageSurface = nullptr;
// guess
if (!mImageSurface) {
NS_WARNING("Allocation of gfxImageSurface should succeed");
} else if (!mImageSurface->CairoStatus()) {
NS_WARNING("gfxImageSurface should have good CairoStatus");
}
if (!mImageSurface) {
NS_WARNING("Failed to create VolatileDataSourceSurface");
// Image surface allocation is failed, need to return
// the booked buffer size.
DiscardTracker::InformDeallocation(4 * mSize.width * mSize.height);
return NS_ERROR_OUT_OF_MEMORY;
}
mInformedDiscardTracker = true;
#ifdef XP_MACOSX
if (!ShouldUseImageSurfaces()) {
mQuartzSurface = new gfxQuartzImageSurface(mImageSurface);
}
#endif
}
return NS_OK;
@ -281,7 +207,7 @@ nsresult imgFrame::Optimize()
// this should always be true
if (mImageSurface->Stride() == mSize.width * 4) {
uint32_t *imgData = (uint32_t*) mImageSurface->Data();
uint32_t *imgData = (uint32_t*) ((uint8_t *)mVBufPtr);
uint32_t firstPixel = * (uint32_t*) imgData;
uint32_t pixelCount = mSize.width * mSize.height + 1;
@ -290,29 +216,22 @@ nsresult imgFrame::Optimize()
if (pixelCount == 0) {
// all pixels were the same
if (mFormat == gfxImageFormat::ARGB32 ||
mFormat == gfxImageFormat::RGB24)
{
// Should already be premult if desired.
gfxRGBA::PackedColorType inputType = gfxRGBA::PACKED_XRGB;
if (mFormat == gfxImageFormat::ARGB32)
inputType = gfxRGBA::PACKED_ARGB_PREMULTIPLIED;
mSinglePixelColor = gfxRGBA(firstPixel, inputType);
if (mFormat == SurfaceFormat::B8G8R8A8 ||
mFormat == SurfaceFormat::B8G8R8X8) {
mSinglePixel = true;
mSinglePixelColor.a = ((firstPixel >> 24) & 0xFF) * (1.0f / 255.0f);
mSinglePixelColor.r = ((firstPixel >> 16) & 0xFF) * (1.0f / 255.0f);
mSinglePixelColor.g = ((firstPixel >> 8) & 0xFF) * (1.0f / 255.0f);
mSinglePixelColor.b = ((firstPixel >> 0) & 0xFF) * (1.0f / 255.0f);
mSinglePixelColor.r /= mSinglePixelColor.a;
mSinglePixelColor.g /= mSinglePixelColor.a;
mSinglePixelColor.b /= mSinglePixelColor.a;
// blow away the older surfaces (if they exist), to release their memory
mVBuf = nullptr;
mVBufPtr = nullptr;
mImageSurface = nullptr;
mOptSurface = nullptr;
#ifdef USE_WIN_SURFACE
mWinSurface = nullptr;
#endif
#ifdef XP_MACOSX
mQuartzSurface = nullptr;
#endif
mDrawSurface = nullptr;
// We just dumped most of our allocated memory, so tell the discard
// tracker that we're not using any at all.
@ -328,94 +247,56 @@ nsresult imgFrame::Optimize()
// if it's not RGB24/ARGB32, don't optimize, but we never hit this at the moment
}
// if we're being forced to use image surfaces due to
// resource constraints, don't try to optimize beyond same-pixel.
if (ShouldUseImageSurfaces())
return NS_OK;
mOptSurface = nullptr;
#ifdef USE_WIN_SURFACE
if (mWinSurface) {
if (!mFormatChanged) {
// just use the DIB if the format has not changed
mOptSurface = mWinSurface;
}
}
#endif
#ifdef XP_MACOSX
if (mQuartzSurface) {
mQuartzSurface->Flush();
}
#endif
#ifdef ANDROID
gfxImageFormat optFormat =
gfxPlatform::GetPlatform()->
OptimalFormatForContent(gfxASurface::ContentFromFormat(mFormat));
SurfaceFormat optFormat =
gfxPlatform::GetPlatform()->Optimal2DFormatForContent(gfxContentType::COLOR);
if (optFormat == gfxImageFormat::RGB16_565) {
if (!GetHasAlpha() && optFormat == SurfaceFormat::R5G6B5) {
RefPtr<VolatileBuffer> buf =
LockedImageSurface::AllocateBuffer(mSize, optFormat);
AllocateBufferForImage(mSize, optFormat);
if (!buf)
return NS_OK;
nsRefPtr<gfxImageSurface> surf =
LockedImageSurface::CreateSurface(buf, mSize, optFormat);
RefPtr<DataSourceSurface> surf =
CreateLockedSurface(buf, mSize, optFormat);
if (!surf)
return NS_ERROR_OUT_OF_MEMORY;
gfxContext ctx(surf);
ctx.SetOperator(gfxContext::OPERATOR_SOURCE);
ctx.SetSource(mImageSurface);
ctx.Paint();
DataSourceSurface::MappedSurface mapping;
DebugOnly<bool> success =
surf->Map(DataSourceSurface::MapType::WRITE, &mapping);
NS_ASSERTION(success, "Failed to map surface");
RefPtr<DrawTarget> target =
Factory::CreateDrawTargetForData(BackendType::CAIRO,
mapping.mData,
mSize,
mapping.mStride,
optFormat);
Rect rect(0, 0, mSize.width, mSize.height);
target->DrawSurface(mImageSurface, rect, rect);
target->Flush();
surf->Unmap();
mImageSurface = surf;
mVBuf = buf;
mFormat = optFormat;
mDrawSurface = nullptr;
}
#else
if (mOptSurface == nullptr)
mOptSurface = gfxPlatform::GetPlatform()->OptimizeImage(mImageSurface, mFormat);
mOptSurface = gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget()->OptimizeSourceSurface(mImageSurface);
if (mOptSurface == mImageSurface)
mOptSurface = nullptr;
#endif
if (mOptSurface) {
mVBuf = nullptr;
mVBufPtr = nullptr;
mImageSurface = nullptr;
#ifdef USE_WIN_SURFACE
mWinSurface = nullptr;
#endif
#ifdef XP_MACOSX
mQuartzSurface = nullptr;
#endif
mDrawSurface = nullptr;
}
return NS_OK;
}
static void
DoSingleColorFastPath(gfxContext* aContext,
const gfxRGBA& aSinglePixelColor,
const gfxRect& aFill)
{
// if a == 0, it's a noop
if (aSinglePixelColor.a == 0.0)
return;
gfxContext::GraphicsOperator op = aContext->CurrentOperator();
if (op == gfxContext::OPERATOR_OVER && aSinglePixelColor.a == 1.0) {
aContext->SetOperator(gfxContext::OPERATOR_SOURCE);
}
aContext->SetDeviceColor(aSinglePixelColor);
aContext->NewPath();
aContext->Rectangle(aFill);
aContext->Fill();
aContext->SetOperator(op);
aContext->SetDeviceColor(gfxRGBA(0,0,0,0));
}
imgFrame::SurfaceWithFormat
imgFrame::SurfaceForDrawing(bool aDoPadding,
bool aDoPartialDecode,
@ -426,7 +307,7 @@ imgFrame::SurfaceForDrawing(bool aDoPadding,
gfxRect& aSubimage,
gfxRect& aSourceRect,
gfxRect& aImageRect,
gfxASurface* aSurface)
SourceSurface* aSurface)
{
IntSize size(int32_t(aImageRect.Width()), int32_t(aImageRect.Height()));
if (!aDoPadding && !aDoPartialDecode) {
@ -440,24 +321,33 @@ imgFrame::SurfaceForDrawing(bool aDoPadding,
// Create a temporary surface.
// Give this surface an alpha channel because there are
// transparent pixels in the padding or undecoded area
gfxImageFormat format = gfxImageFormat::ARGB32;
nsRefPtr<gfxASurface> surface =
gfxPlatform::GetPlatform()->CreateOffscreenSurface(size, gfxImageSurface::ContentFromFormat(format));
if (!surface || surface->CairoStatus())
RefPtr<DrawTarget> target =
gfxPlatform::GetPlatform()->
CreateOffscreenContentDrawTarget(size, SurfaceFormat::B8G8R8A8);
if (!target)
return SurfaceWithFormat();
Rect fillRect(aFill.x, aFill.y, aFill.width, aFill.height);
// Fill 'available' with whatever we've got
gfxContext tmpCtx(surface);
tmpCtx.SetOperator(gfxContext::OPERATOR_SOURCE);
if (mSinglePixel) {
tmpCtx.SetDeviceColor(mSinglePixelColor);
target->FillRect(fillRect, ColorPattern(mSinglePixelColor),
DrawOptions(1.0f, CompositionOp::OP_SOURCE));
} else {
tmpCtx.SetSource(aSurface, gfxPoint(aPadding.left, aPadding.top));
gfxMatrix imageSpaceToUserSpace = aUserSpaceToImageSpace;
imageSpaceToUserSpace.Invert();
SurfacePattern pattern(aSurface,
ExtendMode::REPEAT,
Matrix(imageSpaceToUserSpace.xx,
imageSpaceToUserSpace.xy,
imageSpaceToUserSpace.yx,
imageSpaceToUserSpace.yy,
imageSpaceToUserSpace.x0,
imageSpaceToUserSpace.y0));
target->FillRect(fillRect, pattern);
}
tmpCtx.Rectangle(available);
tmpCtx.Fill();
return SurfaceWithFormat(new gfxSurfaceDrawable(surface, ThebesIntSize(size)), format);
RefPtr<SourceSurface> newsurf = target->Snapshot();
return SurfaceWithFormat(new gfxSurfaceDrawable(newsurf, ThebesIntSize(size)), target->GetFormat());
}
// Not tiling, and we have a surface, so we can account for
@ -493,8 +383,16 @@ bool imgFrame::Draw(gfxContext *aContext, GraphicsFilter aFilter,
bool doPadding = aPadding != nsIntMargin(0,0,0,0);
bool doPartialDecode = !ImageComplete();
RefPtr<DrawTarget> dt = aContext->GetDrawTarget();
if (mSinglePixel && !doPadding && !doPartialDecode) {
DoSingleColorFastPath(aContext, mSinglePixelColor, aFill);
if (mSinglePixelColor.a == 0.0) {
return true;
}
Rect target(aFill.x, aFill.y, aFill.width, aFill.height);
dt->FillRect(target, ColorPattern(mSinglePixelColor),
DrawOptions(1.0f, CompositionOpForOp(aContext->CurrentOperator())));
return true;
}
@ -508,28 +406,9 @@ bool imgFrame::Draw(gfxContext *aContext, GraphicsFilter aFilter,
NS_ASSERTION(!sourceRect.Intersect(subimage).IsEmpty(),
"We must be allowed to sample *some* source pixels!");
nsRefPtr<gfxASurface> surf = CachedThebesSurface();
VolatileBufferPtr<unsigned char> ref(mVBuf);
if (!mSinglePixel && !surf) {
if (ref.WasBufferPurged()) {
return false;
}
surf = mDrawSurface;
if (!surf) {
long stride = gfxImageSurface::ComputeStride(mSize, mFormat);
nsRefPtr<gfxImageSurface> imgSurf =
new gfxImageSurface(ref, mSize, stride, mFormat);
#if defined(XP_MACOSX)
surf = mDrawSurface = new gfxQuartzImageSurface(imgSurf);
#else
surf = mDrawSurface = imgSurf;
#endif
}
if (!surf || surf->CairoStatus()) {
mDrawSurface = nullptr;
return true;
}
RefPtr<SourceSurface> surf = GetSurface();
if (!surf) {
return false;
}
bool doTile = !imageRect.Contains(sourceRect) &&
@ -551,32 +430,24 @@ bool imgFrame::Draw(gfxContext *aContext, GraphicsFilter aFilter,
// This can be called from any thread, but not simultaneously.
nsresult imgFrame::ImageUpdated(const nsIntRect &aUpdateRect)
{
MutexAutoLock lock(mDirtyMutex);
MutexAutoLock lock(mDecodedMutex);
mDecoded.UnionRect(mDecoded, aUpdateRect);
// clamp to bounds, in case someone sends a bogus updateRect (I'm looking at
// you, gif decoder)
nsIntRect boundsRect(mOffset, mSize);
nsIntRect boundsRect(mOffset, nsIntSize(mSize.width, mSize.height));
mDecoded.IntersectRect(mDecoded, boundsRect);
mDirty = true;
return NS_OK;
}
bool imgFrame::GetIsDirty() const
{
MutexAutoLock lock(mDirtyMutex);
return mDirty;
}
nsIntRect imgFrame::GetRect() const
{
return nsIntRect(mOffset, mSize);
return nsIntRect(mOffset, nsIntSize(mSize.width, mSize.height));
}
gfxImageFormat imgFrame::GetFormat() const
SurfaceFormat imgFrame::GetFormat() const
{
return mFormat;
}
@ -584,22 +455,17 @@ gfxImageFormat imgFrame::GetFormat() const
bool imgFrame::GetNeedsBackground() const
{
// We need a background painted if we have alpha or we're incomplete.
return (mFormat == gfxImageFormat::ARGB32 || !ImageComplete());
return (mFormat == SurfaceFormat::B8G8R8A8 || !ImageComplete());
}
uint32_t imgFrame::GetImageBytesPerRow() const
{
if (mImageSurface)
return mImageSurface->Stride();
if (mVBuf)
return gfxImageSurface::ComputeStride(mSize, mFormat);
return mSize.width * BytesPerPixel(mFormat);
if (mPaletteDepth)
return mSize.width;
NS_ERROR("GetImageBytesPerRow called with mImageSurface == null, mVBuf == null and mPaletteDepth == 0");
return 0;
}
@ -613,7 +479,7 @@ void imgFrame::GetImageData(uint8_t **aData, uint32_t *length) const
NS_ABORT_IF_FALSE(mLockCount != 0, "Can't GetImageData unless frame is locked");
if (mImageSurface)
*aData = mImageSurface->Data();
*aData = mVBufPtr;
else if (mPalettedImageData)
*aData = mPalettedImageData + PaletteDataLength();
else
@ -637,7 +503,7 @@ bool imgFrame::GetIsPaletted() const
bool imgFrame::GetHasAlpha() const
{
return mFormat == gfxImageFormat::ARGB32;
return mFormat == SurfaceFormat::B8G8R8A8;
}
void imgFrame::GetPaletteData(uint32_t **aPalette, uint32_t *length) const
@ -687,66 +553,59 @@ nsresult imgFrame::LockImageData()
if (ref.WasBufferPurged())
return NS_ERROR_FAILURE;
mImageSurface = LockedImageSurface::CreateSurface(mVBuf, mSize, mFormat);
if (!mImageSurface || mImageSurface->CairoStatus())
mImageSurface = CreateLockedSurface(mVBuf, mSize, mFormat);
if (!mImageSurface)
return NS_ERROR_OUT_OF_MEMORY;
}
if (mOptSurface || mSinglePixel || mFormat == gfxImageFormat::RGB16_565) {
gfxImageFormat format = mFormat;
if (mFormat == gfxImageFormat::RGB16_565)
format = gfxImageFormat::ARGB32;
if (mOptSurface || mSinglePixel || mFormat == SurfaceFormat::R5G6B5) {
SurfaceFormat format = mFormat;
if (mFormat == SurfaceFormat::R5G6B5)
format = SurfaceFormat::B8G8R8A8;
// Recover the pixels
RefPtr<VolatileBuffer> buf =
LockedImageSurface::AllocateBuffer(mSize, format);
AllocateBufferForImage(mSize, format);
if (!buf) {
return NS_ERROR_OUT_OF_MEMORY;
}
RefPtr<gfxImageSurface> surf =
LockedImageSurface::CreateSurface(buf, mSize, mFormat);
if (!surf || surf->CairoStatus())
RefPtr<DataSourceSurface> surf =
CreateLockedSurface(buf, mSize, format);
if (!surf)
return NS_ERROR_OUT_OF_MEMORY;
gfxContext context(surf);
context.SetOperator(gfxContext::OPERATOR_SOURCE);
DataSourceSurface::MappedSurface mapping;
DebugOnly<bool> success =
surf->Map(DataSourceSurface::MapType::WRITE, &mapping);
NS_ASSERTION(success, "Failed to map surface");
RefPtr<DrawTarget> target =
Factory::CreateDrawTargetForData(BackendType::CAIRO,
mapping.mData,
mSize,
mapping.mStride,
format);
Rect rect(0, 0, mSize.width, mSize.height);
if (mSinglePixel)
context.SetDeviceColor(mSinglePixelColor);
else if (mFormat == gfxImageFormat::RGB16_565)
context.SetSource(mImageSurface);
target->FillRect(rect, ColorPattern(mSinglePixelColor),
DrawOptions(1.0f, CompositionOp::OP_SOURCE));
else if (mFormat == SurfaceFormat::R5G6B5)
target->DrawSurface(mImageSurface, rect, rect);
else
context.SetSource(mOptSurface);
context.Paint();
target->DrawSurface(mOptSurface, rect, rect,
DrawSurfaceOptions(),
DrawOptions(1.0f, CompositionOp::OP_SOURCE));
target->Flush();
surf->Unmap();
mFormat = format;
mVBuf = buf;
mImageSurface = surf;
mOptSurface = nullptr;
#ifdef USE_WIN_SURFACE
mWinSurface = nullptr;
#endif
#ifdef XP_MACOSX
mQuartzSurface = nullptr;
#endif
}
}
// We might write to the bits in this image surface, so we need to make the
// surface ready for that.
if (mImageSurface)
mImageSurface->Flush();
#ifdef USE_WIN_SURFACE
if (mWinSurface)
mWinSurface->Flush();
#endif
#ifdef XP_MACOSX
if (!mQuartzSurface && !ShouldUseImageSurfaces()) {
mQuartzSurface = new gfxQuartzImageSurface(mImageSurface);
}
#endif
mVBufPtr = mVBuf;
return NS_OK;
}
@ -775,79 +634,14 @@ nsresult imgFrame::UnlockImageData()
if (mPalettedImageData)
return NS_OK;
// FIXME: Bug 795737
// If this image has been drawn since we were locked, it has had snapshots
// added, and we need to remove them before calling MarkDirty.
if (mImageSurface)
mImageSurface->Flush();
#ifdef USE_WIN_SURFACE
if (mWinSurface)
mWinSurface->Flush();
#endif
// Assume we've been written to.
if (mImageSurface)
mImageSurface->MarkDirty();
#ifdef USE_WIN_SURFACE
if (mWinSurface)
mWinSurface->MarkDirty();
#endif
#ifdef XP_MACOSX
// The quartz image surface (ab)uses the flush method to get the
// cairo_image_surface data into a CGImage, so we have to call Flush() here.
if (mQuartzSurface)
mQuartzSurface->Flush();
#endif
mVBufPtr = nullptr;
if (mVBuf && mDiscardable) {
mImageSurface = nullptr;
#ifdef XP_MACOSX
mQuartzSurface = nullptr;
#endif
}
return NS_OK;
}
void imgFrame::ApplyDirtToSurfaces()
{
MOZ_ASSERT(NS_IsMainThread());
MutexAutoLock lock(mDirtyMutex);
if (mDirty) {
// FIXME: Bug 795737
// If this image has been drawn since we were locked, it has had snapshots
// added, and we need to remove them before calling MarkDirty.
if (mImageSurface)
mImageSurface->Flush();
#ifdef USE_WIN_SURFACE
if (mWinSurface)
mWinSurface->Flush();
#endif
if (mImageSurface)
mImageSurface->MarkDirty();
#ifdef USE_WIN_SURFACE
if (mWinSurface)
mWinSurface->MarkDirty();
#endif
#ifdef XP_MACOSX
// The quartz image surface (ab)uses the flush method to get the
// cairo_image_surface data into a CGImage, so we have to call Flush() here.
if (mQuartzSurface)
mQuartzSurface->Flush();
#endif
mDirty = false;
}
}
void imgFrame::SetDiscardable()
{
MOZ_ASSERT(mLockCount, "Expected to be locked when SetDiscardable is called");
@ -857,6 +651,29 @@ void imgFrame::SetDiscardable()
#endif
}
TemporaryRef<SourceSurface>
imgFrame::GetSurface()
{
if (mOptSurface) {
if (mOptSurface->IsValid())
return mOptSurface;
else
mOptSurface = nullptr;
}
if (mImageSurface)
return mImageSurface;
if (!mVBuf)
return nullptr;
VolatileBufferPtr<char> buf(mVBuf);
if (buf.WasBufferPurged())
return nullptr;
return CreateLockedSurface(mVBuf, mSize, mFormat);
}
int32_t imgFrame::GetRawTimeout() const
{
return mTimeout;
@ -890,9 +707,10 @@ void imgFrame::SetBlendMethod(int32_t aBlendMethod)
// This can be called from any thread.
bool imgFrame::ImageComplete() const
{
MutexAutoLock lock(mDirtyMutex);
MutexAutoLock lock(mDecodedMutex);
return mDecoded.IsEqualInterior(nsIntRect(mOffset, mSize));
return mDecoded.IsEqualInterior(nsIntRect(mOffset.x, mOffset.y,
mSize.width, mSize.height));
}
// A hint from the image decoders that this image has no alpha, even
@ -903,10 +721,8 @@ bool imgFrame::ImageComplete() const
// set to 0xff.
void imgFrame::SetHasNoAlpha()
{
if (mFormat == gfxImageFormat::ARGB32) {
mFormat = gfxImageFormat::RGB24;
mFormatChanged = true;
ThebesSurface()->SetOpaqueRect(gfxRect(0, 0, mSize.width, mSize.height));
if (mFormat == SurfaceFormat::B8G8R8A8) {
mFormat = SurfaceFormat::B8G8R8X8;
}
}
@ -948,25 +764,11 @@ imgFrame::SizeOfExcludingThisWithComputedFallbackIfHeap(gfxMemoryLocation aLocat
n += n2;
}
#ifdef USE_WIN_SURFACE
if (mWinSurface && aLocation == mWinSurface->GetMemoryLocation()) {
n += mWinSurface->KnownMemoryUsed();
} else
#endif
#ifdef XP_MACOSX
if (mQuartzSurface && aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) {
n += aMallocSizeOf(mQuartzSurface);
if (mImageSurface && aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) {
n += aMallocSizeOf(mImageSurface);
}
#endif
if (mImageSurface && aLocation == mImageSurface->GetMemoryLocation()) {
size_t n2 = 0;
if (aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) { // HEAP: measure
n2 = mImageSurface->SizeOfIncludingThis(aMallocSizeOf);
}
if (n2 == 0) { // non-HEAP or computed fallback for HEAP
n2 = mImageSurface->KnownMemoryUsed();
}
n += n2;
if (mOptSurface && aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) {
n += aMallocSizeOf(mOptSurface);
}
if (mVBuf && aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) {
@ -978,18 +780,5 @@ imgFrame::SizeOfExcludingThisWithComputedFallbackIfHeap(gfxMemoryLocation aLocat
n += mVBuf->NonHeapSizeOfExcludingThis();
}
if (mOptSurface && aLocation == mOptSurface->GetMemoryLocation()) {
size_t n2 = 0;
if (aLocation == gfxMemoryLocation::IN_PROCESS_HEAP &&
mOptSurface->SizeOfIsMeasured()) {
// HEAP: measure (but only if the sub-class is capable of measuring)
n2 = mOptSurface->SizeOfIncludingThis(aMallocSizeOf);
}
if (n2 == 0) { // non-HEAP or computed fallback for HEAP
n2 = mOptSurface->KnownMemoryUsed();
}
n += n2;
}
return n;
}

View File

@ -10,43 +10,22 @@
#include "mozilla/MemoryReporting.h"
#include "mozilla/Mutex.h"
#include "mozilla/VolatileBuffer.h"
#include "nsRect.h"
#include "nsPoint.h"
#include "nsSize.h"
#include "gfxPattern.h"
#include "gfxDrawable.h"
#include "gfxImageSurface.h"
#if defined(XP_WIN)
#include "gfxWindowsSurface.h"
#elif defined(XP_MACOSX)
#include "gfxQuartzImageSurface.h"
#endif
#include "nsAutoPtr.h"
#include "imgIContainer.h"
#include "gfxColor.h"
/*
* This creates a gfxImageSurface which will unlock the buffer on destruction
*/
class LockedImageSurface
{
public:
static gfxImageSurface *
CreateSurface(mozilla::VolatileBuffer *vbuf,
const gfxIntSize& size,
gfxImageFormat format);
static mozilla::TemporaryRef<mozilla::VolatileBuffer>
AllocateBuffer(const gfxIntSize& size, gfxImageFormat format);
};
class imgFrame
{
typedef mozilla::gfx::Color Color;
typedef mozilla::gfx::DataSourceSurface DataSourceSurface;
typedef mozilla::gfx::IntSize IntSize;
typedef mozilla::gfx::SourceSurface SourceSurface;
typedef mozilla::gfx::SurfaceFormat SurfaceFormat;
public:
imgFrame();
~imgFrame();
nsresult Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight, gfxImageFormat aFormat, uint8_t aPaletteDepth = 0);
nsresult Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight, mozilla::gfx::SurfaceFormat aFormat, uint8_t aPaletteDepth = 0);
nsresult Optimize();
bool Draw(gfxContext *aContext, GraphicsFilter aFilter,
@ -55,10 +34,9 @@ public:
uint32_t aImageFlags = imgIContainer::FLAG_NONE);
nsresult ImageUpdated(const nsIntRect &aUpdateRect);
bool GetIsDirty() const;
nsIntRect GetRect() const;
gfxImageFormat GetFormat() const;
mozilla::gfx::SurfaceFormat GetFormat() const;
bool GetNeedsBackground() const;
uint32_t GetImageBytesPerRow() const;
uint32_t GetImageDataLength() const;
@ -86,25 +64,15 @@ public:
nsresult LockImageData();
nsresult UnlockImageData();
void ApplyDirtToSurfaces();
void SetDiscardable();
nsresult GetSurface(gfxASurface **aSurface)
{
*aSurface = ThebesSurface();
NS_IF_ADDREF(*aSurface);
return NS_OK;
}
mozilla::TemporaryRef<SourceSurface> GetSurface();
nsresult GetPattern(gfxPattern **aPattern)
Color
SinglePixelColor()
{
if (mSinglePixel)
*aPattern = new gfxPattern(mSinglePixelColor);
else
*aPattern = new gfxPattern(ThebesSurface());
NS_ADDREF(*aPattern);
return NS_OK;
return mSinglePixelColor;
}
bool IsSinglePixel()
@ -112,52 +80,7 @@ public:
return mSinglePixel;
}
gfxASurface* CachedThebesSurface()
{
if (mOptSurface)
return mOptSurface;
#if defined(XP_WIN)
if (mWinSurface)
return mWinSurface;
#elif defined(XP_MACOSX)
if (mQuartzSurface)
return mQuartzSurface;
#endif
if (mImageSurface)
return mImageSurface;
return nullptr;
}
gfxASurface* ThebesSurface()
{
gfxASurface *sur = CachedThebesSurface();
if (sur)
return sur;
if (mVBuf) {
mozilla::VolatileBufferPtr<uint8_t> ref(mVBuf);
if (ref.WasBufferPurged())
return nullptr;
gfxImageSurface *imgSur =
LockedImageSurface::CreateSurface(mVBuf, mSize, mFormat);
#if defined(XP_MACOSX)
// Manually addref and release to make sure the cairo surface isn't lost
NS_ADDREF(imgSur);
gfxQuartzImageSurface *quartzSur = new gfxQuartzImageSurface(imgSur);
// quartzSur does not hold on to the gfxImageSurface
NS_RELEASE(imgSur);
return quartzSur;
#else
return imgSur;
#endif
}
// We can return null here if we're single pixel optimized
// or a paletted image. However, one has to check for paletted
// image data first before attempting to get a surface, so
// this is only valid for single pixel optimized images
MOZ_ASSERT(mSinglePixel, "No image surface and not a single pixel!");
return nullptr;
}
mozilla::TemporaryRef<SourceSurface> CachedSurface();
size_t SizeOfExcludingThisWithComputedFallbackIfHeap(
gfxMemoryLocation aLocation,
@ -175,9 +98,9 @@ private: // methods
struct SurfaceWithFormat {
nsRefPtr<gfxDrawable> mDrawable;
gfxImageFormat mFormat;
SurfaceFormat mFormat;
SurfaceWithFormat() {}
SurfaceWithFormat(gfxDrawable* aDrawable, gfxImageFormat aFormat)
SurfaceWithFormat(gfxDrawable* aDrawable, SurfaceFormat aFormat)
: mDrawable(aDrawable), mFormat(aFormat) {}
bool IsValid() { return !!mDrawable; }
};
@ -191,25 +114,18 @@ private: // methods
gfxRect& aSubimage,
gfxRect& aSourceRect,
gfxRect& aImageRect,
gfxASurface* aSurface);
SourceSurface* aSurface);
private: // data
nsRefPtr<gfxImageSurface> mImageSurface;
nsRefPtr<gfxASurface> mOptSurface;
#if defined(XP_WIN)
nsRefPtr<gfxWindowsSurface> mWinSurface;
#elif defined(XP_MACOSX)
nsRefPtr<gfxQuartzImageSurface> mQuartzSurface;
#endif
mozilla::RefPtr<DataSourceSurface> mImageSurface;
mozilla::RefPtr<SourceSurface> mOptSurface;
nsRefPtr<gfxASurface> mDrawSurface;
nsIntSize mSize;
IntSize mSize;
nsIntPoint mOffset;
nsIntRect mDecoded;
mutable mozilla::Mutex mDirtyMutex;
mutable mozilla::Mutex mDecodedMutex;
// The palette and image data for images that are paletted, since Cairo
// doesn't support these images.
@ -217,8 +133,8 @@ private: // data
// Total length is PaletteDataLength() + GetImageDataLength().
uint8_t* mPalettedImageData;
// Note that the data stored in gfxRGBA is *non-alpha-premultiplied*.
gfxRGBA mSinglePixelColor;
// Note that the data stored in gfx::Color is *non-alpha-premultiplied*.
Color mSinglePixelColor;
int32_t mTimeout; // -1 means display forever
int32_t mDisposalMethod;
@ -227,20 +143,18 @@ private: // data
int32_t mLockCount;
mozilla::RefPtr<mozilla::VolatileBuffer> mVBuf;
mozilla::VolatileBufferPtr<uint8_t> mVBufPtr;
gfxImageFormat mFormat;
SurfaceFormat mFormat;
uint8_t mPaletteDepth;
int8_t mBlendMethod;
bool mSinglePixel;
bool mFormatChanged;
bool mCompositingFailed;
bool mNonPremult;
bool mDiscardable;
/** Have we called DiscardTracker::InformAllocation()? */
bool mInformedDiscardTracker;
bool mDirty;
};
namespace mozilla {

View File

@ -232,10 +232,7 @@ typedef bool
(* PropertyAttributesOp)(JSContext *cx, JS::HandleObject obj, JS::Handle<PropertyName*> name,
unsigned *attrsp);
typedef bool
(* DeletePropertyOp)(JSContext *cx, JS::HandleObject obj, JS::Handle<PropertyName*> name,
bool *succeeded);
typedef bool
(* DeleteElementOp)(JSContext *cx, JS::HandleObject obj, uint32_t index, bool *succeeded);
(* DeleteGenericOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id, bool *succeeded);
typedef bool
(* WatchOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject callable);
@ -342,20 +339,18 @@ struct ObjectOps
StrictElementIdOp setElement;
GenericAttributesOp getGenericAttributes;
GenericAttributesOp setGenericAttributes;
DeletePropertyOp deleteProperty;
DeleteElementOp deleteElement;
DeleteGenericOp deleteGeneric;
WatchOp watch;
UnwatchOp unwatch;
SliceOp slice; // Optimized slice, can be null.
JSNewEnumerateOp enumerate;
ObjectOp thisObject;
};
#define JS_NULL_OBJECT_OPS \
{nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr, \
nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr, \
nullptr,nullptr}
{nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, \
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, \
nullptr, nullptr, nullptr}
} // namespace js
@ -366,7 +361,7 @@ typedef void (*JSClassInternal)();
struct JSClass {
JS_CLASS_MEMBERS(JSFinalizeOp);
void *reserved[32];
void *reserved[31];
};
#define JSCLASS_HAS_PRIVATE (1<<0) // objects have private slot

View File

@ -42,7 +42,7 @@ class MOZ_STACK_CLASS SourceBufferHolder;
class HandleValueArray;
class JS_PUBLIC_API(AutoCheckCannotGC);
class AutoCheckCannotGC;
}

View File

@ -660,6 +660,82 @@ function ArrayKeys() {
return CreateArrayIterator(this, ITEM_KIND_KEY);
}
/* ES6 rev 25 (2014 May 22) 22.1.2.1 */
function ArrayFrom(arrayLike, mapfn=undefined, thisArg=undefined) {
// Step 1.
var C = this;
// Steps 2-3.
var items = ToObject(arrayLike);
// Steps 4-5.
var mapping = (mapfn !== undefined);
if (mapping && !IsCallable(mapfn))
ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(1, mapfn));
// All elements defined by this algorithm have the same attrs:
var attrs = ATTR_CONFIGURABLE | ATTR_ENUMERABLE | ATTR_WRITABLE;
// Steps 6-8.
var usingIterator = items["@@iterator"];
if (usingIterator !== undefined) {
// Steps 8.a-c.
var A = IsConstructor(C) ? new C() : [];
// Steps 8.d-e.
var iterator = callFunction(usingIterator, items);
// Step 8.f.
var k = 0;
// Steps 8.g.i-vi.
// These steps cannot be implemented using a for-of loop.
// See <https://bugs.ecmascript.org/show_bug.cgi?id=2883>.
var next;
while (true) {
// Steps 8.g.ii-vi.
next = iterator.next();
if (!IsObject(next))
ThrowError(JSMSG_NEXT_RETURNED_PRIMITIVE);
if (next.done)
break; // Substeps of 8.g.iv are implemented below.
var nextValue = next.value;
// Steps 8.g.vii-viii.
var mappedValue = mapping ? callFunction(mapfn, thisArg, nextValue, k) : nextValue;
// Steps 8.g.ix-xi.
_DefineDataProperty(A, k++, mappedValue, attrs);
}
} else {
// Step 9 is an assertion: items is not an Iterator. Testing this is
// literally the very last thing we did, so we don't assert here.
// Steps 10-12.
// FIXME: Array operations should use ToLength (bug 924058).
var len = ToInteger(items.length);
// Steps 13-15.
var A = IsConstructor(C) ? new C(len) : NewDenseArray(len);
// Steps 16-17.
for (var k = 0; k < len; k++) {
// Steps 17.a-c.
var kValue = items[k];
// Steps 17.d-e.
var mappedValue = mapping ? callFunction(mapfn, thisArg, kValue, k) : kValue;
// Steps 17.f-g.
_DefineDataProperty(A, k, mappedValue, attrs);
}
}
// Steps 8.g.iv.1-3 and 18-20 are the same.
A.length = k;
return A;
}
#ifdef ENABLE_PARALLEL_JS
/*

View File

@ -816,11 +816,11 @@ function SupportedLocales(availableLocales, requestedLocales, options) {
// Step 4.
for (var i = 0; i < subset.length; i++) {
_DefineValueProperty(subset, i, subset[i],
ATTR_ENUMERABLE | ATTR_NONCONFIGURABLE | ATTR_NONWRITABLE);
_DefineDataProperty(subset, i, subset[i],
ATTR_ENUMERABLE | ATTR_NONCONFIGURABLE | ATTR_NONWRITABLE);
}
_DefineValueProperty(subset, "length", subset.length,
ATTR_NONENUMERABLE | ATTR_NONCONFIGURABLE | ATTR_NONWRITABLE);
_DefineDataProperty(subset, "length", subset.length,
ATTR_NONENUMERABLE | ATTR_NONCONFIGURABLE | ATTR_NONWRITABLE);
// Step 5.
return subset;
@ -896,7 +896,7 @@ function GetNumberOption(options, property, minimum, maximum, fallback) {
* to avoid interference from setters on Object.prototype.
*/
function defineProperty(o, p, v) {
_DefineValueProperty(o, p, v, ATTR_ENUMERABLE | ATTR_CONFIGURABLE | ATTR_WRITABLE);
_DefineDataProperty(o, p, v, ATTR_ENUMERABLE | ATTR_CONFIGURABLE | ATTR_WRITABLE);
}

View File

@ -371,7 +371,7 @@ DefineAccessor(JSContext *cx, unsigned argc, Value *vp)
if (!BoxNonStrictThis(cx, args))
return false;
if (args.length() < 2 || !js_IsCallable(args[1])) {
if (args.length() < 2 || !IsCallable(args[1])) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
JSMSG_BAD_GETTER_OR_SETTER,
Type == GetterAccessor ? js_getter_str : js_setter_str);

View File

@ -2093,10 +2093,8 @@ TypedObject::obj_setGenericAttributes(JSContext *cx, HandleObject obj,
}
bool
TypedObject::obj_deleteProperty(JSContext *cx, HandleObject obj,
HandlePropertyName name, bool *succeeded)
TypedObject::obj_deleteGeneric(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded)
{
Rooted<jsid> id(cx, NameToId(name));
if (IsOwnId(cx, obj, id))
return ReportPropertyError(cx, JSMSG_CANT_DELETE, id);
@ -2106,27 +2104,7 @@ TypedObject::obj_deleteProperty(JSContext *cx, HandleObject obj,
return true;
}
return JSObject::deleteProperty(cx, proto, name, succeeded);
}
bool
TypedObject::obj_deleteElement(JSContext *cx, HandleObject obj, uint32_t index,
bool *succeeded)
{
RootedId id(cx);
if (!IndexToId(cx, index, &id))
return false;
if (IsOwnId(cx, obj, id))
return ReportPropertyError(cx, JSMSG_CANT_DELETE, id);
RootedObject proto(cx, obj->getProto());
if (!proto) {
*succeeded = false;
return true;
}
return JSObject::deleteElement(cx, proto, index, succeeded);
return JSObject::deleteGeneric(cx, proto, id, succeeded);
}
bool
@ -2287,8 +2265,7 @@ const Class TransparentTypedObject::class_ = {
TypedObject::obj_setElement,
TypedObject::obj_getGenericAttributes,
TypedObject::obj_setGenericAttributes,
TypedObject::obj_deleteProperty,
TypedObject::obj_deleteElement,
TypedObject::obj_deleteGeneric,
nullptr, nullptr, // watch/unwatch
nullptr, /* slice */
TypedObject::obj_enumerate,
@ -2633,8 +2610,7 @@ const Class OpaqueTypedObject::class_ = {
TypedObject::obj_setElement,
TypedObject::obj_getGenericAttributes,
TypedObject::obj_setGenericAttributes,
TypedObject::obj_deleteProperty,
TypedObject::obj_deleteElement,
TypedObject::obj_deleteGeneric,
nullptr, nullptr, // watch/unwatch
nullptr, // slice
TypedObject::obj_enumerate,

View File

@ -576,10 +576,7 @@ class TypedObject : public ArrayBufferViewObject
static bool obj_setGenericAttributes(JSContext *cx, HandleObject obj,
HandleId id, unsigned *attrsp);
static bool obj_deleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
bool *succeeded);
static bool obj_deleteElement(JSContext *cx, HandleObject obj, uint32_t index,
bool *succeeded);
static bool obj_deleteGeneric(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded);
static bool obj_enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op,
MutableHandleValue statep, MutableHandleId idp);

View File

@ -28,8 +28,13 @@
// Remove unsafe builtin functions.
Object.defineProperty = null; // See bug 988416.
// Cache builtin functions so using them doesn't require cloning the whole object they're
// Cache builtin functions so using them doesn't require cloning the whole object they're
// installed on.
//
// WARNING: Do not make std_ references to builtin constructors (like Array and
// Object) below. Setting `var std_Array = Array;`, for instance, would cause
// the entire Array constructor, including its prototype and methods, to be
// cloned into content compartments.
var std_isFinite = isFinite;
var std_isNaN = isNaN;
var std_Array_indexOf = ArrayIndexOf;

View File

@ -265,6 +265,18 @@ Zone::hasMarkedCompartments()
return false;
}
bool
Zone::canCollect()
{
// Zones cannot be collected while in use by other threads.
if (usedByExclusiveThread)
return false;
JSRuntime *rt = runtimeFromAnyThread();
if (rt->isAtomsZone(this) && rt->exclusiveThreadsPresent())
return false;
return true;
}
JS::Zone *
js::ZoneOfValue(const JS::Value &value)
{
@ -273,3 +285,9 @@ js::ZoneOfValue(const JS::Value &value)
return value.toObject().zone();
return static_cast<js::gc::Cell *>(value.toGCThing())->tenuredZone();
}
bool
js::ZonesIter::atAtomsZone(JSRuntime *rt)
{
return rt->isAtomsZone(*it);
}

View File

@ -137,15 +137,7 @@ struct Zone : public JS::shadow::Zone,
void setPreservingCode(bool preserving) { gcPreserveCode_ = preserving; }
bool isPreservingCode() const { return gcPreserveCode_; }
bool canCollect() {
// Zones cannot be collected while in use by other threads.
if (usedByExclusiveThread)
return false;
JSRuntime *rt = runtimeFromAnyThread();
if (rt->isAtomsZone(this) && rt->exclusiveThreadsPresent())
return false;
return true;
}
bool canCollect();
enum GCState {
NoGC,
@ -306,11 +298,13 @@ class ZonesIter
end = rt->gc.zones.end();
if (selector == SkipAtoms) {
JS_ASSERT(rt->isAtomsZone(*it));
MOZ_ASSERT(atAtomsZone(rt));
it++;
}
}
bool atAtomsZone(JSRuntime *rt);
bool done() const { return it == end; }
void next() {

View File

@ -36,48 +36,61 @@ using mozilla::BinarySearch;
using mozilla::IsNaN;
using mozilla::PodZero;
AsmJSFrameIterator::AsmJSFrameIterator(const AsmJSActivation *activation)
static uint8_t *
ReturnAddressForExitCall(uint8_t **psp)
{
if (!activation || activation->isInterruptedSP()) {
PodZero(this);
JS_ASSERT(done());
return;
}
module_ = &activation->module();
sp_ = activation->exitSP();
uint8_t *sp = *psp;
#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
// For calls to Ion/C++ on x86/x64, the exitSP is the SP right before the call
// to C++. Since the call instruction pushes the return address, we know
// that the return address is 1 word below exitSP.
returnAddress_ = *(uint8_t**)(sp_ - sizeof(void*));
return *(uint8_t**)(sp - sizeof(void*));
#elif defined(JS_CODEGEN_ARM)
// For calls to Ion/C++ on ARM, the *caller* pushes the return address on
// the stack. For Ion, this is just part of the ABI. For C++, the return
// address is explicitly pushed before the call since we cannot expect the
// callee to immediately push lr. This means that exitSP points to the
// return address.
returnAddress_ = *(uint8_t**)sp_;
return *(uint8_t**)sp;
#elif defined(JS_CODEGEN_MIPS)
// On MIPS we have two cases. Exit to C++ will store return addres at
// sp + 16, While on exits to Ion, the return address will be stored at
// sp + 0. We indicate exits to ion by setting the lowest bit of stored sp.
// Check if this is the exit to Ion.
if (uint32_t(sp_) & 0x1) {
// Clear the low bit.
sp_ -= 0x1;
returnAddress_ = *(uint8_t**)sp_;
} else {
// This is exit to C++
returnAddress_ = *(uint8_t**)(sp_ + ShadowStackSpace);
// On MIPS we have two cases: an exit to C++ will store the return address
// at ShadowStackSpace above sp; an exit to Ion will store the return
// address at sp. To distinguish the two cases, the low bit of sp (which is
// aligned and therefore zero) is set for Ion exits.
if (uintptr_t(sp) & 0x1) {
sp = *psp -= 0x1; // Clear the low bit
return *(uint8_t**)sp;
}
return *(uint8_t**)(sp + ShadowStackSpace);
#else
# error "Unknown architecture!"
#endif
}
settle();
static uint8_t *
ReturnAddressForJitCall(uint8_t *sp)
{
// Once inside JIT code, sp always points to the word before the return
// address.
return *(uint8_t**)(sp - sizeof(void*));
}
AsmJSFrameIterator::AsmJSFrameIterator(const AsmJSActivation *activation)
: module_(nullptr)
{
if (!activation || activation->isInterruptedSP())
return;
module_ = &activation->module();
sp_ = activation->exitSP();
settle(ReturnAddressForExitCall(&sp_));
}
void
AsmJSFrameIterator::operator++()
{
settle(ReturnAddressForJitCall(sp_));
}
struct GetCallSite
@ -90,43 +103,31 @@ struct GetCallSite
};
void
AsmJSFrameIterator::popFrame()
AsmJSFrameIterator::settle(uint8_t *returnAddress)
{
// After adding stackDepth, sp points to the word before the return address,
// on both ARM and x86/x64.
sp_ += callsite_->stackDepth();
returnAddress_ = *(uint8_t**)(sp_ - sizeof(void*));
}
uint32_t target = returnAddress - module_->codeBase();
size_t lowerBound = 0;
size_t upperBound = module_->numCallSites();
void
AsmJSFrameIterator::settle()
{
while (true) {
uint32_t target = returnAddress_ - module_->codeBase();
size_t lowerBound = 0;
size_t upperBound = module_->numCallSites();
size_t match;
if (!BinarySearch(GetCallSite(*module_), lowerBound, upperBound, target, &match)) {
callsite_ = nullptr;
return;
}
callsite_ = &module_->callSite(match);
if (callsite_->isExit()) {
popFrame();
continue;
}
if (callsite_->isEntry()) {
callsite_ = nullptr;
return;
}
JS_ASSERT(callsite_->isNormal());
size_t match;
if (!BinarySearch(GetCallSite(*module_), lowerBound, upperBound, target, &match)) {
module_ = nullptr;
return;
}
callsite_ = &module_->callSite(match);
if (callsite_->isEntry()) {
module_ = nullptr;
return;
}
sp_ += callsite_->stackDepth();
if (callsite_->isExit())
return settle(ReturnAddressForJitCall(sp_));
JS_ASSERT(callsite_->isNormal());
}
JSAtom *

View File

@ -23,15 +23,13 @@ class AsmJSFrameIterator
const AsmJSModule *module_;
const jit::CallSite *callsite_;
uint8_t *sp_;
uint8_t *returnAddress_;
void popFrame();
void settle();
void settle(uint8_t *returnAddress);
public:
explicit AsmJSFrameIterator(const AsmJSActivation *activation);
void operator++() { popFrame(); settle(); }
bool done() const { return !callsite_; }
void operator++();
bool done() const { return !module_; }
JSAtom *functionDisplayAtom() const;
unsigned computeLine(uint32_t *column) const;
};

View File

@ -8482,70 +8482,36 @@ bool
CodeGenerator::visitProfilerStackOp(LProfilerStackOp *lir)
{
Register temp = ToRegister(lir->temp()->output());
bool inlinedFunction = lir->inlineLevel() > 0;
switch (lir->type()) {
case MProfilerStackOp::InlineEnter:
// Multiple scripts can be inlined at one depth, but there is only
// one InlineExit node to signify this. To deal with this, if we
// reach the entry of another inline script on the same level, then
// just reset the sps metadata about the frame. We must balance
// calls to leave()/reenter(), so perform the balance without
// emitting any instrumentation. Technically the previous inline
// call at this same depth has reentered, but the instrumentation
// will be emitted at the common join point for all inlines at the
// same depth.
if (sps_.inliningDepth() == lir->inlineLevel()) {
sps_.leaveInlineFrame();
sps_.skipNextReenter();
sps_.reenter(masm, temp);
}
sps_.leave(masm, temp, /* inlinedFunction = */ true);
if (!sps_.enterInlineFrame())
return false;
// fallthrough
case MProfilerStackOp::Enter:
if (gen->options.spsSlowAssertionsEnabled()) {
if (!inlinedFunction) {
saveLive(lir);
pushArg(ImmGCPtr(lir->script()));
if (!callVM(SPSEnterInfo, lir))
return false;
restoreLive(lir);
}
sps_.pushManual(lir->script(), masm, temp, /* inlinedFunction = */ inlinedFunction);
saveLive(lir);
pushArg(ImmGCPtr(lir->script()));
if (!callVM(SPSEnterInfo, lir))
return false;
restoreLive(lir);
sps_.pushManual(lir->script(), masm, temp, /* inlinedFunction = */ false);
return true;
}
return sps_.push(lir->script(), masm, temp, /* inlinedFunction = */ inlinedFunction);
case MProfilerStackOp::InlineExit:
// all inline returns were covered with ::Exit, so we just need to
// maintain the state of inline frames currently active and then
// reenter the caller
sps_.leaveInlineFrame();
sps_.reenter(masm, temp, /* inlinedFunction = */ true);
return true;
return sps_.push(lir->script(), masm, temp, /* inlinedFunction = */ false);
case MProfilerStackOp::Exit:
if (gen->options.spsSlowAssertionsEnabled()) {
if (!inlinedFunction) {
saveLive(lir);
pushArg(ImmGCPtr(lir->script()));
// Once we've exited, then we shouldn't emit instrumentation for
// the corresponding reenter() because we no longer have a
// frame.
sps_.skipNextReenter();
if (!callVM(SPSExitInfo, lir))
return false;
restoreLive(lir);
}
saveLive(lir);
pushArg(ImmGCPtr(lir->script()));
// Once we've exited, then we shouldn't emit instrumentation for
// the corresponding reenter() because we no longer have a
// frame.
sps_.skipNextReenter();
if (!callVM(SPSExitInfo, lir))
return false;
restoreLive(lir);
return true;
}
sps_.pop(masm, temp, /* inlinedFunction = */ inlinedFunction);
sps_.pop(masm, temp, /* inlinedFunction = */ false);
return true;
default:

View File

@ -172,10 +172,8 @@ public:
return *this;
}
InlineForwardListIterator<T> operator ++(int) {
JS_ASSERT(modifyCount_ == owner_->modifyCount_);
InlineForwardListIterator<T> old(*this);
prev = iter;
iter = iter->next;
operator++();
return old;
}
T * operator *() const {
@ -339,12 +337,16 @@ class InlineListIterator
}
InlineListIterator<T> operator ++(int) {
InlineListIterator<T> old(*this);
iter = static_cast<Node *>(iter->next);
operator++();
return old;
}
InlineListIterator<T> & operator --() {
iter = iter->prev;
return *this;
}
InlineListIterator<T> operator --(int) {
InlineListIterator<T> old(*this);
iter = iter->prev;
operator--();
return old;
}
T * operator *() const {
@ -383,7 +385,16 @@ class InlineListReverseIterator
}
InlineListReverseIterator<T> operator ++(int) {
InlineListReverseIterator<T> old(*this);
iter = iter->prev;
operator++();
return old;
}
InlineListReverseIterator<T> & operator --() {
iter = static_cast<Node *>(iter->next);
return *this;
}
InlineListReverseIterator<T> operator --(int) {
InlineListReverseIterator<T> old(*this);
operator--();
return old;
}
T * operator *() {
@ -464,12 +475,12 @@ class InlineConcatListIterator
public:
InlineConcatListIterator<T> & operator ++() {
iter = iter->next;
return *iter;
iter = static_cast<Node *>(iter->next);
return *this;
}
InlineConcatListIterator<T> operator ++(int) {
InlineConcatListIterator<T> old(*this);
iter = static_cast<Node *>(iter->next);
operator++();
return old;
}
T * operator *() const {

View File

@ -1413,8 +1413,7 @@ OptimizeMIR(MIRGenerator *mir)
// frequently.
JSScript *script = mir->info().script();
if (!script || !script->hadFrequentBailouts()) {
LICM licm(mir, graph);
if (!licm.analyze())
if (!LICM(mir, graph))
return false;
IonSpewPass("LICM");
AssertExtendedGraphCoherency(graph);
@ -1508,6 +1507,21 @@ OptimizeMIR(MIRGenerator *mir)
return false;
}
// Make loops contiguious. We do this after GVN/UCE and range analysis,
// which can remove CFG edges, exposing more blocks that can be moved.
// We also disable this when profiling, since reordering blocks appears
// to make the profiler unhappy.
{
AutoTraceLog log(logger, TraceLogger::MakeLoopsContiguous);
if (!MakeLoopsContiguous(graph))
return false;
IonSpewPass("Make loops contiguous");
AssertExtendedGraphCoherency(graph);
if (mir->shouldCancel("Make loops contiguous"))
return false;
}
// Passes after this point must not move instructions; these analyses
// depend on knowing the final order in which instructions will execute.

View File

@ -1407,7 +1407,7 @@ jit::AssertBasicGraphCoherency(MIRGraph &graph)
#ifdef DEBUG
static void
AssertReversePostOrder(MIRGraph &graph)
AssertReversePostorder(MIRGraph &graph)
{
// Check that every block is visited after all its predecessors (except backedges).
for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) {
@ -1415,7 +1415,10 @@ AssertReversePostOrder(MIRGraph &graph)
for (size_t i = 0; i < block->numPredecessors(); i++) {
MBasicBlock *pred = block->getPredecessor(i);
JS_ASSERT_IF(!pred->isLoopBackedge(), pred->isMarked());
if (!pred->isMarked()) {
JS_ASSERT(pred->isLoopBackedge());
JS_ASSERT(block->backedge() == pred);
}
}
block->mark();
@ -1478,7 +1481,7 @@ jit::AssertGraphCoherency(MIRGraph &graph)
if (!js_JitOptions.checkGraphConsistency)
return;
AssertBasicGraphCoherency(graph);
AssertReversePostOrder(graph);
AssertReversePostorder(graph);
#endif
}
@ -2482,3 +2485,169 @@ jit::AnalyzeArgumentsUsage(JSContext *cx, JSScript *scriptArg)
script->setNeedsArgsObj(false);
return true;
}
// Mark all the blocks that are in the loop with the given header.
// Returns the number of blocks marked. Set *canOsr to true if the loop is
// reachable from both the normal entry and the OSR entry.
size_t
jit::MarkLoopBlocks(MIRGraph &graph, MBasicBlock *header, bool *canOsr)
{
#ifdef DEBUG
for (ReversePostorderIterator i = graph.rpoBegin(), e = graph.rpoEnd(); i != e; ++i)
MOZ_ASSERT(!i->isMarked(), "Some blocks already marked");
#endif
MBasicBlock *osrBlock = graph.osrBlock();
*canOsr = false;
// The blocks are in RPO; start at the loop backedge, which is marks the
// bottom of the loop, and walk up until we get to the header. Loops may be
// discontiguous, so we trace predecessors to determine which blocks are
// actually part of the loop. The backedge is always part of the loop, and
// so are its predecessors, transitively, up to the loop header or an OSR
// entry.
MBasicBlock *backedge = header->backedge();
backedge->mark();
size_t numMarked = 1;
for (PostorderIterator i = graph.poBegin(backedge); ; ++i) {
MOZ_ASSERT(i != graph.poEnd(),
"Reached the end of the graph while searching for the loop header");
MBasicBlock *block = *i;
// A block not marked by the time we reach it is not in the loop.
if (!block->isMarked())
continue;
// If we've reached the loop header, we're done.
if (block == header)
break;
// This block is in the loop; trace to its predecessors.
for (size_t p = 0, e = block->numPredecessors(); p != e; ++p) {
MBasicBlock *pred = block->getPredecessor(p);
if (pred->isMarked())
continue;
// Blocks dominated by the OSR entry are not part of the loop
// (unless they aren't reachable from the normal entry).
if (osrBlock && pred != header && osrBlock->dominates(pred)) {
*canOsr = true;
continue;
}
MOZ_ASSERT(pred->id() >= header->id() && pred->id() <= backedge->id(),
"Loop block not between loop header and loop backedge");
pred->mark();
++numMarked;
// A nested loop may not exit back to the enclosing loop at its
// bottom. If we just marked its header, then the whole nested loop
// is part of the enclosing loop.
if (pred->isLoopHeader()) {
MBasicBlock *innerBackedge = pred->backedge();
if (!innerBackedge->isMarked()) {
// Mark its backedge so that we add all of its blocks to the
// outer loop as we walk upwards.
innerBackedge->mark();
++numMarked;
// If the nested loop is not contiguous, we may have already
// passed its backedge. If this happens, back up.
if (backedge->id() > block->id()) {
i = graph.poBegin(innerBackedge);
--i;
}
}
}
}
}
MOZ_ASSERT(header->isMarked(), "Loop header should be part of the loop");
return numMarked;
}
// Unmark all the blocks that are in the loop with the given header.
void
jit::UnmarkLoopBlocks(MIRGraph &graph, MBasicBlock *header)
{
MBasicBlock *backedge = header->backedge();
for (ReversePostorderIterator i = graph.rpoBegin(header); ; ++i) {
MOZ_ASSERT(i != graph.rpoEnd(),
"Reached the end of the graph while searching for the backedge");
MBasicBlock *block = *i;
if (block->isMarked()) {
block->unmark();
if (block == backedge)
break;
}
}
#ifdef DEBUG
for (ReversePostorderIterator i = graph.rpoBegin(), e = graph.rpoEnd(); i != e; ++i)
MOZ_ASSERT(!i->isMarked(), "Not all blocks got unmarked");
#endif
}
// Reorder the blocks in the loop starting at the given header to be contiguous.
static void
MakeLoopContiguous(MIRGraph &graph, MBasicBlock *header, size_t numMarked)
{
MBasicBlock *backedge = header->backedge();
MOZ_ASSERT(header->isMarked(), "Loop header is not part of loop");
MOZ_ASSERT(backedge->isMarked(), "Loop backedge is not part of loop");
// If there are any blocks between the loop header and the loop backedge
// that are not part of the loop, prepare to move them to the end. We keep
// them in order, which preserves RPO.
ReversePostorderIterator insertIter = graph.rpoBegin(backedge);
insertIter++;
MBasicBlock *insertPt = *insertIter;
// Visit all the blocks from the loop header to the loop backedge.
size_t headerId = header->id();
size_t inLoopId = headerId;
size_t notInLoopId = inLoopId + numMarked;
ReversePostorderIterator i = graph.rpoBegin(header);
for (;;) {
MBasicBlock *block = *i++;
MOZ_ASSERT(block->id() >= header->id() && block->id() <= backedge->id(),
"Loop backedge should be last block in loop");
if (block->isMarked()) {
// This block is in the loop.
block->unmark();
block->setId(inLoopId++);
// If we've reached the loop backedge, we're done!
if (block == backedge)
break;
} else {
// This block is not in the loop. Move it to the end.
graph.moveBlockBefore(insertPt, block);
block->setId(notInLoopId++);
}
}
MOZ_ASSERT(header->id() == headerId, "Loop header id changed");
MOZ_ASSERT(inLoopId == headerId + numMarked, "Wrong number of blocks kept in loop");
MOZ_ASSERT(notInLoopId == (insertIter != graph.rpoEnd() ? insertPt->id() : graph.numBlocks()),
"Wrong number of blocks moved out of loop");
}
// Reorder the blocks in the graph so that loops are contiguous.
bool
jit::MakeLoopsContiguous(MIRGraph &graph)
{
// Visit all loop headers (in any order).
for (MBasicBlockIterator i(graph.begin()); i != graph.end(); i++) {
MBasicBlock *header = *i;
if (!header->isLoopHeader())
continue;
// Mark all blocks that are actually part of the loop.
bool canOsr;
size_t numMarked = MarkLoopBlocks(graph, header, &canOsr);
// Move all blocks between header and backedge that aren't marked to
// the end of the loop, making the loop itself contiguous.
MakeLoopContiguous(graph, header, numMarked);
}
return true;
}

View File

@ -31,6 +31,15 @@ enum Observability {
bool
EliminatePhis(MIRGenerator *mir, MIRGraph &graph, Observability observe);
size_t
MarkLoopBlocks(MIRGraph &graph, MBasicBlock *header, bool *canOsr);
void
UnmarkLoopBlocks(MIRGraph &graph, MBasicBlock *header);
bool
MakeLoopsContiguous(MIRGraph &graph);
bool
EliminateDeadResumePointOperands(MIRGenerator *mir, MIRGraph &graph);

View File

@ -831,16 +831,6 @@ IonBuilder::buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoi
MBasicBlock *predecessor = callerBuilder->current;
JS_ASSERT(predecessor == callerResumePoint->block());
// All further instructions generated in from this scope should be
// considered as part of the function that we're inlining. We also need to
// keep track of the inlining depth because all scripts inlined on the same
// level contiguously have only one InlineExit node.
if (instrumentedProfiling()) {
predecessor->add(MProfilerStackOp::New(alloc(), script(),
MProfilerStackOp::InlineEnter,
inliningDepth_));
}
predecessor->end(MGoto::New(alloc(), current));
if (!current->addPredecessorWithoutPhis(predecessor))
return false;
@ -3701,9 +3691,8 @@ IonBuilder::processReturn(JSOp op)
MOZ_ASSUME_UNREACHABLE("unknown return op");
}
if (instrumentedProfiling()) {
current->add(MProfilerStackOp::New(alloc(), script(), MProfilerStackOp::Exit,
inliningDepth_));
if (instrumentedProfiling() && inliningDepth_ == 0) {
current->add(MProfilerStackOp::New(alloc(), script(), MProfilerStackOp::Exit));
}
MReturn *ret = MReturn::New(alloc(), def);
current->end(ret);
@ -4048,10 +4037,6 @@ IonBuilder::inlineScriptedCall(CallInfo &callInfo, JSFunction *target)
return false;
returnBlock->setCallerResumePoint(callerResumePoint_);
// When profiling add InlineExit instruction to indicate end of inlined function.
if (instrumentedProfiling())
returnBlock->add(MProfilerStackOp::New(alloc(), nullptr, MProfilerStackOp::InlineExit));
// Inherit the slots from current and pop |fun|.
returnBlock->inheritSlots(current);
returnBlock->pop();

View File

@ -1961,9 +1961,10 @@ MacroAssembler::spsMarkJit(SPSProfiler *p, Register framePtr, Register temp)
// and won't be regenerated when SPS state changes.
spsProfileEntryAddressSafe(p, 0, temp, &stackFull);
// Push a C++ frame with non-copy label
storePtr(ImmPtr(enterJitLabel), Address(temp, ProfileEntry::offsetOfLabel()));
storePtr(framePtr, Address(temp, ProfileEntry::offsetOfSpOrScript()));
store32(Imm32(ProfileEntry::NullPCOffset), Address(temp, ProfileEntry::offsetOfLineOrPc()));
store32(Imm32(0), Address(temp, ProfileEntry::offsetOfLineOrPc()));
store32(Imm32(ProfileEntry::IS_CPP_ENTRY), Address(temp, ProfileEntry::offsetOfFlags()));
/* Always increment the stack size, whether or not we actually pushed. */

View File

@ -1086,10 +1086,11 @@ class MacroAssembler : public MacroAssemblerSpecific
Label stackFull;
spsProfileEntryAddress(p, 0, temp, &stackFull);
// Push a JS frame with a copy label
storePtr(ImmPtr(str), Address(temp, ProfileEntry::offsetOfLabel()));
storePtr(ImmGCPtr(s), Address(temp, ProfileEntry::offsetOfSpOrScript()));
store32(Imm32(ProfileEntry::NullPCOffset), Address(temp, ProfileEntry::offsetOfLineOrPc()));
store32(Imm32(0), Address(temp, ProfileEntry::offsetOfFlags()));
store32(Imm32(ProfileEntry::FRAME_LABEL_COPY), Address(temp, ProfileEntry::offsetOfFlags()));
/* Always increment the stack size, whether or not we actually pushed. */
bind(&stackFull);
@ -1104,6 +1105,7 @@ class MacroAssembler : public MacroAssemblerSpecific
Label stackFull;
spsProfileEntryAddressSafe(p, 0, temp, &stackFull);
// Push a JS frame with a copy label
loadPtr(str, temp2);
storePtr(temp2, Address(temp, ProfileEntry::offsetOfLabel()));
@ -1114,7 +1116,7 @@ class MacroAssembler : public MacroAssemblerSpecific
// (See probes::EnterScript, which calls spsProfiler.enter, which pushes an entry
// with 0 pcIdx).
store32(Imm32(0), Address(temp, ProfileEntry::offsetOfLineOrPc()));
store32(Imm32(0), Address(temp, ProfileEntry::offsetOfFlags()));
store32(Imm32(ProfileEntry::FRAME_LABEL_COPY), Address(temp, ProfileEntry::offsetOfFlags()));
/* Always increment the stack size, whether or not we actually pushed. */
bind(&stackFull);

View File

@ -6,377 +6,252 @@
#include "jit/LICM.h"
#include <stdio.h>
#include "jit/IonAnalysis.h"
#include "jit/IonSpewer.h"
#include "jit/MIR.h"
#include "jit/MIRGenerator.h"
#include "jit/MIRGraph.h"
using namespace js;
using namespace js::jit;
namespace {
typedef Vector<MInstruction*, 1, IonAllocPolicy> InstructionQueue;
class Loop
// Test whether any instruction in the loop possiblyCalls().
static bool
LoopContainsPossibleCall(MIRGraph &graph, MBasicBlock *header, MBasicBlock *backedge)
{
MIRGenerator *mir;
public:
// Loop code may return three values:
enum LoopReturn {
LoopReturn_Success,
LoopReturn_Error, // Compilation failure.
LoopReturn_Skip // The loop is not suitable for LICM, but there is no error.
};
public:
// A loop is constructed on a backedge found in the control flow graph.
Loop(MIRGenerator *mir, MBasicBlock *footer);
// Initializes the loop, finds all blocks and instructions contained in the loop.
LoopReturn init();
// Identifies hoistable loop invariant instructions and moves them out of the loop.
bool optimize();
private:
// These blocks define the loop. header_ points to the loop header
MBasicBlock *header_;
// The pre-loop block is the first predecessor of the loop header. It is where
// the loop is first entered and where hoisted instructions will be placed.
MBasicBlock* preLoop_;
// This indicates whether the loop contains calls or other things which
// clobber most or all floating-point registers. In such loops,
// floating-point constants should not be hoisted unless it enables further
// hoisting.
bool containsPossibleCall_;
TempAllocator &alloc() const {
return mir->alloc();
}
bool hoistInstructions(InstructionQueue &toHoist);
// Utility methods for invariance testing and instruction hoisting.
bool isInLoop(MDefinition *ins);
bool isBeforeLoop(MDefinition *ins);
bool isLoopInvariant(MInstruction *ins);
// This method determines if this block hot within a loop. That is, if it's
// always or usually run when the loop executes
bool checkHotness(MBasicBlock *block);
// Worklist and worklist usage methods
InstructionQueue worklist_;
bool insertInWorklist(MInstruction *ins);
MInstruction* popFromWorklist();
inline bool isHoistable(const MDefinition *ins) const {
return ins->isMovable() && !ins->isEffectful() && !ins->neverHoist();
}
bool requiresHoistedUse(const MDefinition *ins) const;
};
} /* namespace anonymous */
LICM::LICM(MIRGenerator *mir, MIRGraph &graph)
: mir(mir), graph(graph)
{
}
bool
LICM::analyze()
{
IonSpew(IonSpew_LICM, "Beginning LICM pass.");
// Iterate in RPO to visit outer loops before inner loops.
for (ReversePostorderIterator i(graph.rpoBegin()); i != graph.rpoEnd(); i++) {
MBasicBlock *header = *i;
// Skip non-headers and self-loops.
if (!header->isLoopHeader() || header->numPredecessors() < 2)
for (auto i(graph.rpoBegin(header)); ; ++i) {
MOZ_ASSERT(i != graph.rpoEnd(), "Reached end of graph searching for blocks in loop");
MBasicBlock *block = *i;
if (!block->isMarked())
continue;
// Attempt to optimize loop.
Loop loop(mir, header);
Loop::LoopReturn lr = loop.init();
if (lr == Loop::LoopReturn_Error)
return false;
if (lr == Loop::LoopReturn_Skip) {
graph.unmarkBlocks();
continue;
}
if (!loop.optimize())
return false;
graph.unmarkBlocks();
}
return true;
}
Loop::Loop(MIRGenerator *mir, MBasicBlock *header)
: mir(mir),
header_(header),
containsPossibleCall_(false),
worklist_(mir->alloc())
{
preLoop_ = header_->getPredecessor(0);
}
Loop::LoopReturn
Loop::init()
{
IonSpew(IonSpew_LICM, "Loop identified, headed by block %d", header_->id());
IonSpew(IonSpew_LICM, "footer is block %d", header_->backedge()->id());
// The first predecessor of the loop header must dominate the header.
JS_ASSERT(header_->id() > header_->getPredecessor(0)->id());
// Loops from backedge to header and marks all visited blocks
// as part of the loop. At the same time add all hoistable instructions
// (in RPO order) to the instruction worklist.
Vector<MBasicBlock *, 1, IonAllocPolicy> inlooplist(alloc());
if (!inlooplist.append(header_->backedge()))
return LoopReturn_Error;
header_->backedge()->mark();
while (!inlooplist.empty()) {
MBasicBlock *block = inlooplist.back();
// Hoisting requires more finesse if the loop contains a block that
// self-dominates: there exists control flow that may enter the loop
// without passing through the loop preheader.
//
// Rather than perform a complicated analysis of the dominance graph,
// just return a soft error to ignore this loop.
if (block->immediateDominator() == block) {
while (!worklist_.empty())
popFromWorklist();
return LoopReturn_Skip;
}
// Add not yet visited predecessors to the inlooplist.
if (block != header_) {
for (size_t i = 0; i < block->numPredecessors(); i++) {
MBasicBlock *pred = block->getPredecessor(i);
if (pred->isMarked())
continue;
if (!inlooplist.append(pred))
return LoopReturn_Error;
pred->mark();
for (auto insIter(block->begin()), insEnd(block->end()); insIter != insEnd; ++insIter) {
MInstruction *ins = *insIter;
if (ins->possiblyCalls()) {
IonSpew(IonSpew_LICM, " Possile call found at %s%u", ins->opName(), ins->id());
return true;
}
}
// If any block was added, process them first.
if (block != inlooplist.back())
continue;
// Add all instructions in this block (but the control instruction) to the worklist
for (MInstructionIterator i = block->begin(); i != block->end(); i++) {
MInstruction *ins = *i;
// Remember whether this loop contains anything which clobbers most
// or all floating-point registers. This is just a rough heuristic.
if (ins->possiblyCalls())
containsPossibleCall_ = true;
if (isHoistable(ins)) {
if (!insertInWorklist(ins))
return LoopReturn_Error;
}
}
// All successors of this block are visited.
inlooplist.popBack();
if (block == backedge)
break;
}
return LoopReturn_Success;
return false;
}
bool
Loop::optimize()
// When a nested loop has no exits back into what would be its parent loop,
// MarkLoopBlocks on the parent loop doesn't mark the blocks of the nested
// loop, since they technically aren't part of the loop. However, AliasAnalysis
// currently does consider such nested loops to be part of their parent
// loops. Consequently, we can't use IsInLoop on dependency() values; we must
// test whether a dependency() is *before* the loop, even if it is not
// technically in the loop.
static bool
IsBeforeLoop(MDefinition *ins, MBasicBlock *header)
{
InstructionQueue invariantInstructions(alloc());
IonSpew(IonSpew_LICM, "These instructions are in the loop: ");
while (!worklist_.empty()) {
if (mir->shouldCancel("LICM (worklist)"))
return false;
MInstruction *ins = popFromWorklist();
IonSpewHeader(IonSpew_LICM);
if (IonSpewEnabled(IonSpew_LICM)) {
ins->printName(IonSpewFile);
fprintf(IonSpewFile, " <- ");
ins->printOpcode(IonSpewFile);
fprintf(IonSpewFile, ": ");
}
if (isLoopInvariant(ins)) {
// Flag this instruction as loop invariant.
ins->setLoopInvariant();
if (!invariantInstructions.append(ins))
return false;
if (IonSpewEnabled(IonSpew_LICM))
fprintf(IonSpewFile, " Loop Invariant!\n");
}
}
if (!hoistInstructions(invariantInstructions))
return false;
return true;
return ins->block()->id() < header->id();
}
bool
Loop::requiresHoistedUse(const MDefinition *ins) const
// Test whether the given instruction is inside the loop (and thus not
// loop-invariant).
static bool
IsInLoop(MDefinition *ins)
{
if (ins->isConstantElements() || ins->isBox())
return ins->block()->isMarked();
}
// Test whether the given instruction is cheap and not worth hoisting unless
// one of its users will be hoisted as well.
static bool
RequiresHoistedUse(const MDefinition *ins, bool hasCalls)
{
if (ins->isConstantElements())
return true;
// Integer constants can often be folded as immediates and aren't worth
// hoisting on their own, in general. Floating-point constants typically
// are worth hoisting, unless they'll end up being spilled (eg. due to a
// call).
if (ins->isConstant() && (IsFloatingPointType(ins->type()) || containsPossibleCall_))
if (ins->isBox()) {
MOZ_ASSERT(!ins->toBox()->input()->isBox(),
"Box of a box could lead to unbounded recursion");
return true;
}
// Integer constants are usually cheap and aren't worth hoisting on their
// own, in general. Floating-point constants typically are worth hoisting,
// unless they'll end up being spilled (eg. due to a call).
if (ins->isConstant() && (!IsFloatingPointType(ins->type()) || hasCalls))
return true;
return false;
}
bool
Loop::hoistInstructions(InstructionQueue &toHoist)
// Test whether the given instruction has any operands defined within the loop.
static bool
HasOperandInLoop(MInstruction *ins, bool hasCalls)
{
// Iterate in post-order (uses before definitions)
for (int32_t i = toHoist.length() - 1; i >= 0; i--) {
MInstruction *ins = toHoist[i];
// An instruction is only loop invariant if it and all of its operands can
// be safely hoisted into the loop preheader.
for (size_t i = 0, e = ins->numOperands(); i != e; ++i) {
MDefinition *op = ins->getOperand(i);
if (!IsInLoop(op))
continue;
if (RequiresHoistedUse(op, hasCalls)) {
// Recursively test for loop invariance. Note that the recursion is
// bounded because we require RequiresHoistedUse to be set at each
// level.
if (!HasOperandInLoop(op->toInstruction(), hasCalls))
continue;
}
return true;
}
return false;
}
// Test whether the given instruction is hoistable, ignoring memory
// dependencies.
static bool
IsHoistableIgnoringDependency(MInstruction *ins, bool hasCalls)
{
return ins->isMovable() && !ins->isEffectful() && !ins->neverHoist() &&
!HasOperandInLoop(ins, hasCalls);
}
// Test whether the given instruction has a memory dependency inside the loop.
static bool
HasDependencyInLoop(MInstruction *ins, MBasicBlock *header)
{
// Don't hoist if this instruction depends on a store inside the loop.
if (MDefinition *dep = ins->dependency())
return !IsBeforeLoop(dep, header);
return false;
}
// Test whether the given instruction is hoistable.
static bool
IsHoistable(MInstruction *ins, MBasicBlock *header, bool hasCalls)
{
return IsHoistableIgnoringDependency(ins, hasCalls) && !HasDependencyInLoop(ins, header);
}
// In preparation for hoisting an instruction, hoist any of its operands which
// were too cheap to hoist on their own.
static void
MoveDeferredOperands(MInstruction *ins, MInstruction *hoistPoint, bool hasCalls)
{
// If any of our operands were waiting for a user to be hoisted, make a note
// to hoist them.
for (size_t i = 0, e = ins->numOperands(); i != e; ++i) {
MDefinition *op = ins->getOperand(i);
if (!IsInLoop(op))
continue;
MOZ_ASSERT(RequiresHoistedUse(op, hasCalls),
"Deferred loop-invariant operand is not cheap");
MInstruction *opIns = op->toInstruction();
// Recursively move the operands. Note that the recursion is bounded
// because we require RequiresHoistedUse to be set at each level.
MoveDeferredOperands(opIns, hoistPoint, hasCalls);
IonSpew(IonSpew_LICM, " Hoisting %s%u (now that a user will be hoisted)",
opIns->opName(), opIns->id());
opIns->block()->moveBefore(hoistPoint, opIns);
}
}
static void
VisitLoopBlock(MBasicBlock *block, MBasicBlock *header, MInstruction *hoistPoint, bool hasCalls)
{
for (auto insIter(block->begin()), insEnd(block->end()); insIter != insEnd; ) {
MInstruction *ins = *insIter++;
if (!IsHoistable(ins, header, hasCalls)) {
#ifdef DEBUG
if (IsHoistableIgnoringDependency(ins, hasCalls)) {
IonSpew(IonSpew_LICM, " %s%u isn't hoistable due to dependency on %s%u",
ins->opName(), ins->id(),
ins->dependency()->opName(), ins->dependency()->id());
}
#endif
continue;
}
// Don't hoist a cheap constant if it doesn't enable us to hoist one of
// its uses. We want those instructions as close as possible to their
// use, to facilitate folding and minimize register pressure.
if (requiresHoistedUse(ins)) {
bool loopInvariantUse = false;
for (MUseDefIterator use(ins); use; use++) {
if (use.def()->isLoopInvariant()) {
loopInvariantUse = true;
break;
}
}
if (!loopInvariantUse)
ins->setNotLoopInvariant();
// use, to minimize register pressure.
if (RequiresHoistedUse(ins, hasCalls)) {
IonSpew(IonSpew_LICM, " %s%u will be hoisted only if its users are",
ins->opName(), ins->id());
continue;
}
// Hoist operands which were too cheap to hoist on their own.
MoveDeferredOperands(ins, hoistPoint, hasCalls);
IonSpew(IonSpew_LICM, " Hoisting %s%u", ins->opName(), ins->id());
// Move the instruction to the hoistPoint.
block->moveBefore(hoistPoint, ins);
}
}
// Move all instructions to the preLoop_ block just before the control instruction.
for (size_t i = 0; i < toHoist.length(); i++) {
MInstruction *ins = toHoist[i];
static void
VisitLoop(MIRGraph &graph, MBasicBlock *header)
{
MInstruction *hoistPoint = header->loopPredecessor()->lastIns();
// Loads may have an implicit dependency on either stores (effectful instructions) or
// control instructions so we should never move these.
JS_ASSERT(!ins->isControlInstruction());
JS_ASSERT(!ins->isEffectful());
JS_ASSERT(ins->isMovable());
IonSpew(IonSpew_LICM, " Visiting loop with header block%u, hoisting to %s%u",
header->id(), hoistPoint->opName(), hoistPoint->id());
if (!ins->isLoopInvariant())
MBasicBlock *backedge = header->backedge();
// This indicates whether the loop contains calls or other things which
// clobber most or all floating-point registers. In such loops,
// floating-point constants should not be hoisted unless it enables further
// hoisting.
bool hasCalls = LoopContainsPossibleCall(graph, header, backedge);
for (auto i(graph.rpoBegin(header)); ; ++i) {
MOZ_ASSERT(i != graph.rpoEnd(), "Reached end of graph searching for blocks in loop");
MBasicBlock *block = *i;
if (!block->isMarked())
continue;
if (checkHotness(ins->block())) {
ins->block()->moveBefore(preLoop_->lastIns(), ins);
ins->setNotLoopInvariant();
}
}
VisitLoopBlock(block, header, hoistPoint, hasCalls);
return true;
if (block == backedge)
break;
}
}
bool
Loop::isInLoop(MDefinition *ins)
jit::LICM(MIRGenerator *mir, MIRGraph &graph)
{
return ins->block()->isMarked();
}
IonSpew(IonSpew_LICM, "Beginning LICM pass");
bool
Loop::isBeforeLoop(MDefinition *ins)
{
return ins->block()->id() < header_->id();
}
// Iterate in RPO to visit outer loops before inner loops. We'd hoist the
// same things either way, but outer first means we do a little less work.
for (auto i(graph.rpoBegin()), e(graph.rpoEnd()); i != e; ++i) {
MBasicBlock *header = *i;
if (!header->isLoopHeader())
continue;
bool
Loop::isLoopInvariant(MInstruction *ins)
{
if (!isHoistable(ins)) {
if (IonSpewEnabled(IonSpew_LICM))
fprintf(IonSpewFile, "not hoistable\n");
return false;
}
bool canOsr;
MarkLoopBlocks(graph, header, &canOsr);
// Don't hoist if this instruction depends on a store inside or after the loop.
// Note: "after the loop" can sound strange, but Alias Analysis doesn't look
// at the control flow. Therefore it doesn't match the definition here, that a block
// is in the loop when there is a (directed) path from the block to the loop header.
if (ins->dependency() && !isBeforeLoop(ins->dependency())) {
if (IonSpewEnabled(IonSpew_LICM)) {
fprintf(IonSpewFile, "depends on store inside or after loop: ");
ins->dependency()->printName(IonSpewFile);
fprintf(IonSpewFile, "\n");
}
return false;
}
// Hoisting out of a loop that has an entry from the OSR block in
// addition to its normal entry is tricky. In theory we could clone
// the instruction and insert phis.
if (!canOsr)
VisitLoop(graph, header);
else
IonSpew(IonSpew_LICM, " Skipping loop with header block%u due to OSR", header->id());
// An instruction is only loop invariant if it and all of its operands can
// be safely hoisted into the loop preheader.
for (size_t i = 0, e = ins->numOperands(); i < e; i++) {
if (isInLoop(ins->getOperand(i)) &&
!ins->getOperand(i)->isLoopInvariant()) {
if (IonSpewEnabled(IonSpew_LICM)) {
ins->getOperand(i)->printName(IonSpewFile);
fprintf(IonSpewFile, " is in the loop.\n");
}
UnmarkLoopBlocks(graph, header);
if (mir->shouldCancel("LICM (main loop)"))
return false;
}
}
return true;
}
bool
Loop::checkHotness(MBasicBlock *block)
{
// TODO: determine if instructions within this block are worth hoisting.
// They might not be if the block is cold enough within the loop.
// BUG 669517
return true;
}
bool
Loop::insertInWorklist(MInstruction *ins)
{
if (!worklist_.insert(worklist_.begin(), ins))
return false;
ins->setInWorklist();
return true;
}
MInstruction*
Loop::popFromWorklist()
{
MInstruction* toReturn = worklist_.popCopy();
toReturn->setNotInWorklist();
return toReturn;
}

View File

@ -15,15 +15,7 @@ namespace jit {
class MIRGenerator;
class MIRGraph;
class LICM
{
MIRGenerator *mir;
MIRGraph &graph;
public:
LICM(MIRGenerator *mir, MIRGraph &graph);
bool analyze();
};
bool LICM(MIRGenerator *mir, MIRGraph &graph);
} // namespace jit
} // namespace js

View File

@ -5736,10 +5736,6 @@ class LProfilerStackOp : public LInstructionHelper<0, 0, 1>
MProfilerStackOp::Type type() {
return mir_->toProfilerStackOp()->type();
}
unsigned inlineLevel() {
return mir_->toProfilerStackOp()->inlineLevel();
}
};
class LIsCallable : public LInstructionHelper<1, 1, 0>

View File

@ -698,6 +698,7 @@ class LInstructionVisitor
protected:
jsbytecode *lastPC_;
jsbytecode *lastNotInlinedPC_;
LInstruction *instruction() {
return ins_;
@ -706,13 +707,17 @@ class LInstructionVisitor
public:
void setInstruction(LInstruction *ins) {
ins_ = ins;
if (ins->mirRaw())
if (ins->mirRaw()) {
lastPC_ = ins->mirRaw()->trackedPc();
if (ins->mirRaw()->trackedTree())
lastNotInlinedPC_ = ins->mirRaw()->profilerLeavePc();
}
}
LInstructionVisitor()
: ins_(nullptr),
lastPC_(nullptr)
lastPC_(nullptr),
lastNotInlinedPC_(nullptr)
{}
public:

View File

@ -60,7 +60,6 @@ MIRType MIRTypeFromValue(const js::Value &vp)
#define MIR_FLAG_LIST(_) \
_(InWorklist) \
_(EmittedAtUses) \
_(LoopInvariant) \
_(Commutative) \
_(Movable) /* Allow LICM and GVN to move this instruction */ \
_(Lowered) /* (Debug only) has a virtual register */ \
@ -734,11 +733,15 @@ class MUseDefIterator
operator bool() const {
return current_ != def_->usesEnd();
}
MUseDefIterator operator ++() {
JS_ASSERT(current_ != def_->usesEnd());
++current_;
current_ = search(current_);
return *this;
}
MUseDefIterator operator ++(int) {
MUseDefIterator old(*this);
if (current_ != def_->usesEnd())
current_++;
current_ = search(current_);
operator++();
return old;
}
MUse *use() const {
@ -9724,33 +9727,25 @@ class MProfilerStackOp : public MNullaryInstruction
public:
enum Type {
Enter, // a function has begun executing and it is not inline
Exit, // any function has exited (inlined or normal)
InlineEnter, // an inline function has begun executing
InlineExit // all instructions of an inline function are done, a
// return from the inline function could have occurred
// before this boundary
Exit // any function has exited and is not inline
};
private:
JSScript *script_;
Type type_;
unsigned inlineLevel_;
MProfilerStackOp(JSScript *script, Type type, unsigned inlineLevel)
: script_(script), type_(type), inlineLevel_(inlineLevel)
MProfilerStackOp(JSScript *script, Type type)
: script_(script), type_(type)
{
JS_ASSERT_IF(type != InlineExit, script != nullptr);
JS_ASSERT_IF(type == InlineEnter, inlineLevel != 0);
JS_ASSERT(script);
setGuard();
}
public:
INSTRUCTION_HEADER(ProfilerStackOp)
static MProfilerStackOp *New(TempAllocator &alloc, JSScript *script, Type type,
unsigned inlineLevel = 0) {
return new(alloc) MProfilerStackOp(script, type, inlineLevel);
static MProfilerStackOp *New(TempAllocator &alloc, JSScript *script, Type type) {
return new(alloc) MProfilerStackOp(script, type);
}
JSScript *script() {
@ -9761,10 +9756,6 @@ class MProfilerStackOp : public MNullaryInstruction
return type_;
}
unsigned inlineLevel() {
return inlineLevel_;
}
AliasSet getAliasSet() const {
return AliasSet::None();
}

View File

@ -691,7 +691,9 @@ MBasicBlock::moveBefore(MInstruction *at, MInstruction *ins)
// Insert into new block, which may be distinct.
// Uses and operands are untouched.
at->block()->insertBefore(at, ins);
ins->setBlock(at->block());
at->block()->instructions_.insertBefore(at, ins);
ins->setTrackedSite(at->trackedSite());
}
static inline void

View File

@ -361,9 +361,14 @@ class MBasicBlock : public TempObject, public InlineListNode<MBasicBlock>
return mark_;
}
void mark() {
MOZ_ASSERT(!mark_, "Marking already-marked block");
markUnchecked();
}
void markUnchecked() {
mark_ = true;
}
void unmark() {
MOZ_ASSERT(mark_, "Unarking unmarked block");
mark_ = false;
}
void makeStart(MStart *start) {
@ -603,6 +608,9 @@ class MIRGraph
PostorderIterator poBegin() {
return blocks_.rbegin();
}
PostorderIterator poBegin(MBasicBlock *at) {
return blocks_.rbegin(at);
}
PostorderIterator poEnd() {
return blocks_.rend();
}
@ -622,6 +630,11 @@ class MIRGraph
blocks_.remove(block);
blocks_.pushBack(block);
}
void moveBlockBefore(MBasicBlock *at, MBasicBlock *block) {
JS_ASSERT(block->id());
blocks_.remove(block);
blocks_.insertBefore(at, block);
}
size_t numBlocks() const {
return numBlocks_;
}

View File

@ -375,7 +375,7 @@ ParallelSafetyAnalysis::analyze()
if (!visitor.unsafe()) {
// Block consists of only safe instructions. Visit its successors.
for (uint32_t i = 0; i < block->numSuccessors(); i++)
block->getSuccessor(i)->mark();
block->getSuccessor(i)->markUnchecked();
} else {
// Block contains an unsafe instruction. That means that once
// we enter this block, we are guaranteed to bailout.

View File

@ -1536,37 +1536,6 @@ MRandom::computeRange(TempAllocator &alloc)
// Range Analysis
///////////////////////////////////////////////////////////////////////////////
bool
RangeAnalysis::markBlocksInLoopBody(MBasicBlock *header, MBasicBlock *backedge)
{
Vector<MBasicBlock *, 16, IonAllocPolicy> worklist(alloc());
// Mark the header as being in the loop. This terminates the walk.
header->mark();
backedge->mark();
if (!worklist.append(backedge))
return false;
// If we haven't reached the loop header yet, walk up the predecessors
// we haven't seen already.
while (!worklist.empty()) {
MBasicBlock *current = worklist.popCopy();
for (size_t i = 0; i < current->numPredecessors(); i++) {
MBasicBlock *pred = current->getPredecessor(i);
if (pred->isMarked())
continue;
pred->mark();
if (!worklist.append(pred))
return false;
}
}
return true;
}
bool
RangeAnalysis::analyzeLoop(MBasicBlock *header)
{
@ -1581,8 +1550,8 @@ RangeAnalysis::analyzeLoop(MBasicBlock *header)
if (backedge == header)
return true;
if (!markBlocksInLoopBody(header, backedge))
return false;
bool canOsr;
MarkLoopBlocks(graph_, header, &canOsr);
LoopIterationBound *iterationBound = nullptr;
@ -1609,7 +1578,7 @@ RangeAnalysis::analyzeLoop(MBasicBlock *header)
} while (block != header);
if (!iterationBound) {
graph_.unmarkBlocks();
UnmarkLoopBlocks(graph_, header);
return true;
}
@ -1662,7 +1631,7 @@ RangeAnalysis::analyzeLoop(MBasicBlock *header)
}
}
graph_.unmarkBlocks();
UnmarkLoopBlocks(graph_, header);
return true;
}

View File

@ -105,7 +105,6 @@ class RangeAnalysis
MTest *test, BranchDirection direction);
void analyzeLoopPhi(MBasicBlock *header, LoopIterationBound *loopBound, MPhi *phi);
bool tryHoistBoundsCheck(MBasicBlock *header, MBoundsCheck *ins);
bool markBlocksInLoopBody(MBasicBlock *header, MBasicBlock *current);
};
class Range : public TempObject {

View File

@ -138,7 +138,7 @@ struct AllocationIntegrityState
class CodePosition
{
private:
MOZ_CONSTEXPR explicit CodePosition(const uint32_t &bits)
MOZ_CONSTEXPR explicit CodePosition(uint32_t bits)
: bits_(bits)
{ }
@ -178,27 +178,27 @@ class CodePosition
return (SubPosition)(bits_ & SUBPOSITION_MASK);
}
bool operator <(const CodePosition &other) const {
bool operator <(CodePosition other) const {
return bits_ < other.bits_;
}
bool operator <=(const CodePosition &other) const {
bool operator <=(CodePosition other) const {
return bits_ <= other.bits_;
}
bool operator !=(const CodePosition &other) const {
bool operator !=(CodePosition other) const {
return bits_ != other.bits_;
}
bool operator ==(const CodePosition &other) const {
bool operator ==(CodePosition other) const {
return bits_ == other.bits_;
}
bool operator >(const CodePosition &other) const {
bool operator >(CodePosition other) const {
return bits_ > other.bits_;
}
bool operator >=(const CodePosition &other) const {
bool operator >=(CodePosition other) const {
return bits_ >= other.bits_;
}
@ -268,7 +268,7 @@ class InstructionDataMap
return true;
}
InstructionData &operator[](const CodePosition &pos) {
InstructionData &operator[](CodePosition pos) {
JS_ASSERT(pos.ins() < numIns_);
return insData_[pos.ins()];
}

View File

@ -174,7 +174,7 @@ UnreachableCodeElimination::prunePointlessBranchesAndMarkReachableBlocks()
if (!osrBlock->getSuccessor(i)->isMarked()) {
// OSR block has an otherwise unreachable successor, abort.
for (MBasicBlockIterator iter(graph_.begin()); iter != graph_.end(); iter++)
iter->mark();
iter->markUnchecked();
marked_ = graph_.numBlocks();
return true;
}

View File

@ -49,7 +49,7 @@ CodeGeneratorShared::CodeGeneratorShared(MIRGenerator *gen, LIRGraph *graph, Mac
pushedArgs_(0),
#endif
lastOsiPointOffset_(0),
sps_(&GetIonContext()->runtime->spsProfiler(), &lastPC_),
sps_(&GetIonContext()->runtime->spsProfiler(), &lastNotInlinedPC_),
osrEntryOffset_(0),
skipArgCheckEntryOffset_(0),
frameDepth_(graph->paddedLocalSlotsSize() + graph->argumentsSize()),

View File

@ -357,7 +357,7 @@ MSG_DEF(JSMSG_ALREADY_HAS_PRAGMA, 303, 2, JSEXN_ERR, "{0} is being assigned
MSG_DEF(JSMSG_PAR_ARRAY_BAD_ARG, 304, 0, JSEXN_RANGEERR, "invalid parallel method argument")
MSG_DEF(JSMSG_REGEXP_RUNTIME_ERROR, 305, 0, JSEXN_INTERNALERR, "an error occurred while executing regular expression")
MSG_DEF(JSMSG_DEBUG_OPTIMIZED_OUT, 306, 0, JSEXN_ERR, "variable has been optimized out")
MSG_DEF(JSMSG_UNUSED307, 307, 0, JSEXN_NONE, "")
MSG_DEF(JSMSG_NEXT_RETURNED_PRIMITIVE,307, 0, JSEXN_TYPEERR, "iterator.next() returned a non-object value")
MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_CONFLICT, 308, 0, JSEXN_ERR, "no conflict resolution function provided")
MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_BOUNDS, 309, 0, JSEXN_ERR, "index in scatter vector out of bounds")
MSG_DEF(JSMSG_CANT_REPORT_NC_AS_NE, 310, 0, JSEXN_TYPEERR, "proxy can't report a non-configurable own property as non-existent")

View File

@ -3631,7 +3631,7 @@ JS_DeletePropertyById2(JSContext *cx, HandleObject obj, HandleId id, bool *resul
CHECK_REQUEST(cx);
assertSameCompartment(cx, obj, id);
return JSObject::deleteByValue(cx, obj, IdToValue(id), result);
return JSObject::deleteGeneric(cx, obj, id, result);
}
JS_PUBLIC_API(bool)
@ -3653,7 +3653,8 @@ JS_DeleteProperty2(JSContext *cx, HandleObject obj, const char *name, bool *resu
JSAtom *atom = Atomize(cx, name, strlen(name));
if (!atom)
return false;
return JSObject::deleteByValue(cx, obj, StringValue(atom), result);
RootedId id(cx, AtomToId(atom));
return JSObject::deleteGeneric(cx, obj, id, result);
}
JS_PUBLIC_API(bool)
@ -3666,7 +3667,8 @@ JS_DeleteUCProperty2(JSContext *cx, HandleObject obj, const jschar *name, size_t
JSAtom *atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
if (!atom)
return false;
return JSObject::deleteByValue(cx, obj, StringValue(atom), result);
RootedId id(cx, AtomToId(atom));
return JSObject::deleteGeneric(cx, obj, id, result);
}
JS_PUBLIC_API(bool)

View File

@ -337,10 +337,10 @@ DeleteArrayElement(JSContext *cx, HandleObject obj, double index, bool *succeede
return true;
}
if (index <= UINT32_MAX)
return JSObject::deleteElement(cx, obj, uint32_t(index), succeeded);
return JSObject::deleteByValue(cx, obj, DoubleValue(index), succeeded);
RootedId id(cx);
if (!ToId(cx, index, &id))
return false;
return JSObject::deleteGeneric(cx, obj, id, succeeded);
}
/* ES6 20130308 draft 9.3.5 */
@ -1140,7 +1140,7 @@ array_toString(JSContext *cx, unsigned argc, Value *vp)
if (!JSObject::getProperty(cx, obj, obj, cx->names().join, &join))
return false;
if (!js_IsCallable(join)) {
if (!IsCallable(join)) {
JSString *str = JS_BasicObjectToString(cx, obj);
if (!str)
return false;
@ -2992,6 +2992,7 @@ static const JSFunctionSpec array_static_methods[] = {
JS_SELF_HOSTED_FN("some", "ArrayStaticSome", 2,0),
JS_SELF_HOSTED_FN("reduce", "ArrayStaticReduce", 2,0),
JS_SELF_HOSTED_FN("reduceRight", "ArrayStaticReduceRight", 2,0),
JS_SELF_HOSTED_FN("from", "ArrayFrom", 3,0),
JS_FN("of", array_of, 0,0),
#ifdef ENABLE_PARALLEL_JS

View File

@ -365,13 +365,6 @@ AtomizeAndCopyChars(ExclusiveContext *cx, const jschar *tbchars, size_t length,
if (pp)
return pp->asPtr();
/*
* If a GC occurs at js_NewStringCopy then |p| will still have the correct
* hash, allowing us to avoid rehashing it. Even though the hash is
* unchanged, we need to re-lookup the table position because a last-ditch
* GC will potentially free some table entries.
*/
AutoLockForExclusiveAccess lock(cx);
AtomSet& atoms = cx->atoms();
@ -392,7 +385,10 @@ AtomizeAndCopyChars(ExclusiveContext *cx, const jschar *tbchars, size_t length,
JSAtom *atom = flat->morphAtomizedStringIntoAtom();
if (!atoms.relookupOrAdd(p, lookup, AtomStateEntry(atom, bool(ib)))) {
// We have held the lock since looking up p, and the operations we've done
// since then can't GC; therefore the atoms table has not been modified and
// p is still valid.
if (!atoms.add(p, AtomStateEntry(atom, bool(ib)))) {
js_ReportOutOfMemory(cx); /* SystemAllocPolicy does not report OOM. */
return nullptr;
}

View File

@ -1016,7 +1016,7 @@ bool intrinsic_ThrowError(JSContext *cx, unsigned argc, Value *vp);
bool intrinsic_NewDenseArray(JSContext *cx, unsigned argc, Value *vp);
bool intrinsic_UnsafePutElements(JSContext *cx, unsigned argc, Value *vp);
bool intrinsic_DefineValueProperty(JSContext *cx, unsigned argc, Value *vp);
bool intrinsic_DefineDataProperty(JSContext *cx, unsigned argc, Value *vp);
bool intrinsic_UnsafeSetReservedSlot(JSContext *cx, unsigned argc, Value *vp);
bool intrinsic_UnsafeGetReservedSlot(JSContext *cx, unsigned argc, Value *vp);
bool intrinsic_HaveSameClass(JSContext *cx, unsigned argc, Value *vp);

View File

@ -2479,7 +2479,7 @@ date_toJSON(JSContext *cx, unsigned argc, Value *vp)
return false;
/* Step 5. */
if (!js_IsCallable(toISO)) {
if (!IsCallable(toISO)) {
JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, nullptr,
JSMSG_BAD_TOISOSTRING_PROP);
return false;

View File

@ -283,8 +283,7 @@ namespace js {
js::proxy_SetElement, \
js::proxy_GetGenericAttributes, \
js::proxy_SetGenericAttributes, \
js::proxy_DeleteProperty, \
js::proxy_DeleteElement, \
js::proxy_DeleteGeneric, \
js::proxy_Watch, js::proxy_Unwatch, \
js::proxy_Slice, \
nullptr, /* enumerate */ \
@ -349,10 +348,7 @@ proxy_GetGenericAttributes(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
extern JS_FRIEND_API(bool)
proxy_SetGenericAttributes(JSContext *cx, JS::HandleObject obj, JS::HandleId id, unsigned *attrsp);
extern JS_FRIEND_API(bool)
proxy_DeleteProperty(JSContext *cx, JS::HandleObject obj, JS::Handle<PropertyName*> name,
bool *succeeded);
extern JS_FRIEND_API(bool)
proxy_DeleteElement(JSContext *cx, JS::HandleObject obj, uint32_t index, bool *succeeded);
proxy_DeleteGeneric(JSContext *cx, JS::HandleObject obj, JS::HandleId id, bool *succeeded);
extern JS_FRIEND_API(void)
proxy_Trace(JSTracer *trc, JSObject *obj);

Some files were not shown because too many files have changed in this diff Show More