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

This commit is contained in:
Ryan VanderMeulen 2015-02-17 21:24:49 -05:00
commit 75cd780807
212 changed files with 11012 additions and 6180 deletions

View File

@ -225,7 +225,7 @@ DocAccessibleChild::RecvTextSubstring(const uint64_t& aID,
bool bool
DocAccessibleChild::RecvGetTextAfterOffset(const uint64_t& aID, DocAccessibleChild::RecvGetTextAfterOffset(const uint64_t& aID,
const int32_t& aOffset, const int32_t& aOffset,
const AccessibleTextBoundary& aBoundaryType, const int32_t& aBoundaryType,
nsString* aText, nsString* aText,
int32_t* aStartOffset, int32_t* aStartOffset,
int32_t* aEndOffset) int32_t* aEndOffset)
@ -243,7 +243,7 @@ DocAccessibleChild::RecvGetTextAfterOffset(const uint64_t& aID,
bool bool
DocAccessibleChild::RecvGetTextAtOffset(const uint64_t& aID, DocAccessibleChild::RecvGetTextAtOffset(const uint64_t& aID,
const int32_t& aOffset, const int32_t& aOffset,
const AccessibleTextBoundary& aBoundaryType, const int32_t& aBoundaryType,
nsString* aText, nsString* aText,
int32_t* aStartOffset, int32_t* aStartOffset,
int32_t* aEndOffset) int32_t* aEndOffset)
@ -261,7 +261,7 @@ DocAccessibleChild::RecvGetTextAtOffset(const uint64_t& aID,
bool bool
DocAccessibleChild::RecvGetTextBeforeOffset(const uint64_t& aID, DocAccessibleChild::RecvGetTextBeforeOffset(const uint64_t& aID,
const int32_t& aOffset, const int32_t& aOffset,
const AccessibleTextBoundary& aBoundaryType, const int32_t& aBoundaryType,
nsString* aText, nsString* aText,
int32_t* aStartOffset, int32_t* aStartOffset,
int32_t* aEndOffset) int32_t* aEndOffset)

View File

@ -70,17 +70,17 @@ public:
virtual bool RecvGetTextAfterOffset(const uint64_t& aID, virtual bool RecvGetTextAfterOffset(const uint64_t& aID,
const int32_t& aOffset, const int32_t& aOffset,
const AccessibleTextBoundary& aBoundaryType, const int32_t& aBoundaryType,
nsString* aText, int32_t* aStartOffset, nsString* aText, int32_t* aStartOffset,
int32_t* aEndOffset) MOZ_OVERRIDE; int32_t* aEndOffset) MOZ_OVERRIDE;
virtual bool RecvGetTextAtOffset(const uint64_t& aID, virtual bool RecvGetTextAtOffset(const uint64_t& aID,
const int32_t& aOffset, const int32_t& aOffset,
const AccessibleTextBoundary& aBoundaryType, const int32_t& aBoundaryType,
nsString* aText, int32_t* aStartOffset, nsString* aText, int32_t* aStartOffset,
int32_t* aEndOffset) MOZ_OVERRIDE; int32_t* aEndOffset) MOZ_OVERRIDE;
virtual bool RecvGetTextBeforeOffset(const uint64_t& aID, virtual bool RecvGetTextBeforeOffset(const uint64_t& aID,
const int32_t& aOffset, const int32_t& aOffset,
const AccessibleTextBoundary& aBoundaryType, const int32_t& aBoundaryType,
nsString* aText, int32_t* aStartOffset, nsString* aText, int32_t* aStartOffset,
int32_t* aEndOffset) MOZ_OVERRIDE; int32_t* aEndOffset) MOZ_OVERRIDE;

View File

@ -6,8 +6,6 @@
include protocol PContent; include protocol PContent;
using AccessibleTextBoundary from "nsIAccessibleText.h";
namespace mozilla { namespace mozilla {
namespace a11y { namespace a11y {
@ -69,11 +67,11 @@ child:
// TextSubstring is getText in IDL. // TextSubstring is getText in IDL.
prio(high) sync TextSubstring(uint64_t aID, int32_t aStartOffset, int32_t prio(high) sync TextSubstring(uint64_t aID, int32_t aStartOffset, int32_t
aEndOffset) returns(nsString aText); aEndOffset) returns(nsString aText);
prio(high) sync GetTextAfterOffset(uint64_t aID, int32_t aOffset, AccessibleTextBoundary aBoundaryType) prio(high) sync GetTextAfterOffset(uint64_t aID, int32_t aOffset, int32_t aBoundaryType)
returns(nsString aText, int32_t aStartOffset, int32_t aEndOffset); returns(nsString aText, int32_t aStartOffset, int32_t aEndOffset);
prio(high) sync GetTextAtOffset(uint64_t aID, int32_t aOffset, AccessibleTextBoundary aBoundaryType) prio(high) sync GetTextAtOffset(uint64_t aID, int32_t aOffset, int32_t aBoundaryType)
returns(nsString aText, int32_t aStartOffset, int32_t aEndOffset); returns(nsString aText, int32_t aStartOffset, int32_t aEndOffset);
prio(high) sync GetTextBeforeOffset(uint64_t aID, int32_t aOffset, AccessibleTextBoundary aBoundaryType) prio(high) sync GetTextBeforeOffset(uint64_t aID, int32_t aOffset, int32_t aBoundaryType)
returns(nsString aText, int32_t aStartOffset, int32_t aEndOffset); returns(nsString aText, int32_t aStartOffset, int32_t aEndOffset);
}; };

View File

@ -688,6 +688,9 @@ pref("ui.useOverlayScrollbars", 1);
pref("ui.scrollbarFadeBeginDelay", 450); pref("ui.scrollbarFadeBeginDelay", 450);
pref("ui.scrollbarFadeDuration", 200); pref("ui.scrollbarFadeDuration", 200);
// Scrollbar position follows the document `dir` attribute
pref("layout.scrollbar.side", 1);
// Enable the ProcessPriorityManager, and give processes with no visible // Enable the ProcessPriorityManager, and give processes with no visible
// documents a 1s grace period before they're eligible to be marked as // documents a 1s grace period before they're eligible to be marked as
// background. Background processes that are perceivable due to playing // background. Background processes that are perceivable due to playing
@ -1114,10 +1117,8 @@ pref("dom.requestSync.enabled", true);
pref("gfx.vsync.hw-vsync.enabled", true); pref("gfx.vsync.hw-vsync.enabled", true);
pref("gfx.vsync.compositor", true); pref("gfx.vsync.compositor", true);
pref("gfx.touch.resample", true); pref("gfx.touch.resample", true);
pref("gfx.vsync.refreshdriver", true);
#else #else
pref("gfx.vsync.hw-vsync.enabled", false); pref("gfx.vsync.hw-vsync.enabled", false);
pref("gfx.vsync.compositor", false); pref("gfx.vsync.compositor", false);
pref("gfx.touch.resample", false); pref("gfx.touch.resample", false);
pref("gfx.vsync.refreshdriver", false);
#endif #endif

View File

@ -36,6 +36,13 @@ xul|scrollbar[orient="vertical"] {
max-width: 8px; max-width: 8px;
} }
/* workaround for bug 1119057: as -moz-margin-start may not work as expected,
* force a right margin value in RTL mode. */
[dir="rtl"] xul|scrollbar[root="true"][orient="vertical"] {
-moz-margin-start: unset;
margin-right: -8px;
}
xul|scrollbar[orient="vertical"] xul|thumb { xul|scrollbar[orient="vertical"] xul|thumb {
max-width: 6px !important; max-width: 6px !important;
min-width: 6px !important; min-width: 6px !important;

View File

@ -15,11 +15,11 @@
<project name="platform_build" path="build" remote="b2g" revision="cdaa0a4ac28c781709df8c318ed079e9e475503a"> <project name="platform_build" path="build" remote="b2g" revision="cdaa0a4ac28c781709df8c318ed079e9e475503a">
<copyfile dest="Makefile" src="core/root.mk"/> <copyfile dest="Makefile" src="core/root.mk"/>
</project> </project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="4f39e48b95fa00c8669b8707447542024bb55432"/> <project name="gaia" path="gaia" remote="mozillaorg" revision="82f286f10a41aab84a0796c89fbefe67b179994b"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/> <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/> <project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/> <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/> <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/> <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>

View File

@ -19,12 +19,12 @@
<copyfile dest="Makefile" src="core/root.mk"/> <copyfile dest="Makefile" src="core/root.mk"/>
</project> </project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/> <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="4f39e48b95fa00c8669b8707447542024bb55432"/> <project name="gaia.git" path="gaia" remote="mozillaorg" revision="82f286f10a41aab84a0796c89fbefe67b179994b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/> <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eb1795a9002eb142ac58c8d68f8f4ba094af07ca"/> <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eb1795a9002eb142ac58c8d68f8f4ba094af07ca"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="2c31ac3a31a340b40ecd9c291df9b9613d3afa72"/> <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="2c31ac3a31a340b40ecd9c291df9b9613d3afa72"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/> <project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d0d11d190ccc50d7d66009bcc896ad4b42d3f0d"/> <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d0d11d190ccc50d7d66009bcc896ad4b42d3f0d"/>
<!-- Stock Android things --> <!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/> <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>

View File

@ -17,9 +17,9 @@
</project> </project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/> <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="4f39e48b95fa00c8669b8707447542024bb55432"/> <project name="gaia" path="gaia" remote="mozillaorg" revision="82f286f10a41aab84a0796c89fbefe67b179994b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/> <project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d0d11d190ccc50d7d66009bcc896ad4b42d3f0d"/> <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d0d11d190ccc50d7d66009bcc896ad4b42d3f0d"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/> <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/> <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>

View File

@ -15,11 +15,11 @@
<project name="platform_build" path="build" remote="b2g" revision="cdaa0a4ac28c781709df8c318ed079e9e475503a"> <project name="platform_build" path="build" remote="b2g" revision="cdaa0a4ac28c781709df8c318ed079e9e475503a">
<copyfile dest="Makefile" src="core/root.mk"/> <copyfile dest="Makefile" src="core/root.mk"/>
</project> </project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="4f39e48b95fa00c8669b8707447542024bb55432"/> <project name="gaia" path="gaia" remote="mozillaorg" revision="82f286f10a41aab84a0796c89fbefe67b179994b"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/> <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/> <project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/> <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/> <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/> <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>

View File

@ -19,12 +19,12 @@
<copyfile dest="Makefile" src="core/root.mk"/> <copyfile dest="Makefile" src="core/root.mk"/>
</project> </project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/> <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="4f39e48b95fa00c8669b8707447542024bb55432"/> <project name="gaia.git" path="gaia" remote="mozillaorg" revision="82f286f10a41aab84a0796c89fbefe67b179994b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/> <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eb1795a9002eb142ac58c8d68f8f4ba094af07ca"/> <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eb1795a9002eb142ac58c8d68f8f4ba094af07ca"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="2c31ac3a31a340b40ecd9c291df9b9613d3afa72"/> <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="2c31ac3a31a340b40ecd9c291df9b9613d3afa72"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/> <project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d0d11d190ccc50d7d66009bcc896ad4b42d3f0d"/> <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d0d11d190ccc50d7d66009bcc896ad4b42d3f0d"/>
<!-- Stock Android things --> <!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/> <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>

View File

@ -15,11 +15,11 @@
<project name="platform_build" path="build" remote="b2g" revision="cdaa0a4ac28c781709df8c318ed079e9e475503a"> <project name="platform_build" path="build" remote="b2g" revision="cdaa0a4ac28c781709df8c318ed079e9e475503a">
<copyfile dest="Makefile" src="core/root.mk"/> <copyfile dest="Makefile" src="core/root.mk"/>
</project> </project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="4f39e48b95fa00c8669b8707447542024bb55432"/> <project name="gaia" path="gaia" remote="mozillaorg" revision="82f286f10a41aab84a0796c89fbefe67b179994b"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/> <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/> <project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/> <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/> <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/> <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>

View File

@ -17,9 +17,9 @@
</project> </project>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/> <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="4f39e48b95fa00c8669b8707447542024bb55432"/> <project name="gaia" path="gaia" remote="mozillaorg" revision="82f286f10a41aab84a0796c89fbefe67b179994b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/> <project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d0d11d190ccc50d7d66009bcc896ad4b42d3f0d"/> <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d0d11d190ccc50d7d66009bcc896ad4b42d3f0d"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/> <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/> <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>

View File

@ -1,9 +1,9 @@
{ {
"git": { "git": {
"git_revision": "4f39e48b95fa00c8669b8707447542024bb55432", "git_revision": "82f286f10a41aab84a0796c89fbefe67b179994b",
"remote": "https://git.mozilla.org/releases/gaia.git", "remote": "https://git.mozilla.org/releases/gaia.git",
"branch": "" "branch": ""
}, },
"revision": "e0816d2581cdc2d0581f625c06811128c87c0c48", "revision": "066f0e84321a010700467d1814ee0048dca7e5e1",
"repo_path": "integration/gaia-central" "repo_path": "integration/gaia-central"
} }

View File

@ -17,9 +17,9 @@
</project> </project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/> <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="4f39e48b95fa00c8669b8707447542024bb55432"/> <project name="gaia" path="gaia" remote="mozillaorg" revision="82f286f10a41aab84a0796c89fbefe67b179994b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/> <project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d0d11d190ccc50d7d66009bcc896ad4b42d3f0d"/> <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d0d11d190ccc50d7d66009bcc896ad4b42d3f0d"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/> <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/> <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>

View File

@ -15,11 +15,11 @@
<project name="platform_build" path="build" remote="b2g" revision="7f2ee9f4cb926684883fc2a2e407045fd9db2199"> <project name="platform_build" path="build" remote="b2g" revision="7f2ee9f4cb926684883fc2a2e407045fd9db2199">
<copyfile dest="Makefile" src="core/root.mk"/> <copyfile dest="Makefile" src="core/root.mk"/>
</project> </project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="4f39e48b95fa00c8669b8707447542024bb55432"/> <project name="gaia" path="gaia" remote="mozillaorg" revision="82f286f10a41aab84a0796c89fbefe67b179994b"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/> <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/> <project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/> <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/> <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/> <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>

View File

@ -1809,8 +1809,8 @@ pref("experiments.manifest.uri", "https://telemetry-experiment.cdn.mozilla.net/m
// Whether experiments are supported by the current application profile. // Whether experiments are supported by the current application profile.
pref("experiments.supported", true); pref("experiments.supported", true);
// Enable the OpenH264 plugin support in the addon manager. // Enable GMP support in the addon manager.
pref("media.gmp-gmpopenh264.provider.enabled", true); pref("media.gmp-provider.enabled", true);
pref("browser.apps.URL", "https://marketplace.firefox.com/discovery/"); pref("browser.apps.URL", "https://marketplace.firefox.com/discovery/");

View File

@ -69,9 +69,16 @@ CallProgressSocket.prototype = {
let uri = Services.io.newURI(this._progressUrl, null, null); let uri = Services.io.newURI(this._progressUrl, null, null);
// Allow _websocket to be set for testing. // Allow _websocket to be set for testing.
this._websocket = this._websocket || if (!this._websocket) {
Cc["@mozilla.org/network/protocol;1?name=" + uri.scheme] this._websocket = Cc["@mozilla.org/network/protocol;1?name=" + uri.scheme]
.createInstance(Ci.nsIWebSocketChannel); .createInstance(Ci.nsIWebSocketChannel);
this._websocket.initLoadInfo(null, // aLoadingNode
Services.scriptSecurityManager.getSystemPrincipal(),
null, // aTriggeringPrincipal
Ci.nsILoadInfo.SEC_NORMAL,
Ci.nsIContentPolicy.TYPE_WEBSOCKET);
}
this._websocket.asyncOpen(uri, this._progressUrl, this, null); this._websocket.asyncOpen(uri, this._progressUrl, this, null);
}, },

View File

@ -61,6 +61,11 @@ PushSocket.prototype = {
if (!this._websocket) { if (!this._websocket) {
this._websocket = Cc["@mozilla.org/network/protocol;1?name=wss"] this._websocket = Cc["@mozilla.org/network/protocol;1?name=wss"]
.createInstance(Ci.nsIWebSocketChannel); .createInstance(Ci.nsIWebSocketChannel);
this._websocket.initLoadInfo(null, // aLoadingNode
Services.scriptSecurityManager.getSystemPrincipal(),
null, // aTriggeringPrincipal
Ci.nsILoadInfo.SEC_NORMAL,
Ci.nsIContentPolicy.TYPE_WEBSOCKET);
} }
let uri = Services.io.newURI(pushUri, null, null); let uri = Services.io.newURI(pushUri, null, null);

View File

@ -20,6 +20,7 @@ namespace mozilla {
namespace net { namespace net {
class HttpChannelParent; class HttpChannelParent;
class FTPChannelParent; class FTPChannelParent;
class WebSocketChannelParent;
} }
/** /**
@ -51,6 +52,7 @@ private:
friend class net::HttpChannelParent; friend class net::HttpChannelParent;
friend class net::FTPChannelParent; friend class net::FTPChannelParent;
friend class net::WebSocketChannelParent;
~LoadInfo(); ~LoadInfo();

View File

@ -590,7 +590,16 @@ SendPing(void* aClosure, nsIContent* aContent, nsIURI* aURI,
nsIDocument* doc = aContent->OwnerDoc(); nsIDocument* doc = aContent->OwnerDoc();
nsCOMPtr<nsIChannel> chan; nsCOMPtr<nsIChannel> chan;
aIOService->NewChannelFromURI(aURI, getter_AddRefs(chan)); NS_NewChannel(getter_AddRefs(chan),
aURI,
doc,
nsILoadInfo::SEC_NORMAL,
nsIContentPolicy::TYPE_PING,
nullptr, // aLoadGroup
nullptr, // aCallbacks
nsIRequest::LOAD_NORMAL, // aLoadFlags,
aIOService);
if (!chan) { if (!chan) {
return; return;
} }

View File

@ -90,6 +90,7 @@ public:
, mHasFeatureRegistered(false) , mHasFeatureRegistered(false)
#endif #endif
, mIsMainThread(true) , mIsMainThread(true)
, mMutex("WebSocketImpl::mMutex")
, mWorkerShuttingDown(false) , mWorkerShuttingDown(false)
{ {
if (!NS_IsMainThread()) { if (!NS_IsMainThread()) {
@ -222,6 +223,9 @@ public:
nsWeakPtr mWeakLoadGroup; nsWeakPtr mWeakLoadGroup;
bool mIsMainThread; bool mIsMainThread;
// This mutex protects mWorkerShuttingDown.
mozilla::Mutex mMutex;
bool mWorkerShuttingDown; bool mWorkerShuttingDown;
private: private:
@ -422,7 +426,14 @@ public:
~MaybeDisconnect() ~MaybeDisconnect()
{ {
if (mImpl->mWorkerShuttingDown) { bool toDisconnect = false;
{
MutexAutoLock lock(mImpl->mMutex);
toDisconnect = mImpl->mWorkerShuttingDown;
}
if (toDisconnect) {
mImpl->Disconnect(); mImpl->Disconnect();
} }
} }
@ -886,7 +897,7 @@ WebSocket::WebSocket(nsPIDOMWindow* aOwnerWindow)
, mCheckMustKeepAlive(true) , mCheckMustKeepAlive(true)
, mOutgoingBufferedAmount(0) , mOutgoingBufferedAmount(0)
, mBinaryType(dom::BinaryType::Blob) , mBinaryType(dom::BinaryType::Blob)
, mMutex("WebSocketImpl::mMutex") , mMutex("WebSocket::mMutex")
, mReadyState(CONNECTING) , mReadyState(CONNECTING)
{ {
mImpl = new WebSocketImpl(this); mImpl = new WebSocketImpl(this);
@ -1563,15 +1574,11 @@ WebSocketImpl::InitializeConnection()
// are not thread-safe. // are not thread-safe.
mOriginDocument = nullptr; mOriginDocument = nullptr;
nsCOMPtr<nsILoadInfo> loadInfo = wsChannel->InitLoadInfo(doc ? doc->AsDOMNode() : nullptr,
new LoadInfo(doc ? doc ? doc->NodePrincipal() : mPrincipal.get(),
doc->NodePrincipal() : mPrincipal.get(), mPrincipal,
mPrincipal, nsILoadInfo::SEC_NORMAL,
doc, nsIContentPolicy::TYPE_WEBSOCKET);
nsILoadInfo::SEC_NORMAL,
nsIContentPolicy::TYPE_WEBSOCKET);
rv = wsChannel->SetLoadInfo(loadInfo);
NS_ENSURE_SUCCESS(rv, rv);
if (!mRequestedProtocolList.IsEmpty()) { if (!mRequestedProtocolList.IsEmpty()) {
rv = wsChannel->SetProtocol(mRequestedProtocolList); rv = wsChannel->SetProtocol(mRequestedProtocolList);
@ -1982,7 +1989,11 @@ public:
MOZ_ASSERT(aStatus > workers::Running); MOZ_ASSERT(aStatus > workers::Running);
if (aStatus >= Canceling) { if (aStatus >= Canceling) {
mWebSocketImpl->mWorkerShuttingDown = true; {
MutexAutoLock lock(mWebSocketImpl->mMutex);
mWebSocketImpl->mWorkerShuttingDown = true;
}
mWebSocketImpl->CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY); mWebSocketImpl->CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY);
} }
@ -1991,7 +2002,11 @@ public:
bool Suspend(JSContext* aCx) MOZ_OVERRIDE bool Suspend(JSContext* aCx) MOZ_OVERRIDE
{ {
mWebSocketImpl->mWorkerShuttingDown = true; {
MutexAutoLock lock(mWebSocketImpl->mMutex);
mWebSocketImpl->mWorkerShuttingDown = true;
}
mWebSocketImpl->CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY); mWebSocketImpl->CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY);
return true; return true;
} }
@ -2551,9 +2566,13 @@ namespace {
class WorkerRunnableDispatcher MOZ_FINAL : public WorkerRunnable class WorkerRunnableDispatcher MOZ_FINAL : public WorkerRunnable
{ {
nsRefPtr<WebSocketImpl> mWebSocketImpl;
public: public:
WorkerRunnableDispatcher(WorkerPrivate* aWorkerPrivate, nsIRunnable* aEvent) WorkerRunnableDispatcher(WebSocketImpl* aImpl, WorkerPrivate* aWorkerPrivate,
nsIRunnable* aEvent)
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
, mWebSocketImpl(aImpl)
, mEvent(aEvent) , mEvent(aEvent)
{ {
} }
@ -2561,6 +2580,13 @@ public:
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
{ {
aWorkerPrivate->AssertIsOnWorkerThread(); aWorkerPrivate->AssertIsOnWorkerThread();
// No messages when disconnected.
if (mWebSocketImpl->mDisconnectingOrDisconnected) {
NS_WARNING("Dispatching a WebSocket event after the disconnection!");
return true;
}
aWorkerPrivate->ModifyBusyCountFromWorker(aCx, true); aWorkerPrivate->ModifyBusyCountFromWorker(aCx, true);
return !NS_FAILED(mEvent->Run()); return !NS_FAILED(mEvent->Run());
} }
@ -2596,12 +2622,12 @@ WebSocketImpl::Dispatch(nsIRunnable* aEvent, uint32_t aFlags)
return NS_DispatchToMainThread(aEvent); return NS_DispatchToMainThread(aEvent);
} }
// No messages when disconnected. // If the target is a worker, we have to use a custom WorkerRunnableDispatcher
if (mDisconnectingOrDisconnected) { // runnable.
NS_WARNING("Dispatching a WebSocket event after the disconnection!"); nsRefPtr<WorkerRunnableDispatcher> event =
return NS_OK; new WorkerRunnableDispatcher(this, mWorkerPrivate, aEvent);
}
MutexAutoLock lock(mMutex);
if (mWorkerShuttingDown) { if (mWorkerShuttingDown) {
return NS_OK; return NS_OK;
} }
@ -2612,10 +2638,6 @@ WebSocketImpl::Dispatch(nsIRunnable* aEvent, uint32_t aFlags)
MOZ_ASSERT(HasFeatureRegistered()); MOZ_ASSERT(HasFeatureRegistered());
#endif #endif
// If the target is a worker, we have to use a custom WorkerRunnableDispatcher
// runnable.
nsRefPtr<WorkerRunnableDispatcher> event =
new WorkerRunnableDispatcher(mWorkerPrivate, aEvent);
if (!event->Dispatch(nullptr)) { if (!event->Dispatch(nullptr)) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }

View File

@ -1667,14 +1667,30 @@ BluetoothServiceBluedroid::BondStateChangedNotification(
NS_LITERAL_STRING(KEY_ADAPTER), NS_LITERAL_STRING(KEY_ADAPTER),
BluetoothValue(propertiesArray))); BluetoothValue(propertiesArray)));
if (bonded && !sBondingRunnableArray.IsEmpty()) { if (aStatus == STATUS_SUCCESS) {
DispatchBluetoothReply(sBondingRunnableArray[0], // Resolve existing pair/unpair promise when pair/unpair succeeded
BluetoothValue(true), EmptyString()); if (bonded && !sBondingRunnableArray.IsEmpty()) {
sBondingRunnableArray.RemoveElementAt(0); DispatchBluetoothReply(sBondingRunnableArray[0],
} else if (!bonded && !sUnbondingRunnableArray.IsEmpty()) { BluetoothValue(true), EmptyString());
DispatchBluetoothReply(sUnbondingRunnableArray[0], sBondingRunnableArray.RemoveElementAt(0);
BluetoothValue(true), EmptyString()); } else if (!bonded && !sUnbondingRunnableArray.IsEmpty()) {
sUnbondingRunnableArray.RemoveElementAt(0); DispatchBluetoothReply(sUnbondingRunnableArray[0],
BluetoothValue(true), EmptyString());
sUnbondingRunnableArray.RemoveElementAt(0);
}
} else {
// Reject existing pair/unpair promise when pair/unpair failed
if (!bonded && !sBondingRunnableArray.IsEmpty()) {
DispatchBluetoothReply(sBondingRunnableArray[0],
BluetoothValue(),
NS_LITERAL_STRING("Pair Error"));
sBondingRunnableArray.RemoveElementAt(0);
} else if (bonded && !sUnbondingRunnableArray.IsEmpty()) {
DispatchBluetoothReply(sUnbondingRunnableArray[0],
BluetoothValue(),
NS_LITERAL_STRING("Unpair Error"));
sUnbondingRunnableArray.RemoveElementAt(0);
}
} }
} }

View File

@ -20,5 +20,9 @@ mimetype_label=MIME Type
description_label=Description description_label=Description
suffixes_label=Suffixes suffixes_label=Suffixes
# GMP Plugins
openH264_name=OpenH264 Video Codec provided by Cisco Systems, Inc. openH264_name=OpenH264 Video Codec provided by Cisco Systems, Inc.
openH264_description=Play back web video and use video chats. openH264_description=Play back web video and use video chats.
eme-adobe_name=Primetime Content Decryption Module provided by Adobe Systems, Incorporated
eme-adobe_description=Play back protected web video.

View File

@ -2958,7 +2958,7 @@ MediaDecoderStateMachine::FlushDecoding()
// The reader is not supposed to put any tasks to deliver samples into // The reader is not supposed to put any tasks to deliver samples into
// the queue after this runs (unless we request another sample from it). // the queue after this runs (unless we request another sample from it).
RefPtr<nsIRunnable> task; RefPtr<nsIRunnable> task;
task = NS_NewRunnableMethod(mReader, &MediaDecoderReader::ResetDecode); task = NS_NewRunnableMethod(this, &MediaDecoderStateMachine::ResetDecode);
// Wait for the ResetDecode to run and for the decoder to abort // Wait for the ResetDecode to run and for the decoder to abort
// decoding operations and run any pending callbacks. This is // decoding operations and run any pending callbacks. This is
@ -2979,6 +2979,25 @@ MediaDecoderStateMachine::FlushDecoding()
ResetPlayback(); ResetPlayback();
} }
void
MediaDecoderStateMachine::ResetDecode()
{
NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
if (!mReader) {
return;
}
{
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
if (mWaitingForDecoderSeek && !mCancelingSeek) {
mReader->CancelSeek();
mCancelingSeek = true;
}
}
mReader->ResetDecode();
}
void MediaDecoderStateMachine::RenderVideoFrame(VideoData* aData, void MediaDecoderStateMachine::RenderVideoFrame(VideoData* aData,
TimeStamp aTarget) TimeStamp aTarget)
{ {

View File

@ -422,6 +422,9 @@ public:
WaitRequestRef(aRejection.mType).Complete(); WaitRequestRef(aRejection.mType).Complete();
} }
// Resets all state related to decoding, emptying all buffers etc.
void ResetDecode();
private: private:
void AcquireMonitorAndInvokeDecodeError(); void AcquireMonitorAndInvokeDecodeError();

View File

@ -805,9 +805,8 @@ RTCPeerConnection.prototype = {
}, },
removeTrack: function(sender) { removeTrack: function(sender) {
// Bug 844295: Not implementing this functionality. this._checkClosed();
throw new this._win.DOMException("removeTrack not yet implemented", this._impl.removeTrack(sender.track);
"NotSupportedError");
}, },
_replaceTrack: function(sender, withTrack) { _replaceTrack: function(sender, withTrack) {
@ -1115,6 +1114,10 @@ PeerConnectionObserver.prototype = {
} }
}, },
onNegotiationNeeded: function() {
this.dispatchEvent(new this._win.Event("negotiationneeded"));
},
// This method is primarily responsible for updating iceConnectionState. // This method is primarily responsible for updating iceConnectionState.
// This state is defined in the WebRTC specification as follows: // This state is defined in the WebRTC specification as follows:

View File

@ -121,7 +121,7 @@ MediaKeySystemAccess::GetKeySystemStatus(const nsAString& aKeySystem)
if (!IsVistaOrLater()) { if (!IsVistaOrLater()) {
return MediaKeySystemStatus::Cdm_not_supported; return MediaKeySystemStatus::Cdm_not_supported;
} }
if (!Preferences::GetBool("media.eme.adobe-access.enabled", false)) { if (!Preferences::GetBool("media.gmp-eme-adobe.enabled", false)) {
return MediaKeySystemStatus::Cdm_disabled; return MediaKeySystemStatus::Cdm_disabled;
} }
if (!HaveGMPFor(mps, if (!HaveGMPFor(mps,

View File

@ -17,154 +17,159 @@ function getBlobContent(blob) {
}); });
} }
var commandsCreateDataChannel = [
function PC_REMOTE_EXPECT_DATA_CHANNEL(test) {
test.pcRemote.expectDataChannel();
},
function PC_LOCAL_CREATE_DATA_CHANNEL(test) {
var channel = test.pcLocal.createDataChannel({});
is(channel.binaryType, "blob", channel + " is of binary type 'blob'");
is(channel.readyState, "connecting", channel + " is in state: 'connecting'");
is(test.pcLocal.signalingState, STABLE,
"Create datachannel does not change signaling state");
}
];
var commandsWaitForDataChannel = [
function PC_LOCAL_VERIFY_DATA_CHANNEL_STATE(test) {
return test.pcLocal.dataChannels[0].opened;
},
function PC_REMOTE_VERIFY_DATA_CHANNEL_STATE(test) {
return test.pcRemote.nextDataChannel.then(channel => channel.opened);
},
];
var commandsCheckDataChannel = [
function SEND_MESSAGE(test) {
var message = "Lorem ipsum dolor sit amet";
return test.send(message).then(result => {
is(result.data, message, "Message correctly transmitted from pcLocal to pcRemote.");
});
},
function SEND_BLOB(test) {
var contents = ["At vero eos et accusam et justo duo dolores et ea rebum."];
var blob = new Blob(contents, { "type" : "text/plain" });
return test.send(blob).then(result => {
ok(result.data instanceof Blob, "Received data is of instance Blob");
is(result.data.size, blob.size, "Received data has the correct size.");
return getBlobContent(result.data);
}).then(recv_contents =>
is(recv_contents, contents, "Received data has the correct content."));
},
function CREATE_SECOND_DATA_CHANNEL(test) {
return test.createDataChannel({ }).then(result => {
var sourceChannel = result.local;
var targetChannel = result.remote;
is(sourceChannel.readyState, "open", sourceChannel + " is in state: 'open'");
is(targetChannel.readyState, "open", targetChannel + " is in state: 'open'");
is(targetChannel.binaryType, "blob", targetChannel + " is of binary type 'blob'");
});
},
function SEND_MESSAGE_THROUGH_LAST_OPENED_CHANNEL(test) {
var channels = test.pcRemote.dataChannels;
var message = "I am the Omega";
return test.send(message).then(result => {
is(channels.indexOf(result.channel), channels.length - 1, "Last channel used");
is(result.data, message, "Received message has the correct content.");
});
},
function SEND_MESSAGE_THROUGH_FIRST_CHANNEL(test) {
var message = "Message through 1st channel";
var options = {
sourceChannel: test.pcLocal.dataChannels[0],
targetChannel: test.pcRemote.dataChannels[0]
};
return test.send(message, options).then(result => {
is(test.pcRemote.dataChannels.indexOf(result.channel), 0, "1st channel used");
is(result.data, message, "Received message has the correct content.");
});
},
function SEND_MESSAGE_BACK_THROUGH_FIRST_CHANNEL(test) {
var message = "Return a message also through 1st channel";
var options = {
sourceChannel: test.pcRemote.dataChannels[0],
targetChannel: test.pcLocal.dataChannels[0]
};
return test.send(message, options).then(result => {
is(test.pcLocal.dataChannels.indexOf(result.channel), 0, "1st channel used");
is(result.data, message, "Return message has the correct content.");
});
},
function CREATE_NEGOTIATED_DATA_CHANNEL(test) {
var options = {
negotiated:true,
id: 5,
protocol: "foo/bar",
ordered: false,
maxRetransmits: 500
};
return test.createDataChannel(options).then(result => {
var sourceChannel2 = result.local;
var targetChannel2 = result.remote;
is(sourceChannel2.readyState, "open", sourceChannel2 + " is in state: 'open'");
is(targetChannel2.readyState, "open", targetChannel2 + " is in state: 'open'");
is(targetChannel2.binaryType, "blob", targetChannel2 + " is of binary type 'blob'");
is(sourceChannel2.id, options.id, sourceChannel2 + " id is:" + sourceChannel2.id);
var reliable = !options.ordered ? false : (options.maxRetransmits || options.maxRetransmitTime);
is(sourceChannel2.protocol, options.protocol, sourceChannel2 + " protocol is:" + sourceChannel2.protocol);
is(sourceChannel2.reliable, reliable, sourceChannel2 + " reliable is:" + sourceChannel2.reliable);
/*
These aren't exposed by IDL yet
is(sourceChannel2.ordered, options.ordered, sourceChannel2 + " ordered is:" + sourceChannel2.ordered);
is(sourceChannel2.maxRetransmits, options.maxRetransmits, sourceChannel2 + " maxRetransmits is:" +
sourceChannel2.maxRetransmits);
is(sourceChannel2.maxRetransmitTime, options.maxRetransmitTime, sourceChannel2 + " maxRetransmitTime is:" +
sourceChannel2.maxRetransmitTime);
*/
is(targetChannel2.id, options.id, targetChannel2 + " id is:" + targetChannel2.id);
is(targetChannel2.protocol, options.protocol, targetChannel2 + " protocol is:" + targetChannel2.protocol);
is(targetChannel2.reliable, reliable, targetChannel2 + " reliable is:" + targetChannel2.reliable);
/*
These aren't exposed by IDL yet
is(targetChannel2.ordered, options.ordered, targetChannel2 + " ordered is:" + targetChannel2.ordered);
is(targetChannel2.maxRetransmits, options.maxRetransmits, targetChannel2 + " maxRetransmits is:" +
targetChannel2.maxRetransmits);
is(targetChannel2.maxRetransmitTime, options.maxRetransmitTime, targetChannel2 + " maxRetransmitTime is:" +
targetChannel2.maxRetransmitTime);
*/
});
},
function SEND_MESSAGE_THROUGH_LAST_OPENED_CHANNEL2(test) {
var channels = test.pcRemote.dataChannels;
var message = "I am the walrus; Goo goo g'joob";
return test.send(message).then(result => {
is(channels.indexOf(result.channel), channels.length - 1, "Last channel used");
is(result.data, message, "Received message has the correct content.");
});
}
];
function addInitialDataChannel(chain) { function addInitialDataChannel(chain) {
chain.insertBefore('PC_LOCAL_CREATE_OFFER', [ chain.insertBefore('PC_LOCAL_CREATE_OFFER', commandsCreateDataChannel);
function PC_REMOTE_EXPECT_DATA_CHANNEL(test) { chain.insertBefore('PC_LOCAL_CHECK_MEDIA_TRACKS', commandsWaitForDataChannel);
test.pcRemote.expectDataChannel();
},
function PC_LOCAL_CREATE_DATA_CHANNEL(test) {
var channel = test.pcLocal.createDataChannel({});
is(channel.binaryType, "blob", channel + " is of binary type 'blob'");
is(channel.readyState, "connecting", channel + " is in state: 'connecting'");
is(test.pcLocal.signalingState, STABLE,
"Create datachannel does not change signaling state");
}
]);
chain.insertBefore('PC_LOCAL_CHECK_MEDIA_TRACKS', [
function PC_LOCAL_VERIFY_DATA_CHANNEL_STATE(test) {
return test.pcLocal.dataChannels[0].opened;
},
function PC_REMOTE_VERIFY_DATA_CHANNEL_STATE(test) {
return test.pcRemote.nextDataChannel.then(channel => channel.opened);
}
]);
chain.removeAfter('PC_REMOTE_CHECK_ICE_CONNECTIONS'); chain.removeAfter('PC_REMOTE_CHECK_ICE_CONNECTIONS');
chain.append([ chain.append(commandsCheckDataChannel);
function SEND_MESSAGE(test) {
var message = "Lorem ipsum dolor sit amet";
return test.send(message).then(result => {
is(result.data, message, "Message correctly transmitted from pcLocal to pcRemote.");
});
},
function SEND_BLOB(test) {
var contents = ["At vero eos et accusam et justo duo dolores et ea rebum."];
var blob = new Blob(contents, { "type" : "text/plain" });
return test.send(blob).then(result => {
ok(result.data instanceof Blob, "Received data is of instance Blob");
is(result.data.size, blob.size, "Received data has the correct size.");
return getBlobContent(result.data);
}).then(recv_contents =>
is(recv_contents, contents, "Received data has the correct content."));
},
function CREATE_SECOND_DATA_CHANNEL(test) {
return test.createDataChannel({ }).then(result => {
var sourceChannel = result.local;
var targetChannel = result.remote;
is(sourceChannel.readyState, "open", sourceChannel + " is in state: 'open'");
is(targetChannel.readyState, "open", targetChannel + " is in state: 'open'");
is(targetChannel.binaryType, "blob", targetChannel + " is of binary type 'blob'");
});
},
function SEND_MESSAGE_THROUGH_LAST_OPENED_CHANNEL(test) {
var channels = test.pcRemote.dataChannels;
var message = "I am the Omega";
return test.send(message).then(result => {
is(channels.indexOf(result.channel), channels.length - 1, "Last channel used");
is(result.data, message, "Received message has the correct content.");
});
},
function SEND_MESSAGE_THROUGH_FIRST_CHANNEL(test) {
var message = "Message through 1st channel";
var options = {
sourceChannel: test.pcLocal.dataChannels[0],
targetChannel: test.pcRemote.dataChannels[0]
};
return test.send(message, options).then(result => {
is(test.pcRemote.dataChannels.indexOf(result.channel), 0, "1st channel used");
is(result.data, message, "Received message has the correct content.");
});
},
function SEND_MESSAGE_BACK_THROUGH_FIRST_CHANNEL(test) {
var message = "Return a message also through 1st channel";
var options = {
sourceChannel: test.pcRemote.dataChannels[0],
targetChannel: test.pcLocal.dataChannels[0]
};
return test.send(message, options).then(result => {
is(test.pcLocal.dataChannels.indexOf(result.channel), 0, "1st channel used");
is(result.data, message, "Return message has the correct content.");
});
},
function CREATE_NEGOTIATED_DATA_CHANNEL(test) {
var options = {
negotiated:true,
id: 5,
protocol: "foo/bar",
ordered: false,
maxRetransmits: 500
};
return test.createDataChannel(options).then(result => {
var sourceChannel2 = result.local;
var targetChannel2 = result.remote;
is(sourceChannel2.readyState, "open", sourceChannel2 + " is in state: 'open'");
is(targetChannel2.readyState, "open", targetChannel2 + " is in state: 'open'");
is(targetChannel2.binaryType, "blob", targetChannel2 + " is of binary type 'blob'");
is(sourceChannel2.id, options.id, sourceChannel2 + " id is:" + sourceChannel2.id);
var reliable = !options.ordered ? false : (options.maxRetransmits || options.maxRetransmitTime);
is(sourceChannel2.protocol, options.protocol, sourceChannel2 + " protocol is:" + sourceChannel2.protocol);
is(sourceChannel2.reliable, reliable, sourceChannel2 + " reliable is:" + sourceChannel2.reliable);
/*
These aren't exposed by IDL yet
is(sourceChannel2.ordered, options.ordered, sourceChannel2 + " ordered is:" + sourceChannel2.ordered);
is(sourceChannel2.maxRetransmits, options.maxRetransmits, sourceChannel2 + " maxRetransmits is:" +
sourceChannel2.maxRetransmits);
is(sourceChannel2.maxRetransmitTime, options.maxRetransmitTime, sourceChannel2 + " maxRetransmitTime is:" +
sourceChannel2.maxRetransmitTime);
*/
is(targetChannel2.id, options.id, targetChannel2 + " id is:" + targetChannel2.id);
is(targetChannel2.protocol, options.protocol, targetChannel2 + " protocol is:" + targetChannel2.protocol);
is(targetChannel2.reliable, reliable, targetChannel2 + " reliable is:" + targetChannel2.reliable);
/*
These aren't exposed by IDL yet
is(targetChannel2.ordered, options.ordered, targetChannel2 + " ordered is:" + targetChannel2.ordered);
is(targetChannel2.maxRetransmits, options.maxRetransmits, targetChannel2 + " maxRetransmits is:" +
targetChannel2.maxRetransmits);
is(targetChannel2.maxRetransmitTime, options.maxRetransmitTime, targetChannel2 + " maxRetransmitTime is:" +
targetChannel2.maxRetransmitTime);
*/
});
},
function SEND_MESSAGE_THROUGH_LAST_OPENED_CHANNEL2(test) {
var channels = test.pcRemote.dataChannels;
var message = "I am the walrus; Goo goo g'joob";
return test.send(message).then(result => {
is(channels.indexOf(result.channel), channels.length - 1, "Last channel used");
is(result.data, message, "Received message has the correct content.");
});
}
]);
} }

View File

@ -363,12 +363,19 @@ CommandChain.prototype = {
/** /**
* Returns the index of the specified command in the chain. * Returns the index of the specified command in the chain.
* @param {start} Optional param specifying the index at which the search will
* start. If not specified, the search starts at index 0.
*/ */
indexOf: function(functionOrName) { indexOf: function(functionOrName, start) {
start = start || 0;
if (typeof functionOrName === 'string') { if (typeof functionOrName === 'string') {
return this.commands.findIndex(f => f.name === functionOrName); var index = this.commands.slice(start).findIndex(f => f.name === functionOrName);
if (index !== -1) {
index += start;
}
return index;
} }
return this.commands.indexOf(functionOrName); return this.commands.indexOf(functionOrName, start);
}, },
/** /**
@ -379,20 +386,35 @@ CommandChain.prototype = {
}, },
/** /**
* Inserts the new commands before the specified command. * Inserts the new commands after every occurrence of the specified command
*/ */
insertBefore: function(functionOrName, commands) { insertAfterEach: function(functionOrName, commands) {
this._insertHelper(functionOrName, commands, 0); this._insertHelper(functionOrName, commands, 1, true);
}, },
_insertHelper: function(functionOrName, commands, delta) { /**
var index = this.indexOf(functionOrName); * Inserts the new commands before the specified command.
*/
insertBefore: function(functionOrName, commands, all, start) {
this._insertHelper(functionOrName, commands, 0, all, start);
},
if (index >= 0) { _insertHelper: function(functionOrName, commands, delta, all, start) {
this.commands = [].concat( var index = this.indexOf(functionOrName);
this.commands.slice(0, index + delta), start = start || 0;
commands, for (; index !== -1; index = this.indexOf(functionOrName, index)) {
this.commands.slice(index + delta)); if (!start) {
this.commands = [].concat(
this.commands.slice(0, index + delta),
commands,
this.commands.slice(index + delta));
if (!all) {
break;
}
} else {
start -= 1;
}
index += (commands.length + 1);
} }
}, },
@ -460,7 +482,7 @@ CommandChain.prototype = {
*/ */
filterOut: function (id_match) { filterOut: function (id_match) {
this.commands = this.commands.filter(c => !id_match.test(c.name)); this.commands = this.commands.filter(c => !id_match.test(c.name));
} },
}; };

View File

@ -150,8 +150,32 @@ skip-if = (toolkit == 'gonk' || (e10s && debug)) # b2g (Bug 1059867) or fd exhau
skip-if = (toolkit == 'gonk' || (e10s && debug)) # b2g (Bug 1059867) or fd exhaustion on e10s debug intermittent (Bug 1126078) skip-if = (toolkit == 'gonk' || (e10s && debug)) # b2g (Bug 1059867) or fd exhaustion on e10s debug intermittent (Bug 1126078)
[test_peerConnection_twoVideoStreams.html] [test_peerConnection_twoVideoStreams.html]
skip-if = (toolkit == 'gonk' || (e10s && debug)) # b2g (Bug 1059867) or fd exhaustion on e10s debug intermittent (Bug 1126078) skip-if = (toolkit == 'gonk' || (e10s && debug)) # b2g (Bug 1059867) or fd exhaustion on e10s debug intermittent (Bug 1126078)
# Renegotiation is not yet supported (bug 1017888) [test_peerConnection_addSecondAudioStream.html]
#[test_peerConnection_addSecondAudioStream.html] skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
[test_peerConnection_answererAddSecondAudioStream.html]
skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
[test_peerConnection_removeAudioTrack.html]
skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
[test_peerConnection_removeThenAddAudioTrack.html]
skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
[test_peerConnection_addSecondVideoStream.html]
skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
[test_peerConnection_removeVideoTrack.html]
skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
[test_peerConnection_removeThenAddVideoTrack.html]
skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
[test_peerConnection_addSecondAudioStreamNoBundle.html]
skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
[test_peerConnection_removeThenAddAudioTrackNoBundle.html]
skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
[test_peerConnection_addSecondVideoStreamNoBundle.html]
skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
[test_peerConnection_removeThenAddVideoTrackNoBundle.html]
skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
[test_peerConnection_addDataChannel.html]
skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
[test_peerConnection_addDataChannelNoBundle.html]
skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
# Bug 950317: Hack for making a cleanup hook after finishing all WebRTC cases # Bug 950317: Hack for making a cleanup hook after finishing all WebRTC cases
[test_zmedia_cleanup.html] [test_zmedia_cleanup.html]

View File

@ -121,6 +121,12 @@ function removeVP8(sdp) {
return updated_sdp; return updated_sdp;
} }
var makeDefaultCommands = () => {
return [].concat(commandsPeerConnectionInitial,
commandsGetUserMedia,
commandsPeerConnectionOfferAnswer);
};
/** /**
* This class handles tests for peer connections. * This class handles tests for peer connections.
* *
@ -142,7 +148,7 @@ function removeVP8(sdp) {
function PeerConnectionTest(options) { function PeerConnectionTest(options) {
// If no options are specified make it an empty object // If no options are specified make it an empty object
options = options || { }; options = options || { };
options.commands = options.commands || commandsPeerConnection; options.commands = options.commands || makeDefaultCommands();
options.is_local = "is_local" in options ? options.is_local : true; options.is_local = "is_local" in options ? options.is_local : true;
options.is_remote = "is_remote" in options ? options.is_remote : true; options.is_remote = "is_remote" in options ? options.is_remote : true;
@ -740,8 +746,6 @@ function PeerConnectionWrapper(label, configuration, h264) {
this.dataChannels = [ ]; this.dataChannels = [ ];
this.addStreamCounter = {audio: 0, video: 0 };
this._local_ice_candidates = []; this._local_ice_candidates = [];
this._remote_ice_candidates = []; this._remote_ice_candidates = [];
this.holdIceCandidates = new Promise(r => this.releaseIceCandidates = r); this.holdIceCandidates = new Promise(r => this.releaseIceCandidates = r);
@ -749,6 +753,16 @@ function PeerConnectionWrapper(label, configuration, h264) {
this.remoteRequiresTrickleIce = false; this.remoteRequiresTrickleIce = false;
this.localMediaElements = []; this.localMediaElements = [];
this.expectedLocalTrackTypesById = {};
this.expectedRemoteTrackTypesById = {};
this.observedRemoteTrackTypesById = {};
this.disableRtpCountChecking = false;
this.negotiationNeededFired = false;
this.iceCheckingRestartExpected = false;
this.h264 = typeof h264 !== "undefined" ? true : false; this.h264 = typeof h264 !== "undefined" ? true : false;
info("Creating " + this); info("Creating " + this);
@ -769,27 +783,6 @@ function PeerConnectionWrapper(label, configuration, h264) {
}); });
}; };
/**
* Callback for native peer connection 'onaddstream' events.
*
* @param {Object} event
* Event data which includes the stream to be added
*/
this._pc.onaddstream = event => {
info(this + ": 'onaddstream' event fired for " + JSON.stringify(event.stream));
var type = '';
if (event.stream.getAudioTracks().length > 0) {
type = 'audio';
this.addStreamCounter.audio += this.countTracksInStreams('audio', [event.stream]);
}
if (event.stream.getVideoTracks().length > 0) {
type += 'video';
this.addStreamCounter.video += this.countTracksInStreams('video', [event.stream]);
}
this.attachMedia(event.stream, type, 'remote');
};
createOneShotEventWrapper(this, this._pc, 'datachannel'); createOneShotEventWrapper(this, this._pc, 'datachannel');
this._pc.addEventListener('datachannel', e => { this._pc.addEventListener('datachannel', e => {
var wrapper = new DataChannelWrapper(e.channel, this); var wrapper = new DataChannelWrapper(e.channel, this);
@ -797,6 +790,7 @@ function PeerConnectionWrapper(label, configuration, h264) {
}); });
createOneShotEventWrapper(this, this._pc, 'signalingstatechange'); createOneShotEventWrapper(this, this._pc, 'signalingstatechange');
createOneShotEventWrapper(this, this._pc, 'negotiationneeded');
} }
PeerConnectionWrapper.prototype = { PeerConnectionWrapper.prototype = {
@ -888,6 +882,12 @@ PeerConnectionWrapper.prototype = {
is(sender.track, track, "addTrack returns sender"); is(sender.track, track, "addTrack returns sender");
}); });
} }
stream.getTracks().forEach(track => {
ok(track.id, "track has id");
ok(track.kind, "track has kind");
this.expectedLocalTrackTypesById[track.id] = track.kind;
});
} }
var element = createMediaElement(type, this.label + '_' + side + this.streams.length); var element = createMediaElement(type, this.label + '_' + side + this.streams.length);
@ -902,6 +902,12 @@ PeerConnectionWrapper.prototype = {
} }
}, },
removeSender : function(index) {
var sender = this._pc.getSenders()[index];
delete this.expectedLocalTrackTypesById[sender.track.id];
this._pc.removeTrack(sender);
},
/** /**
* Requests all the media streams as specified in the constrains property. * Requests all the media streams as specified in the constrains property.
* *
@ -1068,6 +1074,59 @@ PeerConnectionWrapper.prototype = {
}); });
}, },
/**
* Checks whether a given track is expected, has not been observed yet, and
* is of the correct type. Then, moves the track from
* |expectedTrackTypesById| to |observedTrackTypesById|.
*/
checkTrackIsExpected : function(track,
expectedTrackTypesById,
observedTrackTypesById) {
ok(expectedTrackTypesById[track.id], "track id " + track.id + " was expected");
ok(!observedTrackTypesById[track.id], "track id " + track.id + " was not yet observed");
var observedKind = track.kind;
var expectedKind = expectedTrackTypesById[track.id];
is(observedKind, expectedKind,
"track id " + track.id + " was of kind " +
observedKind + ", which matches " + expectedKind);
observedTrackTypesById[track.id] = expectedTrackTypesById[track.id];
delete expectedTrackTypesById[track.id];
},
setupAddStreamEventHandler: function() {
var resolveAllAddStreamEventsDone;
// checkMediaTracks waits on this promise later on in the test.
this.allAddStreamEventsDonePromise =
new Promise(resolve => resolveAllAddStreamEventsDone = resolve);
this._pc.addEventListener('addstream', event => {
info(this + ": 'onaddstream' event fired for " + JSON.stringify(event.stream));
// TODO(bug 1130185): We need to handle addtrack events once we start
// testing addTrack on pre-existing streams.
event.stream.getTracks().forEach(track => {
this.checkTrackIsExpected(track,
this.expectedRemoteTrackTypesById,
this.observedRemoteTrackTypesById);
});
if (Object.keys(this.expectedRemoteTrackTypesById).length === 0) {
resolveAllAddStreamEventsDone();
}
var type = '';
if (event.stream.getAudioTracks().length > 0) {
type = 'audio';
}
if (event.stream.getVideoTracks().length > 0) {
type += 'video';
}
this.attachMedia(event.stream, type, 'remote');
});
},
/** /**
* Either adds a given ICE candidate right away or stores it to be added * Either adds a given ICE candidate right away or stores it to be added
* later, depending on the state of the PeerConnection. * later, depending on the state of the PeerConnection.
@ -1148,7 +1207,14 @@ PeerConnectionWrapper.prototype = {
var newstate = this._pc.iceConnectionState; var newstate = this._pc.iceConnectionState;
var oldstate = this.iceConnectionLog[this.iceConnectionLog.length - 1] var oldstate = this.iceConnectionLog[this.iceConnectionLog.length - 1]
if (Object.keys(iceStateTransitions).indexOf(oldstate) != -1) { if (Object.keys(iceStateTransitions).indexOf(oldstate) != -1) {
ok(iceStateTransitions[oldstate].indexOf(newstate) != -1, this + ": legal ICE state transition from " + oldstate + " to " + newstate); if (this.iceCheckingRestartExpected) {
is(newstate, "checking",
"iceconnectionstate event \'" + newstate +
"\' matches expected state \'checking\'");
this.iceCheckingRestartExpected = false;
} else {
ok(iceStateTransitions[oldstate].indexOf(newstate) != -1, this + ": legal ICE state transition from " + oldstate + " to " + newstate);
}
} else { } else {
ok(false, this + ": old ICE state " + oldstate + " missing in ICE transition array"); ok(false, this + ": old ICE state " + oldstate + " missing in ICE transition array");
} }
@ -1284,23 +1350,25 @@ PeerConnectionWrapper.prototype = {
} }
}, },
/* checkLocalMediaTracks : function() {
* Counts the amount of tracks of the given type in a set of streams. var observedLocalTrackTypesById = {};
* // We do not want to empty out this.expectedLocalTrackTypesById, so make a
* @param type audio|video // copy.
* @param streams var expectedLocalTrackTypesById =
* An array of streams (as returned by getLocalStreams()) to be JSON.parse(JSON.stringify((this.expectedLocalTrackTypesById)));
* examined. info(this + " Checking local tracks " +
*/ JSON.stringify(expectedLocalTrackTypesById));
countTracksInStreams: function(type, streams) { this._pc.getLocalStreams().forEach(stream => {
if (!Array.isArray(streams)) { stream.getTracks().forEach(track => {
return 0; this.checkTrackIsExpected(track,
} expectedLocalTrackTypesById,
var f = (type === 'video') ? "getVideoTracks" : "getAudioTracks"; observedLocalTrackTypesById);
});
});
return streams.reduce((count, st) => { Object.keys(expectedLocalTrackTypesById).forEach(id => {
return count + st[f]().length; ok(false, this + " local id " + id + " was observed");
}, 0); });
}, },
/** /**
@ -1309,42 +1377,18 @@ PeerConnectionWrapper.prototype = {
* @param {object} constraints * @param {object} constraints
* The media constraints of the remote peer connection object * The media constraints of the remote peer connection object
*/ */
checkMediaTracks : function(remoteConstraints) { checkMediaTracks : function() {
var waitForExpectedTracks = type => { this.checkLocalMediaTracks();
var outstandingCount = this.countTracksInConstraint(type, remoteConstraints);
outstandingCount -= this.addStreamCounter[type];
if (outstandingCount <= 0) {
return Promise.resolve();
}
return new Promise(resolve => { info(this + " Checking remote tracks " +
this._pc.addEventListener('addstream', e => { JSON.stringify(this.expectedRemoteTrackTypesById));
outstandingCount -= this.countTracksInStreams(type, [e.stream]);
if (outstandingCount <= 0) {
resolve();
}
});
});
};
var checkTrackCounts = (side, streams, constraints) => { // No tracks are expected
['audio', 'video'].forEach(type => { if (Object.keys(this.expectedRemoteTrackTypesById).length === 0) {
var actual = this.countTracksInStreams(type, streams); return;
var expected = this.countTracksInConstraint(type, constraints); }
is(actual, expected, this + ' has ' + actual + ' ' +
side + ' ' + type + ' tracks');
});
};
info(this + " checkMediaTracks() got called before onAddStream fired"); return timerGuard(this.allAddStreamEventsDonePromise, 60000, "onaddstream never fired");
var checkPromise = Promise.all([
waitForExpectedTracks('audio'),
waitForExpectedTracks('video')
]).then(() => {
checkTrackCounts('local', this._pc.getLocalStreams(), this.constraints);
checkTrackCounts('remote', this._pc.getRemoteStreams(), remoteConstraints);
});
return timerGuard(checkPromise, 60000, "onaddstream never fired");
}, },
checkMsids: function() { checkMsids: function() {
@ -1515,10 +1559,12 @@ PeerConnectionWrapper.prototype = {
if(res.type == "outboundrtp") { if(res.type == "outboundrtp") {
ok(rem.type == "inboundrtp", "Rtcp is inbound"); ok(rem.type == "inboundrtp", "Rtcp is inbound");
ok(rem.packetsReceived !== undefined, "Rtcp packetsReceived"); ok(rem.packetsReceived !== undefined, "Rtcp packetsReceived");
ok(rem.packetsReceived <= res.packetsSent, "No more than sent");
ok(rem.packetsLost !== undefined, "Rtcp packetsLost"); ok(rem.packetsLost !== undefined, "Rtcp packetsLost");
ok(rem.bytesReceived >= rem.packetsReceived, "Rtcp bytesReceived"); ok(rem.bytesReceived >= rem.packetsReceived, "Rtcp bytesReceived");
ok(rem.bytesReceived <= res.bytesSent, "No more than sent bytes"); if (!this.disableRtpCountChecking) {
ok(rem.packetsReceived <= res.packetsSent, "No more than sent packets");
ok(rem.bytesReceived <= res.bytesSent, "No more than sent bytes");
}
ok(rem.jitter !== undefined, "Rtcp jitter"); ok(rem.jitter !== undefined, "Rtcp jitter");
ok(rem.mozRtt !== undefined, "Rtcp rtt"); ok(rem.mozRtt !== undefined, "Rtcp rtt");
ok(rem.mozRtt >= 0, "Rtcp rtt " + rem.mozRtt + " >= 0"); ok(rem.mozRtt >= 0, "Rtcp rtt " + rem.mozRtt + " >= 0");

View File

@ -63,17 +63,19 @@ function dumpSdp(test) {
} }
function waitForIceConnected(test, pc) { function waitForIceConnected(test, pc) {
if (pc.isIceConnected()) { if (!pc.iceCheckingRestartExpected) {
info(pc + ": ICE connection state log: " + pc.iceConnectionLog); if (pc.isIceConnected()) {
ok(true, pc + ": ICE is in connected state"); info(pc + ": ICE connection state log: " + pc.iceConnectionLog);
return Promise.resolve(); ok(true, pc + ": ICE is in connected state");
} return Promise.resolve();
}
if (!pc.isIceConnectionPending()) { if (!pc.isIceConnectionPending()) {
dumpSdp(test); dumpSdp(test);
var details = pc + ": ICE is already in bad state: " + pc.iceConnectionState; var details = pc + ": ICE is already in bad state: " + pc.iceConnectionState;
ok(false, details); ok(false, details);
return Promise.reject(new Error(details)); return Promise.reject(new Error(details));
}
} }
return pc.waitForIceConnected() return pc.waitForIceConnected()
@ -135,7 +137,9 @@ function checkTrackStats(pc, audio, outbound) {
var checkAllTrackStats = pc => var checkAllTrackStats = pc =>
Promise.all([0, 1, 2, 3].map(i => checkTrackStats(pc, i & 1, i & 2))); Promise.all([0, 1, 2, 3].map(i => checkTrackStats(pc, i & 1, i & 2)));
var commandsPeerConnection = [ // Commands run once at the beginning of each test, even when performing a
// renegotiation test.
var commandsPeerConnectionInitial = [
function PC_SETUP_SIGNALING_CLIENT(test) { function PC_SETUP_SIGNALING_CLIENT(test) {
if (test.steeplechase) { if (test.steeplechase) {
setTimeout(() => { setTimeout(() => {
@ -169,12 +173,12 @@ var commandsPeerConnection = [
test.pcRemote.logSignalingState(); test.pcRemote.logSignalingState();
}, },
function PC_LOCAL_GUM(test) { function PC_LOCAL_SETUP_ADDSTREAM_HANDLER(test) {
return test.pcLocal.getAllUserMedia(test.pcLocal.constraints); test.pcLocal.setupAddStreamEventHandler();
}, },
function PC_REMOTE_GUM(test) { function PC_REMOTE_SETUP_ADDSTREAM_HANDLER(test) {
return test.pcRemote.getAllUserMedia(test.pcRemote.constraints); test.pcRemote.setupAddStreamEventHandler();
}, },
function PC_LOCAL_CHECK_INITIAL_SIGNALINGSTATE(test) { function PC_LOCAL_CHECK_INITIAL_SIGNALINGSTATE(test) {
@ -197,6 +201,34 @@ var commandsPeerConnection = [
"Initial remote ICE connection state is 'new'"); "Initial remote ICE connection state is 'new'");
}, },
];
var commandsGetUserMedia = [
function PC_LOCAL_GUM(test) {
return test.pcLocal.getAllUserMedia(test.pcLocal.constraints);
},
function PC_REMOTE_GUM(test) {
return test.pcRemote.getAllUserMedia(test.pcRemote.constraints);
},
];
var commandsBeforeRenegotiation = [
function PC_LOCAL_SETUP_NEGOTIATION_CALLBACK(test) {
test.pcLocal.onnegotiationneeded = event => {
test.pcLocal.negotiationNeededFired = true;
};
},
];
var commandsAfterRenegotiation = [
function PC_LOCAL_CHECK_NEGOTIATION_CALLBACK(test) {
ok(test.pcLocal.negotiationNeededFired, "Expected negotiationneeded event");
test.pcLocal.negotiationNeededFired = false;
},
];
var commandsPeerConnectionOfferAnswer = [
function PC_LOCAL_SETUP_ICE_HANDLER(test) { function PC_LOCAL_SETUP_ICE_HANDLER(test) {
test.pcLocal.setupIceCandidateHandler(test); test.pcLocal.setupIceCandidateHandler(test);
if (test.steeplechase) { if (test.steeplechase) {
@ -215,6 +247,56 @@ var commandsPeerConnection = [
} }
}, },
function PC_LOCAL_STEEPLECHASE_SIGNAL_EXPECTED_LOCAL_TRACKS(test) {
if (test.steeplechase) {
send_message({"type": "local_expected_tracks",
"expected_tracks": test.pcLocal.expectedLocalTrackTypesById});
}
},
function PC_REMOTE_STEEPLECHASE_SIGNAL_EXPECTED_LOCAL_TRACKS(test) {
if (test.steeplechase) {
send_message({"type": "remote_expected_tracks",
"expected_tracks": test.pcRemote.expectedLocalTrackTypesById});
}
},
function PC_LOCAL_GET_EXPECTED_REMOTE_TRACKS(test) {
if (test.steeplechase) {
return test.getSignalingMessage("remote_expected_tracks").then(
message => {
test.pcLocal.expectedRemoteTrackTypesById = message.expected_tracks;
});
} else {
// Deep copy, as similar to steeplechase as possible
test.pcLocal.expectedRemoteTrackTypesById =
JSON.parse(JSON.stringify((test.pcRemote.expectedLocalTrackTypesById)));
}
// Remove what we've already observed
Object.keys(test.pcLocal.observedRemoteTrackTypesById).forEach(id => {
delete test.pcLocal.expectedRemoteTrackTypesById[id];
});
},
function PC_LOCAL_GET_EXPECTED_REMOTE_TRACKS(test) {
if (test.steeplechase) {
return test.getSignalingMessage("local_expected_tracks").then(
message => {
test.pcRemote.expectedRemoteTrackTypesById = message.expected_tracks;
});
} else {
// Deep copy, as similar to steeplechase as possible
test.pcRemote.expectedRemoteTrackTypesById =
JSON.parse(JSON.stringify((test.pcLocal.expectedLocalTrackTypesById)));
}
// Remove what we've already observed
Object.keys(test.pcRemote.observedRemoteTrackTypesById).forEach(id => {
delete test.pcRemote.expectedRemoteTrackTypesById[id];
});
},
function PC_LOCAL_CREATE_OFFER(test) { function PC_LOCAL_CREATE_OFFER(test) {
return test.createOffer(test.pcLocal).then(offer => { return test.createOffer(test.pcLocal).then(offer => {
is(test.pcLocal.signalingState, STABLE, is(test.pcLocal.signalingState, STABLE,
@ -390,11 +472,11 @@ var commandsPeerConnection = [
}, },
function PC_LOCAL_CHECK_MEDIA_TRACKS(test) { function PC_LOCAL_CHECK_MEDIA_TRACKS(test) {
return test.pcLocal.checkMediaTracks(test._answer_constraints); return test.pcLocal.checkMediaTracks();
}, },
function PC_REMOTE_CHECK_MEDIA_TRACKS(test) { function PC_REMOTE_CHECK_MEDIA_TRACKS(test) {
return test.pcRemote.checkMediaTracks(test._offer_constraints); return test.pcRemote.checkMediaTracks();
}, },
function PC_LOCAL_CHECK_MEDIA_FLOW_PRESENT(test) { function PC_LOCAL_CHECK_MEDIA_FLOW_PRESENT(test) {
@ -468,3 +550,32 @@ var commandsPeerConnection = [
return checkAllTrackStats(test.pcRemote); return checkAllTrackStats(test.pcRemote);
} }
]; ];
function PC_LOCAL_REMOVE_BUNDLE_FROM_OFFER(test) {
test.originalOffer.sdp = test.originalOffer.sdp.replace(
/a=group:BUNDLE .*\r\n/g,
""
);
info("Updated no bundle offer: " + JSON.stringify(test.originalOffer));
};
var addRenegotiation = (chain, commands, checks) => {
chain.append(commandsBeforeRenegotiation);
chain.append(commands);
chain.append(commandsAfterRenegotiation);
chain.append(commandsPeerConnectionOfferAnswer);
if (checks) {
chain.append(checks);
}
};
var addRenegotiationAnswerer = (chain, commands, checks) => {
chain.append(function SWAP_PC_LOCAL_PC_REMOTE(test) {
var temp = test.pcLocal;
test.pcLocal = test.pcRemote;
test.pcRemote = temp;
});
addRenegotiation(chain, commands, checks);
};

View File

@ -0,0 +1,34 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "1017888",
title: "Renegotiation: add DataChannel"
});
var test;
runNetworkTest(function (options) {
test = new PeerConnectionTest(options);
addRenegotiation(test.chain,
commandsCreateDataChannel,
commandsCheckDataChannel);
// Insert before the second PC_LOCAL_CHECK_MEDIA_TRACKS
test.chain.insertBefore('PC_LOCAL_CHECK_MEDIA_TRACKS',
commandsWaitForDataChannel,
false,
1);
test.setMediaConstraints([{audio: true}], [{audio: true}]);
test.run();
});
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,46 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "1017888",
title: "Renegotiation: add DataChannel"
});
var test;
runNetworkTest(function (options) {
test = new PeerConnectionTest(options);
addRenegotiation(test.chain,
commandsCreateDataChannel.concat(
[
function PC_LOCAL_EXPECT_ICE_CHECKING(test) {
test.pcLocal.iceCheckingRestartExpected = true;
},
function PC_REMOTE_EXPECT_ICE_CHECKING(test) {
test.pcRemote.iceCheckingRestartExpected = true;
},
]
),
commandsCheckDataChannel);
test.chain.insertAfterEach('PC_LOCAL_CREATE_OFFER',
PC_LOCAL_REMOVE_BUNDLE_FROM_OFFER);
// Insert before the second PC_LOCAL_CHECK_MEDIA_TRACKS
test.chain.insertBefore('PC_LOCAL_CHECK_MEDIA_TRACKS',
commandsWaitForDataChannel,
false,
1);
test.setMediaConstraints([{audio: true}], [{audio: true}]);
test.run();
});
</script>
</pre>
</body>
</html>

View File

@ -7,49 +7,24 @@
<pre id="test"> <pre id="test">
<script type="application/javascript"> <script type="application/javascript">
createHTML({ createHTML({
bug: "1091242", bug: "1017888",
title: "Renegotiation: add second audio stream" title: "Renegotiation: add second audio stream"
}); });
var test; var test;
runNetworkTest(function (options) { runNetworkTest(function (options) {
test = new PeerConnectionTest(options); test = new PeerConnectionTest(options);
test.chain.append([ addRenegotiation(test.chain,
function PC_LOCAL_SETUP_NEGOTIATION_CALLBACK(test) { [
test.pcLocal.onNegotiationneededFired = false; function PC_LOCAL_ADD_SECOND_STREAM(test) {
test.pcLocal._pc.onnegotiationneeded = anEvent => { test.setMediaConstraints([{audio: true}, {audio: true}],
info("pcLocal.onnegotiationneeded fired"); [{audio: true}]);
test.pcLocal.onNegotiationneededFired = true; return test.pcLocal.getAllUserMedia([{audio: true}]);
}; },
}, ]
function PC_LOCAL_ADD_SECOND_STREAM(test) { );
return test.pcLocal.getAllUserMedia([{audio: true}]);
}, // TODO(bug 1093835): figure out how to verify if media flows through the new stream
function PC_LOCAL_CREATE_NEW_OFFER(test) {
ok(test.pcLocal.onNegotiationneededFired, "onnegotiationneeded");
return test.createOffer(test.pcLocal).then(offer => {
test._new_offer = offer;
});
},
function PC_LOCAL_SET_NEW_LOCAL_DESCRIPTION(test) {
return test.setLocalDescription(test.pcLocal, test._new_offer, HAVE_LOCAL_OFFER);
},
function PC_REMOTE_SET_NEW_REMOTE_DESCRIPTION(test) {
return test.setRemoteDescription(test.pcRemote, test._new_offer, HAVE_REMOTE_OFFER);
},
function PC_REMOTE_CREATE_NEW_ANSWER(test) {
return test.createAnswer(test.pcRemote).then(answer => {
test._new_answer = answer;
});
},
function PC_REMOTE_SET_NEW_LOCAL_DESCRIPTION(test) {
return test.setLocalDescription(test.pcRemote, test._new_answer, STABLE);
},
function PC_LOCAL_SET_NEW_REMOTE_DESCRIPTION(test) {
return test.setRemoteDescription(test.pcLocal, test._new_answer, STABLE);
}
// TODO(bug 1093835): figure out how to verify if media flows through the new stream
]);
test.setMediaConstraints([{audio: true}], [{audio: true}]); test.setMediaConstraints([{audio: true}], [{audio: true}]);
test.run(); test.run();
}); });

View File

@ -0,0 +1,43 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "1017888",
title: "Renegotiation: add second audio stream, no bundle"
});
var test;
runNetworkTest(function (options) {
test = new PeerConnectionTest(options);
addRenegotiation(test.chain,
[
function PC_LOCAL_ADD_SECOND_STREAM(test) {
test.setMediaConstraints([{audio: true}, {audio: true}],
[{audio: true}]);
// Since this is a NoBundle variant, adding a track will cause us to
// go back to checking.
test.pcLocal.iceCheckingRestartExpected = true;
return test.pcLocal.getAllUserMedia([{audio: true}]);
},
function PC_REMOTE_EXPECT_ICE_CHECKING(test) {
test.pcRemote.iceCheckingRestartExpected = true;
},
]
);
test.chain.insertAfterEach('PC_LOCAL_CREATE_OFFER',
PC_LOCAL_REMOVE_BUNDLE_FROM_OFFER);
// TODO(bug 1093835): figure out how to verify if media flows through the new stream
test.setMediaConstraints([{audio: true}], [{audio: true}]);
test.run();
});
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,34 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "1017888",
title: "Renegotiation: add second video stream"
});
var test;
runNetworkTest(function (options) {
test = new PeerConnectionTest(options);
addRenegotiation(test.chain,
[
function PC_LOCAL_ADD_SECOND_STREAM(test) {
test.setMediaConstraints([{video: true}, {video: true}],
[{video: true}]);
return test.pcLocal.getAllUserMedia([{video: true}]);
},
]
);
// TODO(bug 1093835): figure out how to verify if media flows through the new stream
test.setMediaConstraints([{video: true}], [{video: true}]);
test.run();
});
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,43 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "1017888",
title: "Renegotiation: add second video stream, no bundle"
});
var test;
runNetworkTest(function (options) {
test = new PeerConnectionTest(options);
addRenegotiation(test.chain,
[
function PC_LOCAL_ADD_SECOND_STREAM(test) {
test.setMediaConstraints([{video: true}, {video: true}],
[{video: true}]);
// Since this is a NoBundle variant, adding a track will cause us to
// go back to checking.
test.pcLocal.iceCheckingRestartExpected = true;
return test.pcLocal.getAllUserMedia([{video: true}]);
},
function PC_REMOTE_EXPECT_ICE_CHECKING(test) {
test.pcRemote.iceCheckingRestartExpected = true;
},
]
);
test.chain.insertAfterEach('PC_LOCAL_CREATE_OFFER',
PC_LOCAL_REMOVE_BUNDLE_FROM_OFFER);
// TODO(bug 1093835): figure out how to verify if media flows through the new stream
test.setMediaConstraints([{video: true}], [{video: true}]);
test.run();
});
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,34 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "1017888",
title: "Renegotiation: answerer adds second audio stream"
});
var test;
runNetworkTest(function (options) {
test = new PeerConnectionTest(options);
addRenegotiationAnswerer(test.chain,
[
function PC_LOCAL_ADD_SECOND_STREAM(test) {
test.setMediaConstraints([{audio: true}, {audio: true}],
[{audio: true}]);
return test.pcLocal.getAllUserMedia([{audio: true}]);
},
]
);
test.setMediaConstraints([{audio: true}], [{audio: true}]);
test.run();
});
</script>
</pre>
</body>
</html>

View File

@ -15,15 +15,7 @@
var test = new PeerConnectionTest(options); var test = new PeerConnectionTest(options);
test.chain.insertAfter( test.chain.insertAfter(
'PC_LOCAL_CREATE_OFFER', 'PC_LOCAL_CREATE_OFFER',
[ [PC_LOCAL_REMOVE_BUNDLE_FROM_OFFER]);
function PC_LOCAL_REMOVE_BUNDLE_FROM_OFFER(test) {
test.originalOffer.sdp = test.originalOffer.sdp.replace(
/a=group:BUNDLE .*\r\n/g,
""
);
info("Updated no bundle offer: " + JSON.stringify(test.originalOffer));
}
]);
test.setMediaConstraints([{audio: true}, {video: true}], test.setMediaConstraints([{audio: true}, {video: true}],
[{audio: true}, {video: true}]); [{audio: true}, {video: true}]);
test.run(); test.run();

View File

@ -31,7 +31,10 @@ runNetworkTest(function() {
.then(() => { .then(() => {
var stream = v1.mozCaptureStreamUntilEnded(); var stream = v1.mozCaptureStreamUntilEnded();
is(stream.getTracks().length, 2, "Captured stream has 2 tracks"); is(stream.getTracks().length, 2, "Captured stream has 2 tracks");
stream.getTracks().forEach(tr => test.pcLocal._pc.addTrack(tr, stream)); stream.getTracks().forEach(tr => {
test.pcLocal._pc.addTrack(tr, stream);
test.pcLocal.expectedLocalTrackTypesById[tr.id] = tr.kind;
});
test.pcLocal.constraints = [{ video: true, audio:true }]; // fool tests test.pcLocal.constraints = [{ video: true, audio:true }]; // fool tests
}); });
} }

View File

@ -0,0 +1,35 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "1017888",
title: "Renegotiation: remove audio track"
});
var test;
runNetworkTest(function (options) {
test = new PeerConnectionTest(options);
addRenegotiation(test.chain,
[
function PC_LOCAL_REMOVE_AUDIO_TRACK(test) {
test.setOfferOptions({ offerToReceiveAudio: true });
test.setMediaConstraints([], [{audio: true}]);
return test.pcLocal.removeSender(0);
},
]
);
// TODO(bug 1093835): figure out how to verify that media stopped flowing from pcLocal
test.setMediaConstraints([{audio: true}], [{audio: true}]);
test.run();
});
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,39 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "1017888",
title: "Renegotiation: remove then add audio track"
});
var test;
runNetworkTest(function (options) {
test = new PeerConnectionTest(options);
addRenegotiation(test.chain,
[
function PC_LOCAL_REMOVE_AUDIO_TRACK(test) {
return test.pcLocal.removeSender(0);
},
function PC_LOCAL_ADD_AUDIO_TRACK(test) {
// The new track's pipeline will start with a packet count of
// 0, but the remote side will keep its old pipeline and packet
// count.
test.pcLocal.disableRtpCountChecking = true;
return test.pcLocal.getAllUserMedia([{audio: true}]);
},
]
);
// TODO(bug 1093835): figure out how to verify if media flows through the new stream
test.setMediaConstraints([{audio: true}], [{audio: true}]);
test.run();
});
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,42 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "1017888",
title: "Renegotiation: remove then add audio track"
});
var test;
runNetworkTest(function (options) {
test = new PeerConnectionTest(options);
addRenegotiation(test.chain,
[
function PC_LOCAL_REMOVE_AUDIO_TRACK(test) {
// The new track's pipeline will start with a packet count of
// 0, but the remote side will keep its old pipeline and packet
// count.
test.pcLocal.disableRtpCountChecking = true;
return test.pcLocal.removeSender(0);
},
function PC_LOCAL_ADD_AUDIO_TRACK(test) {
return test.pcLocal.getAllUserMedia([{audio: true}]);
},
]
);
test.chain.insertAfterEach('PC_LOCAL_CREATE_OFFER',
PC_LOCAL_REMOVE_BUNDLE_FROM_OFFER);
// TODO(bug 1093835): figure out how to verify if media flows through the new stream
test.setMediaConstraints([{audio: true}], [{audio: true}]);
test.run();
});
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,39 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "1017888",
title: "Renegotiation: remove then add video track"
});
var test;
runNetworkTest(function (options) {
test = new PeerConnectionTest(options);
addRenegotiation(test.chain,
[
function PC_LOCAL_REMOVE_AUDIO_TRACK(test) {
// The new track's pipeline will start with a packet count of
// 0, but the remote side will keep its old pipeline and packet
// count.
test.pcLocal.disableRtpCountChecking = true;
return test.pcLocal.removeSender(0);
},
function PC_LOCAL_ADD_AUDIO_TRACK(test) {
return test.pcLocal.getAllUserMedia([{video: true}]);
},
]
);
// TODO(bug 1093835): figure out how to verify if media flows through the new stream
test.setMediaConstraints([{video: true}], [{video: true}]);
test.run();
});
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,42 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "1017888",
title: "Renegotiation: remove then add video track, no bundle"
});
var test;
runNetworkTest(function (options) {
test = new PeerConnectionTest(options);
addRenegotiation(test.chain,
[
function PC_LOCAL_REMOVE_AUDIO_TRACK(test) {
// The new track's pipeline will start with a packet count of
// 0, but the remote side will keep its old pipeline and packet
// count.
test.pcLocal.disableRtpCountChecking = true;
return test.pcLocal.removeSender(0);
},
function PC_LOCAL_ADD_AUDIO_TRACK(test) {
return test.pcLocal.getAllUserMedia([{video: true}]);
},
]
);
test.chain.insertAfterEach('PC_LOCAL_CREATE_OFFER',
PC_LOCAL_REMOVE_BUNDLE_FROM_OFFER);
// TODO(bug 1093835): figure out how to verify if media flows through the new stream
test.setMediaConstraints([{video: true}], [{video: true}]);
test.run();
});
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,35 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "1017888",
title: "Renegotiation: remove video track"
});
var test;
runNetworkTest(function (options) {
test = new PeerConnectionTest(options);
addRenegotiation(test.chain,
[
function PC_LOCAL_REMOVE_VIDEO_TRACK(test) {
test.setOfferOptions({ offerToReceiveVideo: true });
test.setMediaConstraints([], [{video: true}]);
return test.pcLocal.removeSender(0);
},
]
);
// TODO(bug 1093835): figure out how to verify that media stopped flowing from pcLocal
test.setMediaConstraints([{video: true}], [{video: true}]);
test.run();
});
</script>
</pre>
</body>
</html>

View File

@ -23,6 +23,8 @@
#include "gfxSharedImageSurface.h" #include "gfxSharedImageSurface.h"
#include "nsNPAPIPluginInstance.h" #include "nsNPAPIPluginInstance.h"
#include "nsPluginInstanceOwner.h" #include "nsPluginInstanceOwner.h"
#include "nsFocusManager.h"
#include "nsIDOMElement.h"
#ifdef MOZ_X11 #ifdef MOZ_X11
#include "gfxXlibSurface.h" #include "gfxXlibSurface.h"
#endif #endif
@ -45,10 +47,6 @@
#include "mozilla/plugins/PluginSurfaceParent.h" #include "mozilla/plugins/PluginSurfaceParent.h"
#include "nsClassHashtable.h" #include "nsClassHashtable.h"
#include "nsHashKeys.h" #include "nsHashKeys.h"
// Plugin focus event for widget.
extern const wchar_t* kOOPPPluginFocusEventId;
UINT gOOPPPluginFocusEvent =
RegisterWindowMessage(kOOPPPluginFocusEventId);
extern const wchar_t* kFlashFullscreenClass; extern const wchar_t* kFlashFullscreenClass;
#elif defined(MOZ_WIDGET_GTK) #elif defined(MOZ_WIDGET_GTK)
#include <gdk/gdk.h> #include <gdk/gdk.h>
@ -1789,7 +1787,6 @@ PluginInstanceParent::SubclassPluginWindow(HWND aWnd)
return; return;
} }
#if defined(XP_WIN)
if (XRE_GetProcessType() == GeckoProcessType_Content) { if (XRE_GetProcessType() == GeckoProcessType_Content) {
if (!aWnd) { if (!aWnd) {
NS_WARNING("PluginInstanceParent::SubclassPluginWindow unexpected null window"); NS_WARNING("PluginInstanceParent::SubclassPluginWindow unexpected null window");
@ -1802,7 +1799,6 @@ PluginInstanceParent::SubclassPluginWindow(HWND aWnd)
sPluginInstanceList->Put((void*)mPluginHWND, this); sPluginInstanceList->Put((void*)mPluginHWND, this);
return; return;
} }
#endif
NS_ASSERTION(!(mPluginHWND && aWnd != mPluginHWND), NS_ASSERTION(!(mPluginHWND && aWnd != mPluginHWND),
"PluginInstanceParent::SubclassPluginWindow hwnd is not our window!"); "PluginInstanceParent::SubclassPluginWindow hwnd is not our window!");
@ -1821,7 +1817,6 @@ PluginInstanceParent::SubclassPluginWindow(HWND aWnd)
void void
PluginInstanceParent::UnsubclassPluginWindow() PluginInstanceParent::UnsubclassPluginWindow()
{ {
#if defined(XP_WIN)
if (XRE_GetProcessType() == GeckoProcessType_Content) { if (XRE_GetProcessType() == GeckoProcessType_Content) {
if (mPluginHWND) { if (mPluginHWND) {
// Remove 'this' from the plugin list safely // Remove 'this' from the plugin list safely
@ -1837,7 +1832,6 @@ PluginInstanceParent::UnsubclassPluginWindow()
mPluginHWND = nullptr; mPluginHWND = nullptr;
return; return;
} }
#endif
if (mPluginHWND && mPluginWndProc) { if (mPluginHWND && mPluginWndProc) {
::SetWindowLongPtrA(mPluginHWND, GWLP_WNDPROC, ::SetWindowLongPtrA(mPluginHWND, GWLP_WNDPROC,
@ -1980,14 +1974,21 @@ PluginInstanceParent::AnswerPluginFocusChange(const bool& gotFocus)
{ {
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION)); PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
// Currently only in use on windows - an rpc event we receive from the // Currently only in use on windows - an event we receive from the child
// child when it's plugin window (or one of it's children) receives keyboard // when it's plugin window (or one of it's children) receives keyboard
// focus. We forward the event down to widget so the dom/focus manager can // focus. We detect this and forward a notification here so we can update
// be updated. // focus.
#if defined(OS_WIN) #if defined(OS_WIN)
// XXX This needs to go to PuppetWidget. bug ??? if (gotFocus) {
if (XRE_GetProcessType() == GeckoProcessType_Default) { nsPluginInstanceOwner* owner = GetOwner();
::SendMessage(mPluginHWND, gOOPPPluginFocusEvent, gotFocus ? 1 : 0, 0); if (owner) {
nsIFocusManager* fm = nsFocusManager::GetFocusManager();
nsCOMPtr<nsIDOMElement> element;
owner->GetDOMElement(getter_AddRefs(element));
if (fm && element) {
fm->SetFocus(element, 0);
}
}
} }
return true; return true;
#else #else

View File

@ -819,6 +819,12 @@ this.PushService = {
if (uri.scheme === "wss") { if (uri.scheme === "wss") {
this._ws = Cc["@mozilla.org/network/protocol;1?name=wss"] this._ws = Cc["@mozilla.org/network/protocol;1?name=wss"]
.createInstance(Ci.nsIWebSocketChannel); .createInstance(Ci.nsIWebSocketChannel);
this._ws.initLoadInfo(null, // aLoadingNode
Services.scriptSecurityManager.getSystemPrincipal(),
null, // aTriggeringPrincipal
Ci.nsILoadInfo.SEC_NORMAL,
Ci.nsIContentPolicy.TYPE_WEBSOCKET);
} }
else if (uri.scheme === "ws") { else if (uri.scheme === "ws") {
debug("Push over an insecure connection (ws://) is not allowed!"); debug("Push over an insecure connection (ws://) is not allowed!");

View File

@ -23,6 +23,7 @@ interface PeerConnectionObserver
void onAddIceCandidateSuccess(); void onAddIceCandidateSuccess();
void onAddIceCandidateError(unsigned long name, DOMString message); void onAddIceCandidateError(unsigned long name, DOMString message);
void onIceCandidate(unsigned short level, DOMString mid, DOMString candidate); void onIceCandidate(unsigned short level, DOMString mid, DOMString candidate);
void onNegotiationNeeded();
/* Stats callbacks */ /* Stats callbacks */
void onGetStatsSuccess(optional RTCStatsReportInternal report); void onGetStatsSuccess(optional RTCStatsReportInternal report);
@ -40,7 +41,7 @@ interface PeerConnectionObserver
/* Changes to MediaStreamTracks */ /* Changes to MediaStreamTracks */
void onAddStream(MediaStream stream); void onAddStream(MediaStream stream);
void onRemoveStream(); void onRemoveStream(MediaStream stream);
void onAddTrack(MediaStreamTrack track); void onAddTrack(MediaStreamTrack track);
void onRemoveTrack(); void onRemoveTrack(MediaStreamTrack track);
}; };

View File

@ -130,7 +130,6 @@ ServiceWorkerRegistrationInfo::Clear()
mWaitingWorker->UpdateState(ServiceWorkerState::Redundant); mWaitingWorker->UpdateState(ServiceWorkerState::Redundant);
// Fire statechange. // Fire statechange.
mWaitingWorker = nullptr; mWaitingWorker = nullptr;
mWaitingToActivate = false;
} }
if (mActiveWorker) { if (mActiveWorker) {
@ -199,8 +198,6 @@ ServiceWorkerManager::~ServiceWorkerManager()
mServiceWorkerRegistrationInfos.Clear(); mServiceWorkerRegistrationInfos.Clear();
} }
class ServiceWorkerRegisterJob;
class ContinueLifecycleTask : public nsISupports class ContinueLifecycleTask : public nsISupports
{ {
NS_DECL_ISUPPORTS NS_DECL_ISUPPORTS
@ -216,6 +213,8 @@ public:
NS_IMPL_ISUPPORTS0(ContinueLifecycleTask); NS_IMPL_ISUPPORTS0(ContinueLifecycleTask);
class ServiceWorkerRegisterJob;
class ContinueInstallTask MOZ_FINAL : public ContinueLifecycleTask class ContinueInstallTask MOZ_FINAL : public ContinueLifecycleTask
{ {
nsRefPtr<ServiceWorkerRegisterJob> mJob; nsRefPtr<ServiceWorkerRegisterJob> mJob;
@ -238,10 +237,7 @@ public:
{ } { }
void void
ContinueAfterWorkerEvent(bool aSuccess, bool aActivateImmediately /* unused */) MOZ_OVERRIDE ContinueAfterWorkerEvent(bool aSuccess, bool aActivateImmediately /* unused */) MOZ_OVERRIDE;
{
mRegistration->FinishActivate(aSuccess);
}
}; };
class ContinueLifecycleRunnable MOZ_FINAL : public nsRunnable class ContinueLifecycleRunnable MOZ_FINAL : public nsRunnable
@ -571,6 +567,7 @@ public:
getter_AddRefs(serviceWorker)); getter_AddRefs(serviceWorker));
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
swm->mSetOfScopesBeingUpdated.Remove(mRegistration->mScope);
Fail(NS_ERROR_DOM_ABORT_ERR); Fail(NS_ERROR_DOM_ABORT_ERR);
return rv; return rv;
} }
@ -585,6 +582,7 @@ public:
jsapi.Init(); jsapi.Init();
bool ok = r->Dispatch(jsapi.cx()); bool ok = r->Dispatch(jsapi.cx());
if (NS_WARN_IF(!ok)) { if (NS_WARN_IF(!ok)) {
swm->mSetOfScopesBeingUpdated.Remove(mRegistration->mScope);
Fail(NS_ERROR_DOM_ABORT_ERR); Fail(NS_ERROR_DOM_ABORT_ERR);
return rv; return rv;
} }
@ -608,6 +606,10 @@ public:
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
MOZ_ASSERT(swm->mSetOfScopesBeingUpdated.Contains(mRegistration->mScope)); MOZ_ASSERT(swm->mSetOfScopesBeingUpdated.Contains(mRegistration->mScope));
swm->mSetOfScopesBeingUpdated.Remove(mRegistration->mScope); swm->mSetOfScopesBeingUpdated.Remove(mRegistration->mScope);
// This is effectively the end of Step 4.3 of the [[Update]] algorithm.
// The invocation of [[Install]] is not part of the atomic block.
// Begin [[Install]] atomic step 4.
if (mRegistration->mInstallingWorker) { if (mRegistration->mInstallingWorker) {
// FIXME(nsm): Terminate and stuff // FIXME(nsm): Terminate and stuff
mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Redundant); mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
@ -620,15 +622,13 @@ public:
Succeed(); Succeed();
// Step 4.6 "Queue a task..." for updatefound.
nsCOMPtr<nsIRunnable> upr = nsCOMPtr<nsIRunnable> upr =
NS_NewRunnableMethodWithArg<ServiceWorkerRegistrationInfo*>(swm, NS_NewRunnableMethodWithArg<ServiceWorkerRegistrationInfo*>(swm,
&ServiceWorkerManager::FireUpdateFound, &ServiceWorkerManager::FireUpdateFound,
mRegistration); mRegistration);
NS_DispatchToMainThread(upr); NS_DispatchToMainThread(upr);
nsMainThreadPtrHandle<ContinueLifecycleTask> handle(
new nsMainThreadPtrHolder<ContinueLifecycleTask>(new ContinueInstallTask(this)));
nsRefPtr<ServiceWorker> serviceWorker; nsRefPtr<ServiceWorker> serviceWorker;
nsresult rv = nsresult rv =
swm->CreateServiceWorker(mRegistration->mPrincipal, swm->CreateServiceWorker(mRegistration->mPrincipal,
@ -637,15 +637,21 @@ public:
getter_AddRefs(serviceWorker)); getter_AddRefs(serviceWorker));
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
ContinueAfterInstallEvent(false /* success */, false /* activate immediately */); ContinueAfterInstallEvent(false /* aSuccess */, false /* aActivateImmediately */);
return; return;
} }
nsMainThreadPtrHandle<ContinueLifecycleTask> handle(
new nsMainThreadPtrHolder<ContinueLifecycleTask>(new ContinueInstallTask(this)));
nsRefPtr<LifecycleEventWorkerRunnable> r = nsRefPtr<LifecycleEventWorkerRunnable> r =
new LifecycleEventWorkerRunnable(serviceWorker->GetWorkerPrivate(), NS_LITERAL_STRING("install"), handle); new LifecycleEventWorkerRunnable(serviceWorker->GetWorkerPrivate(), NS_LITERAL_STRING("install"), handle);
AutoJSAPI jsapi; AutoJSAPI jsapi;
jsapi.Init(); jsapi.Init();
// This triggers Step 4.7 "Queue a task to run the following substeps..."
// which sends the install event to the worker.
r->Dispatch(jsapi.cx()); r->Dispatch(jsapi.cx());
} }
@ -727,7 +733,8 @@ private:
FailCommon(nsresult aRv) FailCommon(nsresult aRv)
{ {
mCallback = nullptr; mCallback = nullptr;
MaybeRemoveRegistration(); nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
swm->MaybeRemoveRegistration(mRegistration);
// Ensures that the job can't do anything useful from this point on. // Ensures that the job can't do anything useful from this point on.
mRegistration = nullptr; mRegistration = nullptr;
Done(aRv); Done(aRv);
@ -745,23 +752,8 @@ private:
} }
void void
MaybeRemoveRegistration() ContinueAfterInstallEvent(bool aInstallEventSuccess, bool aActivateImmediately)
{ {
MOZ_ASSERT(mRegistration);
nsRefPtr<ServiceWorkerInfo> newest = mRegistration->Newest();
if (!newest) {
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
swm->RemoveRegistration(mRegistration);
}
}
void
ContinueAfterInstallEvent(bool aSuccess, bool aActivateImmediately)
{
// By this point the callback should've been notified about success or fail
// and nulled.
MOZ_ASSERT(!mCallback);
if (!mRegistration->mInstallingWorker) { if (!mRegistration->mInstallingWorker) {
NS_WARNING("mInstallingWorker was null."); NS_WARNING("mInstallingWorker was null.");
return Done(NS_ERROR_DOM_ABORT_ERR); return Done(NS_ERROR_DOM_ABORT_ERR);
@ -770,12 +762,12 @@ private:
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
// "If installFailed is true" // "If installFailed is true"
if (!aSuccess) { if (!aInstallEventSuccess) {
mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Redundant); mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
mRegistration->mInstallingWorker = nullptr; mRegistration->mInstallingWorker = nullptr;
swm->InvalidateServiceWorkerRegistrationWorker(mRegistration, swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
WhichServiceWorker::INSTALLING_WORKER); WhichServiceWorker::INSTALLING_WORKER);
MaybeRemoveRegistration(); swm->MaybeRemoveRegistration(mRegistration);
return Done(NS_ERROR_DOM_ABORT_ERR); return Done(NS_ERROR_DOM_ABORT_ERR);
} }
@ -792,14 +784,14 @@ private:
// swapping it with waiting worker. // swapping it with waiting worker.
mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Installed); mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Installed);
mRegistration->mWaitingWorker = mRegistration->mInstallingWorker.forget(); mRegistration->mWaitingWorker = mRegistration->mInstallingWorker.forget();
mRegistration->mWaitingToActivate = false;
swm->InvalidateServiceWorkerRegistrationWorker(mRegistration, swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
WhichServiceWorker::INSTALLING_WORKER | WhichServiceWorker::WAITING_WORKER); WhichServiceWorker::INSTALLING_WORKER | WhichServiceWorker::WAITING_WORKER);
// FIXME(nsm): Bug 982711 Deal with activateImmediately. // FIXME(nsm): Bug 982711 Deal with activateImmediately.
NS_WARN_IF_FALSE(!aActivateImmediately, "Immediate activation using replace() is not supported yet"); NS_WARN_IF_FALSE(!aActivateImmediately, "Immediate activation using replace() is not supported yet");
mRegistration->TryToActivate();
Done(NS_OK); Done(NS_OK);
// Activate() is invoked out of band of atomic.
mRegistration->TryToActivate();
} }
}; };
@ -818,6 +810,8 @@ ContinueUpdateRunnable::Run()
void void
ContinueInstallTask::ContinueAfterWorkerEvent(bool aSuccess, bool aActivateImmediately) ContinueInstallTask::ContinueAfterWorkerEvent(bool aSuccess, bool aActivateImmediately)
{ {
// This does not start the job immediately if there are other jobs in the
// queue, which captures the "atomic" behaviour we want.
mJob->ContinueAfterInstallEvent(aSuccess, aActivateImmediately); mJob->ContinueAfterInstallEvent(aSuccess, aActivateImmediately);
} }
@ -1100,18 +1094,20 @@ LifecycleEventWorkerRunnable::DispatchLifecycleEvent(JSContext* aCx, WorkerPriva
void void
ServiceWorkerRegistrationInfo::TryToActivate() ServiceWorkerRegistrationInfo::TryToActivate()
{ {
mWaitingToActivate = true;
if (!IsControllingDocuments()) { if (!IsControllingDocuments()) {
Activate(); Activate();
} }
} }
void
ContinueActivateTask::ContinueAfterWorkerEvent(bool aSuccess, bool aActivateImmediately /* unused */)
{
mRegistration->FinishActivate(aSuccess);
}
void void
ServiceWorkerRegistrationInfo::Activate() ServiceWorkerRegistrationInfo::Activate()
{ {
MOZ_ASSERT(mWaitingToActivate);
mWaitingToActivate = false;
nsRefPtr<ServiceWorkerInfo> activatingWorker = mWaitingWorker; nsRefPtr<ServiceWorkerInfo> activatingWorker = mWaitingWorker;
nsRefPtr<ServiceWorkerInfo> exitingWorker = mActiveWorker; nsRefPtr<ServiceWorkerInfo> exitingWorker = mActiveWorker;
@ -1132,18 +1128,18 @@ ServiceWorkerRegistrationInfo::Activate()
mWaitingWorker = nullptr; mWaitingWorker = nullptr;
mActiveWorker->UpdateState(ServiceWorkerState::Activating); mActiveWorker->UpdateState(ServiceWorkerState::Activating);
// FIXME(nsm): Unlink appcache if there is one.
swm->CheckPendingReadyPromises(); swm->CheckPendingReadyPromises();
swm->StoreRegistration(mPrincipal, this); swm->StoreRegistration(mPrincipal, this);
// "Queue a task to fire a simple event named controllerchange..." // "Queue a task to fire a simple event named controllerchange..."
nsCOMPtr<nsIRunnable> controllerChangeRunnable = nsCOMPtr<nsIRunnable> controllerChangeRunnable =
NS_NewRunnableMethodWithArg<ServiceWorkerRegistrationInfo*>(swm, &ServiceWorkerManager::FireControllerChange, this); NS_NewRunnableMethodWithArg<ServiceWorkerRegistrationInfo*>(swm,
&ServiceWorkerManager::FireControllerChange,
this);
NS_DispatchToMainThread(controllerChangeRunnable); NS_DispatchToMainThread(controllerChangeRunnable);
// XXXnsm I have my doubts about this. Leaving the main thread means that
// subsequent calls to Activate() not from a Register() call, i.e. due to all
// controlled documents going away, may lead to two or more calls being
// interleaved.
MOZ_ASSERT(mActiveWorker); MOZ_ASSERT(mActiveWorker);
nsRefPtr<ServiceWorker> serviceWorker; nsRefPtr<ServiceWorker> serviceWorker;
nsresult rv = nsresult rv =
@ -1152,7 +1148,11 @@ ServiceWorkerRegistrationInfo::Activate()
mScope, mScope,
getter_AddRefs(serviceWorker)); getter_AddRefs(serviceWorker));
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
FinishActivate(false /* success */); nsCOMPtr<nsIRunnable> r =
NS_NewRunnableMethodWithArg<bool>(this,
&ServiceWorkerRegistrationInfo::FinishActivate,
false /* success */);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
return; return;
} }
@ -1688,7 +1688,10 @@ ServiceWorkerManager::HandleError(JSContext* aCx,
void void
ServiceWorkerRegistrationInfo::FinishActivate(bool aSuccess) ServiceWorkerRegistrationInfo::FinishActivate(bool aSuccess)
{ {
MOZ_ASSERT(mActiveWorker); if (mPendingUninstall || !mActiveWorker) {
return;
}
if (aSuccess) { if (aSuccess) {
mActiveWorker->UpdateState(ServiceWorkerState::Activated); mActiveWorker->UpdateState(ServiceWorkerState::Activated);
} else { } else {
@ -1933,10 +1936,6 @@ void
ServiceWorkerManager::MaybeStartControlling(nsIDocument* aDoc) ServiceWorkerManager::MaybeStartControlling(nsIDocument* aDoc)
{ {
AssertIsOnMainThread(); AssertIsOnMainThread();
if (!Preferences::GetBool("dom.serviceWorkers.enabled")) {
return;
}
nsRefPtr<ServiceWorkerRegistrationInfo> registration = nsRefPtr<ServiceWorkerRegistrationInfo> registration =
GetServiceWorkerRegistrationInfo(aDoc); GetServiceWorkerRegistrationInfo(aDoc);
if (registration) { if (registration) {
@ -1952,10 +1951,6 @@ void
ServiceWorkerManager::MaybeStopControlling(nsIDocument* aDoc) ServiceWorkerManager::MaybeStopControlling(nsIDocument* aDoc)
{ {
MOZ_ASSERT(aDoc); MOZ_ASSERT(aDoc);
if (!Preferences::GetBool("dom.serviceWorkers.enabled")) {
return;
}
nsRefPtr<ServiceWorkerRegistrationInfo> registration; nsRefPtr<ServiceWorkerRegistrationInfo> registration;
mControlledDocuments.Remove(aDoc, getter_AddRefs(registration)); mControlledDocuments.Remove(aDoc, getter_AddRefs(registration));
// A document which was uncontrolled does not maintain that state itself, so // A document which was uncontrolled does not maintain that state itself, so
@ -1963,6 +1958,14 @@ ServiceWorkerManager::MaybeStopControlling(nsIDocument* aDoc)
// associated registration. So this check is required. // associated registration. So this check is required.
if (registration) { if (registration) {
registration->StopControllingADocument(); registration->StopControllingADocument();
if (!registration->IsControllingDocuments()) {
if (registration->mPendingUninstall) {
registration->Clear();
RemoveRegistration(registration);
} else {
registration->TryToActivate();
}
}
} }
} }
@ -2410,4 +2413,14 @@ ServiceWorkerManager::CreateNewRegistration(const nsCString& aScope,
return registration; return registration;
} }
void
ServiceWorkerManager::MaybeRemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration)
{
MOZ_ASSERT(aRegistration);
nsRefPtr<ServiceWorkerInfo> newest = aRegistration->Newest();
if (!newest) {
RemoveRegistration(aRegistration);
}
}
END_WORKERS_NAMESPACE END_WORKERS_NAMESPACE

View File

@ -49,6 +49,7 @@ class ServiceWorkerJobQueue;
class ServiceWorkerJob : public nsISupports class ServiceWorkerJob : public nsISupports
{ {
protected:
// The queue keeps the jobs alive, so they can hold a rawptr back to the // The queue keeps the jobs alive, so they can hold a rawptr back to the
// queue. // queue.
ServiceWorkerJobQueue* mQueue; ServiceWorkerJobQueue* mQueue;
@ -151,7 +152,6 @@ public:
// removed since documents may be controlled. It is marked as // removed since documents may be controlled. It is marked as
// pendingUninstall and when all controlling documents go away, removed. // pendingUninstall and when all controlling documents go away, removed.
bool mPendingUninstall; bool mPendingUninstall;
bool mWaitingToActivate;
explicit ServiceWorkerRegistrationInfo(const nsACString& aScope, explicit ServiceWorkerRegistrationInfo(const nsACString& aScope,
nsIPrincipal* aPrincipal); nsIPrincipal* aPrincipal);
@ -289,13 +289,11 @@ class ServiceWorkerManager MOZ_FINAL
: public nsIServiceWorkerManager : public nsIServiceWorkerManager
, public nsIIPCBackgroundChildCreateCallback , public nsIIPCBackgroundChildCreateCallback
{ {
friend class ActivationRunnable;
friend class ServiceWorkerRegistrationInfo;
friend class ServiceWorkerRegisterJob;
friend class GetReadyPromiseRunnable; friend class GetReadyPromiseRunnable;
friend class GetRegistrationsRunnable; friend class GetRegistrationsRunnable;
friend class GetRegistrationRunnable; friend class GetRegistrationRunnable;
friend class QueueFireUpdateFoundRunnable; friend class ServiceWorkerRegisterJob;
friend class ServiceWorkerRegistrationInfo;
friend class ServiceWorkerUnregisterJob; friend class ServiceWorkerUnregisterJob;
public: public:
@ -349,6 +347,8 @@ public:
void void
RemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration) RemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration)
{ {
MOZ_ASSERT(aRegistration);
MOZ_ASSERT(!aRegistration->IsControllingDocuments());
MOZ_ASSERT(mServiceWorkerRegistrationInfos.Contains(aRegistration->mScope)); MOZ_ASSERT(mServiceWorkerRegistrationInfos.Contains(aRegistration->mScope));
ServiceWorkerManager::RemoveScope(mOrderedScopes, aRegistration->mScope); ServiceWorkerManager::RemoveScope(mOrderedScopes, aRegistration->mScope);
mServiceWorkerRegistrationInfos.Remove(aRegistration->mScope); mServiceWorkerRegistrationInfos.Remove(aRegistration->mScope);
@ -491,6 +491,9 @@ private:
void* aUnused); void* aUnused);
nsClassHashtable<nsISupportsHashKey, PendingReadyPromise> mPendingReadyPromises; nsClassHashtable<nsISupportsHashKey, PendingReadyPromise> mPendingReadyPromises;
void
MaybeRemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration);
mozilla::ipc::PBackgroundChild* mActor; mozilla::ipc::PBackgroundChild* mActor;

View File

@ -17,11 +17,11 @@
// Make sure to use good, unique messages, since the actual expression will not show up in test results. // Make sure to use good, unique messages, since the actual expression will not show up in test results.
function my_ok(result, msg) { function my_ok(result, msg) {
window.opener.postMessage({status: "ok", result: result, message: msg}, "*"); parent.postMessage({status: "ok", result: result, message: msg}, "*");
} }
function finish() { function finish() {
window.opener.postMessage({status: "done"}, "*"); parent.postMessage({status: "done"}, "*");
} }
navigator.serviceWorker.ready.then(function(swr) { navigator.serviceWorker.ready.then(function(swr) {

View File

@ -1,4 +1,4 @@
// Worker that errors on receiving an install event. // Worker that errors on receiving an install event.
oninstall = function(e) { oninstall = function(e) {
undefined.doSomething; undefined.doSomething;
} };

View File

@ -1,5 +1,5 @@
[DEFAULT] [DEFAULT]
skip-if = buildapp == 'b2g' || android_version == "10" # bug 1056702 skip-if = buildapp == 'b2g'
support-files = support-files =
worker.js worker.js
worker2.js worker2.js
@ -22,18 +22,16 @@ support-files =
message_posting_worker.js message_posting_worker.js
[test_unregister.html] [test_unregister.html]
skip-if = true # bug 1094375 skip-if = true # Bug 1133805
[test_installation_simple.html] [test_installation_simple.html]
skip-if = true # bug 1094375
[test_get_serviced.html] [test_get_serviced.html]
[test_install_event.html] [test_install_event.html]
[test_navigator.html] [test_navigator.html]
[test_scopes.html] [test_scopes.html]
skip-if = true # bug 1126470 and many others
[test_controller.html] [test_controller.html]
[test_workerUpdate.html] [test_workerUpdate.html]
skip-if = true # Enable after Bug 982726 postMessage is landed. skip-if = true # Bug 1133805
[test_workerUnregister.html] [test_workerUnregister.html]
skip-if = true # Enable after Bug 982726 postMessage is landed. skip-if = true # Bug 1133805
[test_post_message.html] [test_post_message.html]
[test_post_message_advanced.html] [test_post_message_advanced.html]

View File

@ -3,13 +3,11 @@
<body> <body>
<script type="text/javascript"> <script type="text/javascript">
window.addEventListener('message', function(evt) { window.addEventListener('message', function(evt) {
navigator.serviceWorker.ready.then(function() { navigator.serviceWorker.ready.then(function() {
navigator.serviceWorker.oncontrollerchange = function(e) { evt.ports[0].postMessage("WOW!");
evt.ports[0].postMessage("WOW!"); });
} }, false);
});
}, false);
</script> </script>
</body> </body>

View File

@ -15,6 +15,9 @@
<pre id="test"></pre> <pre id="test"></pre>
<script class="testbody" type="text/javascript"> <script class="testbody" type="text/javascript">
var content;
var iframe;
function simpleRegister() { function simpleRegister() {
// We use the control scope for the less specific registration. The window will register a worker on controller/ // We use the control scope for the less specific registration. The window will register a worker on controller/
return navigator.serviceWorker.register("worker.js", { scope: "./control" }); return navigator.serviceWorker.register("worker.js", { scope: "./control" });
@ -27,17 +30,23 @@
ok(e.data.result, e.data.message); ok(e.data.result, e.data.message);
} else if (e.data.status == "done") { } else if (e.data.status == "done") {
window.onmessage = null; window.onmessage = null;
w.close(); content.removeChild(iframe);
resolve(); resolve();
} }
} }
}); });
var w = window.open("controller/index.html"); content = document.getElementById("content");
ok(content, "Parent exists.");
iframe = document.createElement("iframe");
iframe.setAttribute('src', "controller/index.html");
content.appendChild(iframe);
return p; return p;
} }
// This document just flips the prefs and opens the window for the actual test. // This document just flips the prefs and opens the iframe for the actual test.
function runTest() { function runTest() {
simpleRegister() simpleRegister()
.then(testController) .then(testController)

View File

@ -38,9 +38,10 @@
function installError() { function installError() {
// Silence worker errors so they don't cause the test to fail. // Silence worker errors so they don't cause the test to fail.
window.onerror = function() { } window.onerror = function(e) {}
return navigator.serviceWorker.register("install_event_error_worker.js", { scope: "./install_event" }) return navigator.serviceWorker.register("install_event_error_worker.js", { scope: "./install_event" })
.then(function(swr) { .then(function(swr) {
ok(swr.installing instanceof ServiceWorker, "There should be an installing worker if promise resolves.");
ok(swr.installing.state == "installing", "Installing worker's state should be 'installing'"); ok(swr.installing.state == "installing", "Installing worker's state should be 'installing'");
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
swr.installing.onstatechange = function(e) { swr.installing.onstatechange = function(e) {
@ -85,7 +86,8 @@
} }
function runTest() { function runTest() {
simpleRegister() Promise.resolve()
.then(simpleRegister)
.then(nextRegister) .then(nextRegister)
.then(installError) .then(installError)
.then(activateError) .then(activateError)

View File

@ -75,25 +75,6 @@
}); });
} }
function abortPrevious() {
var p = navigator.serviceWorker.register("worker2.js", { scope: "foo/" });
var q = navigator.serviceWorker.register("worker3.js", { scope: "foo/" });
return Promise.all([
p.then(function(wr) {
ok(false, "First registration should fail with AbortError");
}, function(e) {
ok(e.name === "AbortError", "First registration should fail with AbortError");
}),
q.then(function(wr) {
ok(wr instanceof ServiceWorkerRegistration, "Second registration should succeed");
}, function(e) {
ok(false, "Second registration should succeed");
})
]);
}
function networkError404() { function networkError404() {
return navigator.serviceWorker.register("404.js", { scope: "network_error/"}).then(function(w) { return navigator.serviceWorker.register("404.js", { scope: "network_error/"}).then(function(w) {
ok(false, "Should fail with NetworkError"); ok(false, "Should fail with NetworkError");
@ -111,7 +92,6 @@
is(swr, undefined, "A failed registration for a scope with no prior controllers should clear itself"); is(swr, undefined, "A failed registration for a scope with no prior controllers should clear itself");
}); });
}, function(e) { }, function(e) {
info("NSM " + e.name);
ok(e instanceof Error, "Registration should fail with parse error"); ok(e instanceof Error, "Registration should fail with parse error");
}); });
} }
@ -140,8 +120,7 @@
window.onmessage = null; window.onmessage = null;
// We have to make frame navigate away, otherwise it will call // We have to make frame navigate away, otherwise it will call
// MaybeStopControlling() when this document is unloaded. At that point // MaybeStopControlling() when this document is unloaded. At that point
// the pref has been disabled, and so MaybeStopControlling() will just // the pref has been disabled, so the ServiceWorkerManager is not available.
// return since it is currently gated.
frame.setAttribute("src", new URL("about:blank").href); frame.setAttribute("src", new URL("about:blank").href);
resolve(); resolve();
} else if (e.data.type == "check") { } else if (e.data.type == "check") {
@ -183,7 +162,6 @@
.then(sameOriginScope) .then(sameOriginScope)
.then(httpsOnly) .then(httpsOnly)
.then(realWorker) .then(realWorker)
.then(abortPrevious)
.then(networkError404) .then(networkError404)
.then(parseError) .then(parseError)
.then(updatefound) .then(updatefound)

View File

@ -62,7 +62,7 @@
ok(getScope(p("sub/dir/afoo")) === p("sub/dir/a"), "Scope should match"); ok(getScope(p("sub/dir/afoo")) === p("sub/dir/a"), "Scope should match");
ok(getScope(p("star*wars")) === p("star*"), "Scope should match"); ok(getScope(p("star*wars")) === p("star*"), "Scope should match");
ok(getScope(p("star/a.html")) === p(""), "Scope should match"); ok(getScope(p("star/a.html")) === p(""), "Scope should match");
resolve(true); resolve();
}); });
} }

View File

@ -19,22 +19,19 @@
info("unregister/index.html should not to be launched directly!"); info("unregister/index.html should not to be launched directly!");
} }
SimpleTest.requestFlakyTimeout("Unfortunately we have no way to test for a page being uncontrolled except waiting for ready to not resolve");
var tId = setTimeout(function() { var tId = setTimeout(function() {
info("tId timeout!");
parent.postMessage({ controlled: false }, "*"); parent.postMessage({ controlled: false }, "*");
tId = null; tId = null;
}, 2000); }, 2000);
navigator.serviceWorker.ready.then(function() { navigator.serviceWorker.ready.then(function() {
info("Got ready");
if (tId == null) { if (tId == null) {
info("tId was null");
parent.postMessage("FAIL!!!", "*"); parent.postMessage("FAIL!!!", "*");
return; return;
} }
clearTimeout(tId); clearTimeout(tId);
info("tId was non-null");
parent.postMessage({ controlled: true }, "*"); parent.postMessage({ controlled: true }, "*");
}); });

View File

@ -13,7 +13,6 @@
#include "nsAppDirectoryServiceDefs.h" #include "nsAppDirectoryServiceDefs.h"
#include "nsIURI.h" #include "nsIURI.h"
#include "nsIIOService.h"
#include "nsIFileChannel.h" #include "nsIFileChannel.h"
#include "nsIFile.h" #include "nsIFile.h"
#include "nsGkAtoms.h" #include "nsGkAtoms.h"
@ -27,6 +26,7 @@
#include "mozIStorageService.h" #include "mozIStorageService.h"
#include "nsIChannel.h" #include "nsIChannel.h"
#include "nsIDocument.h" #include "nsIDocument.h"
#include "nsNetUtil.h"
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// //
@ -212,11 +212,13 @@ nsXULTemplateQueryProcessorStorage::GetDatasource(nsIArray* aDataSources,
} }
else { else {
nsCOMPtr<nsIChannel> channel; nsCOMPtr<nsIChannel> channel;
nsCOMPtr<nsIIOService> ioservice = nsCOMPtr<nsINode> node = do_QueryInterface(aRootNode);
do_GetService("@mozilla.org/network/io-service;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = ioservice->NewChannelFromURI(uri, getter_AddRefs(channel)); rv = NS_NewChannel(getter_AddRefs(channel),
uri,
node,
nsILoadInfo::SEC_NORMAL,
nsIContentPolicy::TYPE_OTHER);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(channel, &rv); nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(channel, &rv);

View File

@ -3628,11 +3628,11 @@ nsWebBrowserPersist::CreateChannelFromURI(nsIURI *aURI, nsIChannel **aChannel)
nsresult rv = NS_OK; nsresult rv = NS_OK;
*aChannel = nullptr; *aChannel = nullptr;
nsCOMPtr<nsIIOService> ioserv; rv = NS_NewChannel(aChannel,
ioserv = do_GetIOService(&rv); aURI,
NS_ENSURE_SUCCESS(rv, rv); nsContentUtils::GetSystemPrincipal(),
nsILoadInfo::SEC_NORMAL,
rv = ioserv->NewChannelFromURI(aURI, aChannel); nsIContentPolicy::TYPE_OTHER);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_ARG_POINTER(*aChannel); NS_ENSURE_ARG_POINTER(*aChannel);

View File

@ -1895,18 +1895,16 @@ nsPermissionManager::ImportDefaults()
return NS_OK; return NS_OK;
} }
nsresult rv;
nsCOMPtr<nsIIOService> ioservice =
do_GetService("@mozilla.org/network/io-service;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURI> defaultsURI; nsCOMPtr<nsIURI> defaultsURI;
rv = NS_NewURI(getter_AddRefs(defaultsURI), defaultsURL, nsresult rv = NS_NewURI(getter_AddRefs(defaultsURI), defaultsURL);
nullptr, nullptr, ioservice);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIChannel> channel; nsCOMPtr<nsIChannel> channel;
rv = ioservice->NewChannelFromURI(defaultsURI, getter_AddRefs(channel)); rv = NS_NewChannel(getter_AddRefs(channel),
defaultsURI,
nsContentUtils::GetSystemPrincipal(),
nsILoadInfo::SEC_NORMAL,
nsIContentPolicy::TYPE_OTHER);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIInputStream> inputStream; nsCOMPtr<nsIInputStream> inputStream;

View File

@ -24,6 +24,7 @@
#include "nsCRT.h" #include "nsCRT.h"
#include "nspr.h" #include "nspr.h"
#include "nsXULAppAPI.h" #include "nsXULAppAPI.h"
#include "nsContentUtils.h"
extern PRLogModuleInfo *MCD; extern PRLogModuleInfo *MCD;
@ -253,26 +254,23 @@ nsresult nsReadConfig::openAndEvaluateJSFile(const char *aFileName, int32_t obsc
return rv; return rv;
} else { } else {
nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
if (NS_FAILED(rv))
return rv;
nsAutoCString location("resource://gre/defaults/autoconfig/"); nsAutoCString location("resource://gre/defaults/autoconfig/");
location += aFileName; location += aFileName;
nsCOMPtr<nsIURI> uri; nsCOMPtr<nsIURI> uri;
rv = ioService->NewURI(location, nullptr, nullptr, getter_AddRefs(uri)); rv = NS_NewURI(getter_AddRefs(uri), location);
if (NS_FAILED(rv)) NS_ENSURE_SUCCESS(rv, rv);
return rv;
nsCOMPtr<nsIChannel> channel; nsCOMPtr<nsIChannel> channel;
rv = ioService->NewChannelFromURI(uri, getter_AddRefs(channel)); rv = NS_NewChannel(getter_AddRefs(channel),
if (NS_FAILED(rv)) uri,
return rv; nsContentUtils::GetSystemPrincipal(),
nsILoadInfo::SEC_NORMAL,
nsIContentPolicy::TYPE_OTHER);
NS_ENSURE_SUCCESS(rv, rv);
rv = channel->Open(getter_AddRefs(inStr)); rv = channel->Open(getter_AddRefs(inStr));
if (NS_FAILED(rv)) NS_ENSURE_SUCCESS(rv, rv);
return rv;
} }
uint64_t fs64; uint64_t fs64;

View File

@ -418,36 +418,20 @@ ClientTiledPaintedLayer::RenderLayer()
ToClientLayer(GetMaskLayer())->RenderLayer(); ToClientLayer(GetMaskLayer())->RenderLayer();
} }
// For more complex cases we need to calculate a bunch of metrics before we
// can do the paint.
BeginPaint();
if (mPaintData.mPaintFinished) {
return;
}
// In some cases we can take a fast path and just be done with it. // In some cases we can take a fast path and just be done with it.
if (UseFastPath()) { if (UseFastPath()) {
TILING_LOG("TILING %p: Taking fast-path\n", this); TILING_LOG("TILING %p: Taking fast-path\n", this);
mValidRegion = neededRegion; mValidRegion = neededRegion;
// Make sure that tiles that fall outside of the visible region or outside of the
// critical displayport are discarded on the first update. Also make sure that we
// only draw stuff inside the critical displayport on the first update.
if (!mPaintData.mCriticalDisplayPort.IsEmpty()) {
mValidRegion.And(mValidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort));
invalidRegion.And(invalidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort));
}
if (invalidRegion.IsEmpty()) {
EndPaint();
return;
}
mContentClient->mTiledBuffer.SetFrameResolution(mPaintData.mResolution);
mContentClient->mTiledBuffer.PaintThebes(mValidRegion, invalidRegion, callback, data); mContentClient->mTiledBuffer.PaintThebes(mValidRegion, invalidRegion, callback, data);
ClientManager()->Hold(this); ClientManager()->Hold(this);
mContentClient->UseTiledLayerBuffer(TiledContentClient::TILED_BUFFER); mContentClient->UseTiledLayerBuffer(TiledContentClient::TILED_BUFFER);
EndPaint(); return;
}
// For more complex cases we need to calculate a bunch of metrics before we
// can do the paint.
BeginPaint();
if (mPaintData.mPaintFinished) {
return; return;
} }

View File

@ -2137,13 +2137,8 @@ IsCoercionCall(ModuleCompiler &m, ParseNode *pn, AsmJSCoercion *coercion, ParseN
return true; return true;
} }
if (global->isSimdCtor() || if (global->isSimdOperation() && global->simdOperation() == AsmJSSimdOperation_check) {
(global->isSimdOperation() && global->simdOperation() == AsmJSSimdOperation_check)) switch (global->simdOperationType()) {
{
AsmJSSimdType type = global->isSimdCtor()
? global->simdCtorType()
: global->simdOperationType();
switch (type) {
case AsmJSSimdType_int32x4: case AsmJSSimdType_int32x4:
*coercion = AsmJS_ToInt32x4; *coercion = AsmJS_ToInt32x4;
return true; return true;
@ -5572,12 +5567,7 @@ class CheckSimdVectorScalarArgs
} }
// Second argument is the scalar // Second argument is the scalar
Type coercedFormalType = SimdToCoercedScalarType(formalSimdType_); return CheckSimdScalarArgs(formalSimdType_)(f, arg, argIndex, actualType, def);
if (!(actualType <= coercedFormalType)) {
return f.failf(arg, "%s is not a subtype of %s", actualType.toChars(),
coercedFormalType.toChars());
}
return true;
} }
}; };
@ -5981,11 +5971,6 @@ CheckSimdCtorCall(FunctionCompiler &f, ParseNode *call, const ModuleCompiler::Gl
{ {
MOZ_ASSERT(call->isKind(PNK_CALL)); MOZ_ASSERT(call->isKind(PNK_CALL));
AsmJSCoercion coercion;
ParseNode *argNode;
if (IsCoercionCall(f.m(), call, &coercion, &argNode))
return CheckCoercionArg(f, argNode, coercion, def, type);
AsmJSSimdType simdType = global->simdCtorType(); AsmJSSimdType simdType = global->simdCtorType();
unsigned length = SimdTypeToLength(simdType); unsigned length = SimdTypeToLength(simdType);
DefinitionVector defs; DefinitionVector defs;

View File

@ -350,17 +350,6 @@ SimdTypeDescr::call(JSContext *cx, unsigned argc, Value *vp)
CallArgs args = CallArgsFromVp(argc, vp); CallArgs args = CallArgsFromVp(argc, vp);
Rooted<SimdTypeDescr*> descr(cx, &args.callee().as<SimdTypeDescr>()); Rooted<SimdTypeDescr*> descr(cx, &args.callee().as<SimdTypeDescr>());
if (args.length() == 1) {
// SIMD type used as a coercion
if (!CheckVectorObject(args[0], descr->type())) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SIMD_NOT_A_VECTOR);
return false;
}
args.rval().setObject(args[0].toObject());
return true;
}
MOZ_ASSERT(size_t(static_cast<TypeDescr*>(descr)->size()) <= InlineTypedObject::MaximumSize, MOZ_ASSERT(size_t(static_cast<TypeDescr*>(descr)->size()) <= InlineTypedObject::MaximumSize,
"inline storage is needed for using InternalHandle belows"); "inline storage is needed for using InternalHandle belows");
@ -733,27 +722,18 @@ FuncWith(JSContext *cx, unsigned argc, Value *vp)
typedef typename V::Elem Elem; typedef typename V::Elem Elem;
CallArgs args = CallArgsFromVp(argc, vp); CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() != 2 || !IsVectorObject<V>(args[0]) || if (args.length() != 2 || !IsVectorObject<V>(args[0]))
(!args[1].isNumber() && !args[1].isBoolean()))
{
return ErrorBadArgs(cx); return ErrorBadArgs(cx);
}
Elem *val = TypedObjectMemory<Elem *>(args[0]); Elem *vec = TypedObjectMemory<Elem *>(args[0]);
Elem result[V::lanes]; Elem result[V::lanes];
if (args[1].isNumber()) { Elem value;
Elem withAsNumber; if (!V::toType(cx, args[1], &value))
if (!V::toType(cx, args[1], &withAsNumber)) return false;
return false;
for (unsigned i = 0; i < V::lanes; i++) for (unsigned i = 0; i < V::lanes; i++)
result[i] = OpWith<Elem>::apply(i, withAsNumber, val[i]); result[i] = OpWith<Elem>::apply(i, value, vec[i]);
} else {
MOZ_ASSERT(args[1].isBoolean());
bool withAsBool = args[1].toBoolean();
for (unsigned i = 0; i < V::lanes; i++)
result[i] = OpWith<Elem>::apply(i, withAsBool, val[i]);
}
return StoreResult<V>(cx, args, result); return StoreResult<V>(cx, args, result);
} }
@ -827,7 +807,7 @@ Int32x4BinaryScalar(JSContext *cx, unsigned argc, Value *vp)
return ErrorBadArgs(cx); return ErrorBadArgs(cx);
int32_t result[4]; int32_t result[4];
if (!IsVectorObject<Int32x4>(args[0]) || !args[1].isNumber()) if (!IsVectorObject<Int32x4>(args[0]))
return ErrorBadArgs(cx); return ErrorBadArgs(cx);
int32_t *val = TypedObjectMemory<int32_t *>(args[0]); int32_t *val = TypedObjectMemory<int32_t *>(args[0]);
@ -922,7 +902,7 @@ FuncSplat(JSContext *cx, unsigned argc, Value *vp)
typedef typename Vret::Elem RetElem; typedef typename Vret::Elem RetElem;
CallArgs args = CallArgsFromVp(argc, vp); CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() != 1 || !args[0].isNumber()) if (args.length() != 1)
return ErrorBadArgs(cx); return ErrorBadArgs(cx);
RetElem arg; RetElem arg;

View File

@ -273,7 +273,10 @@ struct Float32x4 {
return a; return a;
} }
static bool toType(JSContext *cx, JS::HandleValue v, Elem *out) { static bool toType(JSContext *cx, JS::HandleValue v, Elem *out) {
*out = v.toNumber(); double d;
if (!ToNumber(cx, v, &d))
return false;
*out = float(d);
return true; return true;
} }
static void setReturn(CallArgs &args, Elem value) { static void setReturn(CallArgs &args, Elem value) {
@ -293,8 +296,7 @@ struct Float64x2 {
return a; return a;
} }
static bool toType(JSContext *cx, JS::HandleValue v, Elem *out) { static bool toType(JSContext *cx, JS::HandleValue v, Elem *out) {
*out = v.toNumber(); return ToNumber(cx, v, out);
return true;
} }
static void setReturn(CallArgs &args, Elem value) { static void setReturn(CallArgs &args, Elem value) {
args.rval().setDouble(JS::CanonicalizeNaN(value)); args.rval().setDouble(JS::CanonicalizeNaN(value));

File diff suppressed because it is too large Load Diff

View File

@ -28,8 +28,10 @@ function assertEqX4(real, expected, assertFunc) {
} }
} }
try {
// Load / Store // Load / Store
var IMPORTS = USE_ASM + 'var H=new glob.Uint8Array(heap); var i4=glob.SIMD.int32x4; var load=i4.load; var store=i4.store;'; var IMPORTS = USE_ASM + 'var H=new glob.Uint8Array(heap); var i4=glob.SIMD.int32x4; var ci4=i4.check; var load=i4.load; var store=i4.store;';
// Bad number of args // Bad number of args
assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load();} return f"); assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load();} return f");
@ -63,9 +65,9 @@ assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(H, " + (IN
assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(H, " + (INT32_MAX + 1 - 15) + ");} return f"); assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(H, " + (INT32_MAX + 1 - 15) + ");} return f");
asmCompile('glob', 'ffi', 'heap', IMPORTS + "function f(){load(H, " + (INT32_MAX + 1 - 16) + ");} return f"); asmCompile('glob', 'ffi', 'heap', IMPORTS + "function f(){load(H, " + (INT32_MAX + 1 - 16) + ");} return f");
assertAsmLinkFail(asmCompile('glob', 'ffi', 'heap', IMPORTS + "function f() {return i4(load(H, " + (BUF_MIN - 15) + "));} return f"), this, {}, buf); assertAsmLinkFail(asmCompile('glob', 'ffi', 'heap', IMPORTS + "function f() {return ci4(load(H, " + (BUF_MIN - 15) + "));} return f"), this, {}, buf);
assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', IMPORTS + "function f() {return i4(load(H, " + (BUF_MIN - 16) + "));} return f"), this, {}, buf)(), [4, 3, 2, 1]); assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', IMPORTS + "function f() {return ci4(load(H, " + (BUF_MIN - 16) + "));} return f"), this, {}, buf)(), [4, 3, 2, 1]);
assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', IMPORTS + "function f() {return i4(load(H, " + BUF_MIN + " - 16 | 0));} return f"), this, {}, buf)(), [4, 3, 2, 1]); assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', IMPORTS + "function f() {return ci4(load(H, " + BUF_MIN + " - 16 | 0));} return f"), this, {}, buf)(), [4, 3, 2, 1]);
var CONSTANT_INDEX = 42; var CONSTANT_INDEX = 42;
var CONSTANT_BYTE_INDEX = CONSTANT_INDEX << 2; var CONSTANT_BYTE_INDEX = CONSTANT_INDEX << 2;
@ -78,33 +80,35 @@ var loadStoreCode = `
var i4 = glob.SIMD.int32x4; var i4 = glob.SIMD.int32x4;
var i4load = i4.load; var i4load = i4.load;
var i4store = i4.store; var i4store = i4.store;
var ci4 = i4.check;
var f4 = glob.SIMD.float32x4; var f4 = glob.SIMD.float32x4;
var f4load = f4.load; var f4load = f4.load;
var f4store = f4.store; var f4store = f4.store;
var cf4 = f4.check;
function f32l(i) { i=i|0; return f4(f4load(H, i|0)); } function f32l(i) { i=i|0; return cf4(f4load(H, i|0)); }
function f32lcst() { return f4(f4load(H, ${CONSTANT_BYTE_INDEX})); } function f32lcst() { return cf4(f4load(H, ${CONSTANT_BYTE_INDEX})); }
function f32s(i, vec) { i=i|0; vec=f4(vec); f4store(H, i|0, vec); } function f32s(i, vec) { i=i|0; vec=cf4(vec); f4store(H, i|0, vec); }
function f32scst(vec) { vec=f4(vec); f4store(H, ${CONSTANT_BYTE_INDEX}, vec); } function f32scst(vec) { vec=cf4(vec); f4store(H, ${CONSTANT_BYTE_INDEX}, vec); }
function i32l(i) { i=i|0; return i4(i4load(H, i|0)); } function i32l(i) { i=i|0; return ci4(i4load(H, i|0)); }
function i32lcst() { return i4(i4load(H, ${CONSTANT_BYTE_INDEX})); } function i32lcst() { return ci4(i4load(H, ${CONSTANT_BYTE_INDEX})); }
function i32s(i, vec) { i=i|0; vec=i4(vec); i4store(H, i|0, vec); } function i32s(i, vec) { i=i|0; vec=ci4(vec); i4store(H, i|0, vec); }
function i32scst(vec) { vec=i4(vec); i4store(H, ${CONSTANT_BYTE_INDEX}, vec); } function i32scst(vec) { vec=ci4(vec); i4store(H, ${CONSTANT_BYTE_INDEX}, vec); }
function f32lbndcheck(i) { function f32lbndcheck(i) {
i=i|0; i=i|0;
if ((i|0) > ${CONSTANT_BYTE_INDEX}) i=${CONSTANT_BYTE_INDEX}; if ((i|0) > ${CONSTANT_BYTE_INDEX}) i=${CONSTANT_BYTE_INDEX};
if ((i|0) < 0) i = 0; if ((i|0) < 0) i = 0;
return f4(f4load(H, i|0)); return cf4(f4load(H, i|0));
} }
function f32sbndcheck(i, vec) { function f32sbndcheck(i, vec) {
i=i|0; i=i|0;
vec=f4(vec); vec=cf4(vec);
if ((i|0) > ${CONSTANT_BYTE_INDEX}) i=${CONSTANT_BYTE_INDEX}; if ((i|0) > ${CONSTANT_BYTE_INDEX}) i=${CONSTANT_BYTE_INDEX};
if ((i|0) < 0) i = 0; if ((i|0) < 0) i = 0;
return f4(f4store(H, i|0, vec)); return cf4(f4store(H, i|0, vec));
} }
return { return {
@ -169,12 +173,12 @@ var code = `
x = (x>>0) < 0 ? 0 : x; x = (x>>0) < 0 ? 0 : x;
// ptr value gets a precise range but the bounds check shouldn't get // ptr value gets a precise range but the bounds check shouldn't get
// eliminated. // eliminated.
return f4(f4l(u8, 0xFFFA + x | 0)); return f4l(u8, 0xFFFA + x | 0);
} }
return g; return g;
`; `;
assertThrowsInstanceOf(() =>asmLink(asmCompile('glob', 'ffi', 'heap', code), this, {}, new ArrayBuffer(0x10000))(0), RangeError); assertThrowsInstanceOf(() => asmLink(asmCompile('glob', 'ffi', 'heap', code), this, {}, new ArrayBuffer(0x10000))(0), RangeError);
// Float32x4.store // Float32x4.store
function f32s(n, v) { return m.f32s((n|0) << 2 | 0, v); }; function f32s(n, v) { return m.f32s((n|0) << 2 | 0, v); };
@ -274,6 +278,7 @@ function MakeCodeFor(typeName) {
return ` return `
"use asm"; "use asm";
var type = glob.SIMD.${typeName}; var type = glob.SIMD.${typeName};
var c = type.check;
var lx = type.loadX; var lx = type.loadX;
var lxy = type.loadXY; var lxy = type.loadXY;
@ -293,13 +298,13 @@ function MakeCodeFor(typeName) {
function loadCstXY() { return lxy(u8, 41 << 2); } function loadCstXY() { return lxy(u8, 41 << 2); }
function loadCstXYZ() { return lxyz(u8, 41 << 2); } function loadCstXYZ() { return lxyz(u8, 41 << 2); }
function storeX(i, x) { i=i|0; x=type(x); return sx(u8, i, x); } function storeX(i, x) { i=i|0; x=c(x); return sx(u8, i, x); }
function storeXY(i, x) { i=i|0; x=type(x); return sxy(u8, i, x); } function storeXY(i, x) { i=i|0; x=c(x); return sxy(u8, i, x); }
function storeXYZ(i, x) { i=i|0; x=type(x); return sxyz(u8, i, x); } function storeXYZ(i, x) { i=i|0; x=c(x); return sxyz(u8, i, x); }
function storeCstX(x) { x=type(x); return sx(u8, 41 << 2, x); } function storeCstX(x) { x=c(x); return sx(u8, 41 << 2, x); }
function storeCstXY(x) { x=type(x); return sxy(u8, 41 << 2, x); } function storeCstXY(x) { x=c(x); return sxy(u8, 41 << 2, x); }
function storeCstXYZ(x) { x=type(x); return sxyz(u8, 41 << 2, x); } function storeCstXYZ(x) { x=c(x); return sxyz(u8, 41 << 2, x); }
return { return {
loadX: loadX, loadX: loadX,
@ -505,3 +510,5 @@ TestPartialLoads(mint32x4, i32,
TestPartialStores(mint32x4, i32, 'int32x4', 42, -3, 13, 37); TestPartialStores(mint32x4, i32, 'int32x4', 42, -3, 13, 37);
})(); })();
} catch (e) { print('stack: ', e.stack); throw e }

View File

@ -10,10 +10,12 @@ if (!isSimdAvailable() || typeof SIMD === 'undefined') {
} }
const I32 = 'var i4 = glob.SIMD.int32x4;' const I32 = 'var i4 = glob.SIMD.int32x4;'
const CI32 = 'var ci4 = i4.check;'
const I32A = 'var i4a = i4.add;' const I32A = 'var i4a = i4.add;'
const I32S = 'var i4s = i4.sub;' const I32S = 'var i4s = i4.sub;'
const I32M = 'var i4m = i4.mul;' const I32M = 'var i4m = i4.mul;'
const F32 = 'var f4 = glob.SIMD.float32x4;' const F32 = 'var f4 = glob.SIMD.float32x4;'
const CF32 = 'var cf4 = f4.check;'
const F32A = 'var f4a = f4.add;' const F32A = 'var f4a = f4.add;'
const F32S = 'var f4s = f4.sub;' const F32S = 'var f4s = f4.sub;'
const F32M = 'var f4m = f4.mul;' const F32M = 'var f4m = f4.mul;'
@ -37,15 +39,15 @@ function assertEqX4(real, expected, assertFunc) {
function CheckI4(header, code, expected) { function CheckI4(header, code, expected) {
// code needs to contain a local called x // code needs to contain a local called x
header = USE_ASM + I32 + F32 + header; header = USE_ASM + I32 + CI32 + F32 + header;
var observed = asmLink(asmCompile('glob', header + ';function f() {' + code + ';return i4(x)} return f'), this)(); var observed = asmLink(asmCompile('glob', header + ';function f() {' + code + ';return ci4(x)} return f'), this)();
assertEqX4(observed, expected); assertEqX4(observed, expected);
} }
function CheckF4(header, code, expected) { function CheckF4(header, code, expected) {
// code needs to contain a local called x // code needs to contain a local called x
header = USE_ASM + F32 + header; header = USE_ASM + F32 + CF32 + header;
var observed = asmLink(asmCompile('glob', header + ';function f() {' + code + ';return f4(x)} return f'), this)(); var observed = asmLink(asmCompile('glob', header + ';function f() {' + code + ';return cf4(x)} return f'), this)();
assertEqX4(observed, expected.map(Math.fround)); assertEqX4(observed, expected.map(Math.fround));
} }
@ -119,27 +121,27 @@ assertAsmTypeFail('glob', USE_ASM + I32 + F32 + "function f(i) {var x=f4(1,2,3,i
assertAsmTypeFail('glob', USE_ASM + I32 + "function f() {return +i4(1,2,3,4)} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + "function f() {return +i4(1,2,3,4)} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + "function f() {return i4(1,2,3,4)|0} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + "function f() {return i4(1,2,3,4)|0} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + FROUND + "function f() {return f32(i4(1,2,3,4))} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + FROUND + "function f() {return f32(i4(1,2,3,4))} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + "function f() {return f4(i4(1,2,3,4))} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CF32 + "function f() {return cf4(i4(1,2,3,4))} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + "function f() {return +f4(1,2,3,4)} return f"); assertAsmTypeFail('glob', USE_ASM + F32 + "function f() {return +f4(1,2,3,4)} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + "function f() {return f4(1,2,3,4)|0} return f"); assertAsmTypeFail('glob', USE_ASM + F32 + "function f() {return f4(1,2,3,4)|0} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + FROUND + "function f() {return f32(f4(1,2,3,4))} return f"); assertAsmTypeFail('glob', USE_ASM + F32 + FROUND + "function f() {return f32(f4(1,2,3,4))} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + "function f() {return i4(f4(1,2,3,4))} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + F32 + "function f() {return ci4(f4(1,2,3,4))} return f");
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + "function f() {return i4(1,2,3,4);} return f"), this)(), [1, 2, 3, 4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + "function f() {return i4(1,2,3,4);} return f"), this)(), [1, 2, 3, 4]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + "function f() {return i4(i4(1,2,3,4));} return f"), this)(), [1, 2, 3, 4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + "function f() {return ci4(i4(1,2,3,4));} return f"), this)(), [1, 2, 3, 4]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + "function f() {return f4(1,2,3,4);} return f"), this)(), [1, 2, 3, 4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + "function f() {return f4(1,2,3,4);} return f"), this)(), [1, 2, 3, 4]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + "function f() {return f4(f4(1,2,3,4));} return f"), this)(), [1, 2, 3, 4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + CF32 + "function f() {return cf4(f4(1,2,3,4));} return f"), this)(), [1, 2, 3, 4]);
assertEq(asmLink(asmCompile('glob', USE_ASM + I32 + "function f() {i4(1,2,3,4);} return f"), this)(), undefined); assertEq(asmLink(asmCompile('glob', USE_ASM + I32 + "function f() {i4(1,2,3,4);} return f"), this)(), undefined);
assertEq(asmLink(asmCompile('glob', USE_ASM + F32 + "function f() {f4(1,2,3,4);} return f"), this)(), undefined); assertEq(asmLink(asmCompile('glob', USE_ASM + F32 + "function f() {f4(1,2,3,4);} return f"), this)(), undefined);
// Int32x4 ctor should accept int? // Int32x4 ctor should accept int?
assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + I32 + "var i32=new glob.Int32Array(heap); function f(i) {i=i|0; return i4(i4(i32[i>>2], 2, 3, 4))} return f"), this, {}, new ArrayBuffer(0x10000))(0x20000), [0, 2, 3, 4]); assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + I32 + CI32 + "var i32=new glob.Int32Array(heap); function f(i) {i=i|0; return ci4(i4(i32[i>>2], 2, 3, 4))} return f"), this, {}, new ArrayBuffer(0x10000))(0x20000), [0, 2, 3, 4]);
// Float32x4 ctor should accept floatish (i.e. float || float? || floatish) and doublit // Float32x4 ctor should accept floatish (i.e. float || float? || floatish) and doublit
assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + F32 + FROUND + "var h=new glob.Float32Array(heap); function f(i) {i=i|0; return f4(f4(h[i>>2], f32(2), f32(3), f32(4)))} return f"), this, {}, new ArrayBuffer(0x10000))(0x20000), [NaN, 2, 3, 4]); assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + F32 + CF32 + FROUND + "var h=new glob.Float32Array(heap); function f(i) {i=i|0; return cf4(f4(h[i>>2], f32(2), f32(3), f32(4)))} return f"), this, {}, new ArrayBuffer(0x10000))(0x20000), [NaN, 2, 3, 4]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + FROUND + "function f(i) {i=i|0; return f4(f4(f32(1) + f32(2), f32(2), f32(3), f32(4)))} return f"), this, {}, new ArrayBuffer(0x10000))(0x20000), [3, 2, 3, 4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + CF32 + FROUND + "function f(i) {i=i|0; return cf4(f4(f32(1) + f32(2), f32(2), f32(3), f32(4)))} return f"), this, {}, new ArrayBuffer(0x10000))(0x20000), [3, 2, 3, 4]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + FROUND + "function f(i) {i=i|0; return f4(f4(f32(1) + f32(2), 2.0, 3.0, 4.0))} return f"), this, {}, new ArrayBuffer(0x10000))(0x20000), [3, 2, 3, 4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + CF32 + FROUND + "function f(i) {i=i|0; return cf4(f4(f32(1) + f32(2), 2.0, 3.0, 4.0))} return f"), this, {}, new ArrayBuffer(0x10000))(0x20000), [3, 2, 3, 4]);
// 1.3.2 Reading values out of lanes // 1.3.2 Reading values out of lanes
assertAsmTypeFail('glob', USE_ASM + "function f() {var x=1; return x.y | 0;} return f"); assertAsmTypeFail('glob', USE_ASM + "function f() {var x=1; return x.y | 0;} return f");
@ -240,73 +242,63 @@ assertAsmTypeFail('glob', USE_ASM + F32 + I32 + "function f() {var x=f4(1,2,3,4)
assertAsmTypeFail('glob', USE_ASM + F32 + I32 + "function f() {var x=f4(1,2,3,4); var y=i4(1,2,3,4); x=1?y:y;} return f"); assertAsmTypeFail('glob', USE_ASM + F32 + I32 + "function f() {var x=f4(1,2,3,4); var y=i4(1,2,3,4); x=1?y:y;} return f");
CheckF4('', 'var x=f4(1,2,3,4); var y=f4(4,3,2,1); x=3?y:x', [4, 3, 2, 1]); CheckF4('', 'var x=f4(1,2,3,4); var y=f4(4,3,2,1); x=3?y:x', [4, 3, 2, 1]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + "function f(x) {x=x|0; var v=f4(1,2,3,4); var w=f4(5,6,7,8); return f4(x?w:v);} return f"), this)(1), [5,6,7,8]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + CF32 + "function f(x) {x=x|0; var v=f4(1,2,3,4); var w=f4(5,6,7,8); return cf4(x?w:v);} return f"), this)(1), [5,6,7,8]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + "function f(v) {v=f4(v); var w=f4(5,6,7,8); return f4(4?w:v);} return f"), this)(SIMD.float32x4(1,2,3,4)), [5,6,7,8]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + CF32 + "function f(v) {v=cf4(v); var w=f4(5,6,7,8); return cf4(4?w:v);} return f"), this)(SIMD.float32x4(1,2,3,4)), [5,6,7,8]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + "function f(v, x) {v=f4(v); x=x|0; var w=f4(5,6,7,8); return f4(x?w:v);} return f"), this)(SIMD.float32x4(1,2,3,4), 0), [1,2,3,4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + CF32 + "function f(v, x) {v=cf4(v); x=x|0; var w=f4(5,6,7,8); return cf4(x?w:v);} return f"), this)(SIMD.float32x4(1,2,3,4), 0), [1,2,3,4]);
CheckI4('', 'var x=i4(1,2,3,4); var y=i4(4,3,2,1); x=(x.x|0)?y:x', [4, 3, 2, 1]); CheckI4('', 'var x=i4(1,2,3,4); var y=i4(4,3,2,1); x=(x.x|0)?y:x', [4, 3, 2, 1]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + "function f(x) {x=x|0; var v=i4(1,2,3,4); var w=i4(5,6,7,8); return i4(x?w:v);} return f"), this)(1), [5,6,7,8]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + "function f(x) {x=x|0; var v=i4(1,2,3,4); var w=i4(5,6,7,8); return ci4(x?w:v);} return f"), this)(1), [5,6,7,8]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + "function f(v) {v=i4(v); var w=i4(5,6,7,8); return i4(4?w:v);} return f"), this)(SIMD.int32x4(1,2,3,4)), [5,6,7,8]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + "function f(v) {v=ci4(v); var w=i4(5,6,7,8); return ci4(4?w:v);} return f"), this)(SIMD.int32x4(1,2,3,4)), [5,6,7,8]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + "function f(v, x) {v=i4(v); x=x|0; var w=i4(5,6,7,8); return i4(x?w:v);} return f"), this)(SIMD.int32x4(1,2,3,4), 0), [1,2,3,4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + "function f(v, x) {v=ci4(v); x=x|0; var w=i4(5,6,7,8); return ci4(x?w:v);} return f"), this)(SIMD.int32x4(1,2,3,4), 0), [1,2,3,4]);
// 1.3.4 Return values // 1.3.4 Return values
assertAsmTypeFail('glob', USE_ASM + I32 + "function f() {var x=1; return i4(x)} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + "function f() {var x=1; return ci4(x)} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + "function f() {var x=1; return i4(x + x)} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + "function f() {var x=1; return ci4(x + x)} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + "function f() {var x=1.; return i4(x)} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + "function f() {var x=1.; return ci4(x)} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + FROUND + "function f() {var x=f32(1.); return i4(x)} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + FROUND + "function f() {var x=f32(1.); return ci4(x)} return f");
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + "function f() {var x=i4(1,2,3,4); return i4(x)} return f"), this)(), [1,2,3,4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + "function f() {var x=i4(1,2,3,4); return ci4(x)} return f"), this)(), [1,2,3,4]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + "function f() {var x=f4(1,2,3,4); return f4(x)} return f"), this)(), [1,2,3,4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + CF32 + "function f() {var x=f4(1,2,3,4); return cf4(x)} return f"), this)(), [1,2,3,4]);
// 1.3.5 Coerce and pass arguments // 1.3.5 Coerce and pass arguments
// Via check // Via check
const CHECK_I32 = 'var c=i4.check;'; assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + "function f() {ci4();} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + "function f(x) {x=ci4(x); ci4(x, x);} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + CHECK_I32 + "function f() {c();} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + "function f() {ci4(1);} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + CHECK_I32 + "function f(x) {x=i4(x); c(x, x);} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + "function f() {ci4(1.);} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + CHECK_I32 + "function f() {c(1);} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + FROUND + "function f() {ci4(f32(1.));} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + CHECK_I32 + "function f() {c(1.);} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + F32 + CF32 + "function f(x) {x=cf4(x); ci4(x);} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + CHECK_I32 + FROUND + "function f() {c(f32(1.));} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + "function f(x) {x=ci4(x); return 1 + ci4(x) | 0;} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + CHECK_I32 + F32 + "function f(x) {x=f4(x); c(x);} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + CHECK_I32 + "function f(x) {x=i4(x); return 1 + c(x) | 0;} return f");
var i32x4 = SIMD.int32x4(1, 3, 3, 7); var i32x4 = SIMD.int32x4(1, 3, 3, 7);
assertEq(asmLink(asmCompile('glob', USE_ASM + I32 + CHECK_I32 + "function f(x) {x=c(x)} return f"), this)(i32x4), undefined); assertEq(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + "function f(x) {x=ci4(x)} return f"), this)(i32x4), undefined);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CHECK_I32 + "function f(x) {x=i4(x); return c(x);} return f"), this)(i32x4), [1,3,3,7]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + "function f(x) {x=ci4(x); return ci4(x);} return f"), this)(i32x4), [1,3,3,7]);
const CHECK_F32 = 'var c=f4.check;'; assertAsmTypeFail('glob', USE_ASM + F32 + CF32 + "function f() {cf4();} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + CF32 + "function f(x) {x=cf4(x); cf4(x, x);} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + CHECK_F32 + "function f() {c();} return f"); assertAsmTypeFail('glob', USE_ASM + F32 + CF32 + "function f() {cf4(1);} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + CHECK_F32 + "function f(x) {x=f4(x); c(x, x);} return f"); assertAsmTypeFail('glob', USE_ASM + F32 + CF32 + "function f() {cf4(1.);} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + CHECK_F32 + "function f() {c(1);} return f"); assertAsmTypeFail('glob', USE_ASM + F32 + CF32 + FROUND + "function f() {cf4(f32(1.));} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + CHECK_F32 + "function f() {c(1.);} return f"); assertAsmTypeFail('glob', USE_ASM + F32 + CF32 + F32 + CF32 + "function f(x) {x=cf4(x); cf4(x);} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + CHECK_F32 + FROUND + "function f() {c(f32(1.));} return f"); assertAsmTypeFail('glob', USE_ASM + F32 + CF32 + "function f(x) {x=cf4(x); return 1 + cf4(x) | 0;} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + CHECK_F32 + I32 + "function f(x) {x=i4(x); c(x);} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + CHECK_F32 + "function f(x) {x=f4(x); return 1 + c(x) | 0;} return f");
var f32x4 = SIMD.float32x4(13.37, 42.42, -0, NaN); var f32x4 = SIMD.float32x4(13.37, 42.42, -0, NaN);
assertEq(asmLink(asmCompile('glob', USE_ASM + F32 + CHECK_F32 + "function f(x) {x=c(x)} return f"), this)(f32x4), undefined); assertEq(asmLink(asmCompile('glob', USE_ASM + F32 + CF32 + "function f(x) {x=cf4(x)} return f"), this)(f32x4), undefined);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + CHECK_F32 + "function f(x) {x=c(x); return c(x);} return f"), this)(f32x4), assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + CF32 + "function f(x) {x=cf4(x); return cf4(x);} return f"), this)(f32x4), [13.37, 42.42, -0, NaN].map(Math.fround));
[Math.fround(13.37), Math.fround(42.42), -0, NaN]);
// Legacy coercions // Legacy coercions
assertAsmTypeFail('glob', USE_ASM + I32 + "function f(x) {x=i4();} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + "function f(x) {x=i4();} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + "function f(x) {x=i4(x);} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + "function f(x) {x=i4(1,2,3,4);} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + "function f(x) {x=i4(1,2,3,4);} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + "function f(x,y) {x=i4(y);y=+y} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + "function f(x,y) {x=i4(y);y=+y} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + "function f(x) {x=ci4(x); return i4(x);} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + "function f(x) {return +i4(1,2,3,4)} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + "function f(x) {return +i4(1,2,3,4)} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + "function f(x) {return 0|i4(1,2,3,4)} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + "function f(x) {return 0|i4(1,2,3,4)} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + FROUND + "function f(x) {return f32(i4(1,2,3,4))} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + FROUND + "function f(x) {return f32(i4(1,2,3,4))} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + "function f(x) {return f4(i4(1,2,3,4))} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + F32 + "function f(x) {return f4(i4(1,2,3,4))} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + CF32 + "function f(x) {x=cf4(x); return f4(x);} return f");
var i32x4 = SIMD.int32x4(1, 3, 3, 7);
assertEq(asmLink(asmCompile('glob', USE_ASM + I32 + "function f(x) {x=i4(x)} return f"), this)(i32x4), undefined);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + "function f(x) {x=i4(x); return i4(x);} return f"), this)(i32x4), [1,3,3,7]);
var f32x4 = SIMD.float32x4(13.37, 42.42, -0, NaN);
assertEq(asmLink(asmCompile('glob', USE_ASM + F32 + "function f(x) {x=f4(x)} return f"), this)(f32x4), undefined);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + "function f(x) {x=f4(x); return f4(x);} return f"), this)(f32x4),
[Math.fround(13.37), Math.fround(42.42), -0, NaN]);
function assertCaught(f) { function assertCaught(f) {
var caught = false; var caught = false;
@ -320,14 +312,14 @@ function assertCaught(f) {
assertEq(caught, true); assertEq(caught, true);
} }
var f = asmLink(asmCompile('glob', USE_ASM + F32 + "function f(x) {x=f4(x); return f4(x);} return f"), this); var f = asmLink(asmCompile('glob', USE_ASM + F32 + CF32 + "function f(x) {x=cf4(x); return cf4(x);} return f"), this);
assertCaught(f); assertCaught(f);
assertCaught(f, 1); assertCaught(f, 1);
assertCaught(f, {}); assertCaught(f, {});
assertCaught(f, "I sincerely am a SIMD typed object."); assertCaught(f, "I sincerely am a SIMD typed object.");
assertCaught(f, SIMD.int32x4(1,2,3,4)); assertCaught(f, SIMD.int32x4(1,2,3,4));
var f = asmLink(asmCompile('glob', USE_ASM + I32 + "function f(x) {x=i4(x); return i4(x);} return f"), this); var f = asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + "function f(x) {x=ci4(x); return ci4(x);} return f"), this);
assertCaught(f); assertCaught(f);
assertCaught(f, 1); assertCaught(f, 1);
assertCaught(f, {}); assertCaught(f, {});
@ -345,8 +337,8 @@ assertAsmTypeFail('glob', USE_ASM + F32 + "var g=f4(1., 2., 3., 4.); function f(
assertAsmTypeFail('glob', USE_ASM + F32 + "var g=f4(1., 2., 3., 4.); function f() {var x=4.; x=+g;} return f"); assertAsmTypeFail('glob', USE_ASM + F32 + "var g=f4(1., 2., 3., 4.); function f() {var x=4.; x=+g;} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + "var g=f4(1., 2., 3., 4.); var f32=glob.Math.fround; function f() {var x=f32(4.); x=f32(g);} return f"); assertAsmTypeFail('glob', USE_ASM + F32 + "var g=f4(1., 2., 3., 4.); var f32=glob.Math.fround; function f() {var x=f32(4.); x=f32(g);} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + I32 + "var g=f4(1., 2., 3., 4.); function f() {var x=i4(1,2,3,4); x=i4(g);} return f"); assertAsmTypeFail('glob', USE_ASM + F32 + I32 + CI32 + "var g=f4(1., 2., 3., 4.); function f() {var x=i4(1,2,3,4); x=ci4(g);} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + "var g=i4(1,2,3,4); function f() {var x=f4(1.,2.,3.,4.); x=f4(g);} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CF32 + "var g=i4(1,2,3,4); function f() {var x=f4(1.,2.,3.,4.); x=cf4(g);} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + "var g=0; function f() {var x=i4(1,2,3,4); x=g|0;} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + "var g=0; function f() {var x=i4(1,2,3,4); x=g|0;} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + "var g=0.; function f() {var x=i4(1,2,3,4); x=+g;} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + "var g=0.; function f() {var x=i4(1,2,3,4); x=+g;} return f");
@ -372,10 +364,10 @@ assertAsmTypeFail('glob', USE_ASM + F32 + "var g=f4(1., 2., 3., 4.); function f(
assertAsmTypeFail('glob', USE_ASM + F32 + "var g=f4(1., 2., 3., 4.); function f() {var x=4.; g=+x;} return f"); assertAsmTypeFail('glob', USE_ASM + F32 + "var g=f4(1., 2., 3., 4.); function f() {var x=4.; g=+x;} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + "var g=f4(1., 2., 3., 4.); var f32=glob.Math.fround; function f() {var x=f32(4.); g=f32(x);} return f"); assertAsmTypeFail('glob', USE_ASM + F32 + "var g=f4(1., 2., 3., 4.); var f32=glob.Math.fround; function f() {var x=f32(4.); g=f32(x);} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + I32 + "var g=f4(1., 2., 3., 4.); function f() {var x=i4(1,2,3,4); g=i4(x);} return f"); assertAsmTypeFail('glob', USE_ASM + F32 + I32 + CI32 + "var g=f4(1., 2., 3., 4.); function f() {var x=i4(1,2,3,4); g=ci4(x);} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + I32 + "var g=f4(1., 2., 3., 4.); function f() {var x=i4(1,2,3,4); g=f4(x);} return f"); assertAsmTypeFail('glob', USE_ASM + F32 + I32 + CF32 + "var g=f4(1., 2., 3., 4.); function f() {var x=i4(1,2,3,4); g=cf4(x);} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + "var g=i4(1,2,3,4); function f() {var x=f4(1.,2.,3.,4.); g=f4(x);} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CF32 + "var g=i4(1,2,3,4); function f() {var x=f4(1.,2.,3.,4.); g=cf4(x);} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + "var g=i4(1,2,3,4); function f() {var x=f4(1.,2.,3.,4.); g=i4(x);} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CI32 + "var g=i4(1,2,3,4); function f() {var x=f4(1.,2.,3.,4.); g=ci4(x);} return f");
CheckI4('var x=i4(0,0,0,0);', 'x=i4(1,2,3,4)', [1,2,3,4]); CheckI4('var x=i4(0,0,0,0);', 'x=i4(1,2,3,4)', [1,2,3,4]);
CheckF4('var x=f4(0.,0.,0.,0.);', 'x=f4(5.,3.,4.,2.)', [5,3,4,2]); CheckF4('var x=f4(0.,0.,0.,0.);', 'x=f4(5.,3.,4.,2.)', [5,3,4,2]);
@ -385,32 +377,32 @@ CheckF4('var x=f4(0,0,0,0); var y=42; var z=3.9; var w=13.37', 'x=f4(1,2,3,4); y
// 1.3.6.2 Imported globals // 1.3.6.2 Imported globals
// Read // Read
var int32x4 = asmLink(asmCompile('glob', 'ffi', USE_ASM + I32 + "var g=i4(ffi.g); function f() {return i4(g)} return f"), this, {g: SIMD.int32x4(1,2,3,4)})(); var int32x4 = asmLink(asmCompile('glob', 'ffi', USE_ASM + I32 + CI32 + "var g=ci4(ffi.g); function f() {return ci4(g)} return f"), this, {g: SIMD.int32x4(1,2,3,4)})();
assertEq(int32x4.x, 1); assertEq(int32x4.x, 1);
assertEq(int32x4.y, 2); assertEq(int32x4.y, 2);
assertEq(int32x4.z, 3); assertEq(int32x4.z, 3);
assertEq(int32x4.w, 4); assertEq(int32x4.w, 4);
for (var v of [1, {}, "totally legit SIMD variable", SIMD.float32x4(1,2,3,4)]) for (var v of [1, {}, "totally legit SIMD variable", SIMD.float32x4(1,2,3,4)])
assertCaught(asmCompile('glob', 'ffi', USE_ASM + I32 + "var g=i4(ffi.g); function f() {return i4(g)} return f"), this, {g: v}); assertCaught(asmCompile('glob', 'ffi', USE_ASM + I32 + CI32 + "var g=ci4(ffi.g); function f() {return ci4(g)} return f"), this, {g: v});
var float32x4 = asmLink(asmCompile('glob', 'ffi', USE_ASM + F32 + "var g=f4(ffi.g); function f() {return f4(g)} return f"), this, {g: SIMD.float32x4(1,2,3,4)})(); var float32x4 = asmLink(asmCompile('glob', 'ffi', USE_ASM + F32 + CF32 + "var g=cf4(ffi.g); function f() {return cf4(g)} return f"), this, {g: SIMD.float32x4(1,2,3,4)})();
assertEq(float32x4.x, 1); assertEq(float32x4.x, 1);
assertEq(float32x4.y, 2); assertEq(float32x4.y, 2);
assertEq(float32x4.z, 3); assertEq(float32x4.z, 3);
assertEq(float32x4.w, 4); assertEq(float32x4.w, 4);
for (var v of [1, {}, "totally legit SIMD variable", SIMD.int32x4(1,2,3,4)]) for (var v of [1, {}, "totally legit SIMD variable", SIMD.int32x4(1,2,3,4)])
assertCaught(asmCompile('glob', 'ffi', USE_ASM + F32 + "var g=f4(ffi.g); function f() {return f4(g)} return f"), this, {g: v}); assertCaught(asmCompile('glob', 'ffi', USE_ASM + F32 + CF32 + "var g=cf4(ffi.g); function f() {return cf4(g)} return f"), this, {g: v});
// Write // Write
var int32x4 = asmLink(asmCompile('glob', 'ffi', USE_ASM + I32 + "var g=i4(ffi.g); function f() {g=i4(4,5,6,7); return i4(g)} return f"), this, {g: SIMD.int32x4(1,2,3,4)})(); var int32x4 = asmLink(asmCompile('glob', 'ffi', USE_ASM + I32 + CI32 + "var g=ci4(ffi.g); function f() {g=i4(4,5,6,7); return ci4(g)} return f"), this, {g: SIMD.int32x4(1,2,3,4)})();
assertEq(int32x4.x, 4); assertEq(int32x4.x, 4);
assertEq(int32x4.y, 5); assertEq(int32x4.y, 5);
assertEq(int32x4.z, 6); assertEq(int32x4.z, 6);
assertEq(int32x4.w, 7); assertEq(int32x4.w, 7);
var float32x4 = asmLink(asmCompile('glob', 'ffi', USE_ASM + F32 + "var g=f4(ffi.g); function f() {g=f4(4.,5.,6.,7.); return f4(g)} return f"), this, {g: SIMD.float32x4(1,2,3,4)})(); var float32x4 = asmLink(asmCompile('glob', 'ffi', USE_ASM + F32 + CF32 + "var g=cf4(ffi.g); function f() {g=f4(4.,5.,6.,7.); return cf4(g)} return f"), this, {g: SIMD.float32x4(1,2,3,4)})();
assertEq(float32x4.x, 4); assertEq(float32x4.x, 4);
assertEq(float32x4.y, 5); assertEq(float32x4.y, 5);
assertEq(float32x4.z, 6); assertEq(float32x4.z, 6);
@ -458,31 +450,31 @@ assertAsmTypeFail('glob', USE_ASM + I32 + F32 + F32A + "function f() {var x=i4(0
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + F32A + "function f() {var x=i4(0,0,0,0); var y=f4(4,3,2,1); y=f4a(x, y);} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + F32 + F32A + "function f() {var x=i4(0,0,0,0); var y=f4(4,3,2,1); y=f4a(x, y);} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + F32A + "function f() {var x=i4(0,0,0,0); var y=f4(4,3,2,1); x=f4a(y, y);} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + F32 + F32A + "function f() {var x=i4(0,0,0,0); var y=f4(4,3,2,1); x=f4a(y, y);} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + I32A + 'function f() {var x=i4(1,2,3,4); var y=0; y=i4(x,x)|0} return f'); assertAsmTypeFail('glob', USE_ASM + I32 + I32A + 'function f() {var x=i4(1,2,3,4); var y=0; y=i4a(x,x)|0} return f');
assertAsmTypeFail('glob', USE_ASM + I32 + I32A + 'function f() {var x=i4(1,2,3,4); var y=0.; y=+i4(x,x)} return f'); assertAsmTypeFail('glob', USE_ASM + I32 + I32A + 'function f() {var x=i4(1,2,3,4); var y=0.; y=+i4a(x,x)} return f');
CheckI4(I32A, 'var z=i4(1,2,3,4); var y=i4(0,1,0,3); var x=i4(0,0,0,0); x=i4a(z,y)', [1,3,3,7]); CheckI4(I32A, 'var z=i4(1,2,3,4); var y=i4(0,1,0,3); var x=i4(0,0,0,0); x=i4a(z,y)', [1,3,3,7]);
CheckI4(I32A, 'var x=i4(2,3,4,5); var y=i4(0,1,0,3); x=i4a(x,y)', [2,4,4,8]); CheckI4(I32A, 'var x=i4(2,3,4,5); var y=i4(0,1,0,3); x=i4a(x,y)', [2,4,4,8]);
CheckI4(I32A, 'var x=i4(1,2,3,4); x=i4a(x,x)', [2,4,6,8]); CheckI4(I32A, 'var x=i4(1,2,3,4); x=i4a(x,x)', [2,4,6,8]);
CheckI4(I32A, 'var x=i4(' + INT32_MAX + ',2,3,4); var y=i4(1,1,0,3); x=i4a(x,y)', [INT32_MIN,3,3,7]); CheckI4(I32A, 'var x=i4(' + INT32_MAX + ',2,3,4); var y=i4(1,1,0,3); x=i4a(x,y)', [INT32_MIN,3,3,7]);
CheckI4(I32A, 'var x=i4(' + INT32_MAX + ',2,3,4); var y=i4(1,1,0,3); x=i4(i4a(x,y))', [INT32_MIN,3,3,7]); CheckI4(I32A, 'var x=i4(' + INT32_MAX + ',2,3,4); var y=i4(1,1,0,3); x=ci4(i4a(x,y))', [INT32_MIN,3,3,7]);
CheckF4(F32A, 'var x=f4(1,2,3,4); x=f4a(x,x)', [2,4,6,8]); CheckF4(F32A, 'var x=f4(1,2,3,4); x=f4a(x,x)', [2,4,6,8]);
CheckF4(F32A, 'var x=f4(1,2,3,4); var y=f4(4,3,5,2); x=f4a(x,y)', [5,5,8,6]); CheckF4(F32A, 'var x=f4(1,2,3,4); var y=f4(4,3,5,2); x=f4a(x,y)', [5,5,8,6]);
CheckF4(F32A, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=f4a(x,y)', [Math.fround(13.37) + 4,5,8,6]); CheckF4(F32A, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=f4a(x,y)', [Math.fround(13.37) + 4,5,8,6]);
CheckF4(F32A, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=f4(f4a(x,y))', [Math.fround(13.37) + 4,5,8,6]); CheckF4(F32A, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=cf4(f4a(x,y))', [Math.fround(13.37) + 4,5,8,6]);
// 2.3.2. Subtracts // 2.3.2. Subtracts
CheckI4(I32S, 'var x=i4(1,2,3,4); var y=i4(-1,1,0,2); x=i4s(x,y)', [2,1,3,2]); CheckI4(I32S, 'var x=i4(1,2,3,4); var y=i4(-1,1,0,2); x=i4s(x,y)', [2,1,3,2]);
CheckI4(I32S, 'var x=i4(5,4,3,2); var y=i4(1,2,3,4); x=i4s(x,y)', [4,2,0,-2]); CheckI4(I32S, 'var x=i4(5,4,3,2); var y=i4(1,2,3,4); x=i4s(x,y)', [4,2,0,-2]);
CheckI4(I32S, 'var x=i4(1,2,3,4); x=i4s(x,x)', [0,0,0,0]); CheckI4(I32S, 'var x=i4(1,2,3,4); x=i4s(x,x)', [0,0,0,0]);
CheckI4(I32S, 'var x=i4(' + INT32_MIN + ',2,3,4); var y=i4(1,1,0,3); x=i4s(x,y)', [INT32_MAX,1,3,1]); CheckI4(I32S, 'var x=i4(' + INT32_MIN + ',2,3,4); var y=i4(1,1,0,3); x=i4s(x,y)', [INT32_MAX,1,3,1]);
CheckI4(I32S, 'var x=i4(' + INT32_MIN + ',2,3,4); var y=i4(1,1,0,3); x=i4(i4s(x,y))', [INT32_MAX,1,3,1]); CheckI4(I32S, 'var x=i4(' + INT32_MIN + ',2,3,4); var y=i4(1,1,0,3); x=ci4(i4s(x,y))', [INT32_MAX,1,3,1]);
CheckF4(F32S, 'var x=f4(1,2,3,4); x=f4s(x,x)', [0,0,0,0]); CheckF4(F32S, 'var x=f4(1,2,3,4); x=f4s(x,x)', [0,0,0,0]);
CheckF4(F32S, 'var x=f4(1,2,3,4); var y=f4(4,3,5,2); x=f4s(x,y)', [-3,-1,-2,2]); CheckF4(F32S, 'var x=f4(1,2,3,4); var y=f4(4,3,5,2); x=f4s(x,y)', [-3,-1,-2,2]);
CheckF4(F32S, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=f4s(x,y)', [Math.fround(13.37) - 4,-1,-2,2]); CheckF4(F32S, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=f4s(x,y)', [Math.fround(13.37) - 4,-1,-2,2]);
CheckF4(F32S, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=f4(f4s(x,y))', [Math.fround(13.37) - 4,-1,-2,2]); CheckF4(F32S, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=cf4(f4s(x,y))', [Math.fround(13.37) - 4,-1,-2,2]);
// 2.3.3. Multiplications / Divisions // 2.3.3. Multiplications / Divisions
assertAsmTypeFail('glob', USE_ASM + I32 + "var f4d=i4.div; function f() {} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + "var f4d=i4.div; function f() {} return f");
@ -501,11 +493,11 @@ CheckI4(I32M, 'var x=i4(1,2,3,4); x=i4m(x,x)', [1,4,9,16]);
CheckF4(F32M, 'var x=f4(1,2,3,4); x=f4m(x,x)', [1,4,9,16]); CheckF4(F32M, 'var x=f4(1,2,3,4); x=f4m(x,x)', [1,4,9,16]);
CheckF4(F32M, 'var x=f4(1,2,3,4); var y=f4(4,3,5,2); x=f4m(x,y)', [4,6,15,8]); CheckF4(F32M, 'var x=f4(1,2,3,4); var y=f4(4,3,5,2); x=f4m(x,y)', [4,6,15,8]);
CheckF4(F32M, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=f4m(x,y)', [Math.fround(13.37) * 4,6,15,8]); CheckF4(F32M, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=f4m(x,y)', [Math.fround(13.37) * 4,6,15,8]);
CheckF4(F32M, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=f4(f4m(x,y))', [Math.fround(13.37) * 4,6,15,8]); CheckF4(F32M, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=cf4(f4m(x,y))', [Math.fround(13.37) * 4,6,15,8]);
var f32x4 = SIMD.float32x4(0, NaN, -0, NaN); var f32x4 = SIMD.float32x4(0, NaN, -0, NaN);
var another = SIMD.float32x4(NaN, -1, -0, NaN); var another = SIMD.float32x4(NaN, -1, -0, NaN);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + F32M + "function f(x, y) {x=f4(x); y=f4(y); x=f4m(x,y); return f4(x);} return f"), this)(f32x4, another), [NaN, NaN, 0, NaN]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + F32M + CF32 + "function f(x, y) {x=cf4(x); y=cf4(y); x=f4m(x,y); return cf4(x);} return f"), this)(f32x4, another), [NaN, NaN, 0, NaN]);
CheckF4(F32D, 'var x=f4(1,2,3,4); x=f4d(x,x)', [1,1,1,1]); CheckF4(F32D, 'var x=f4(1,2,3,4); x=f4d(x,x)', [1,1,1,1]);
CheckF4(F32D, 'var x=f4(1,2,3,4); var y=f4(4,3,5,2); x=f4d(x,y)', [1/4,2/3,3/5,2]); CheckF4(F32D, 'var x=f4(1,2,3,4); var y=f4(4,3,5,2); x=f4d(x,y)', [1/4,2/3,3/5,2]);
@ -513,11 +505,11 @@ CheckF4(F32D, 'var x=f4(13.37,1,1,4); var y=f4(4,0,-0.,2); x=f4d(x,y)', [Math.fr
var f32x4 = SIMD.float32x4(0, 0, -0, NaN); var f32x4 = SIMD.float32x4(0, 0, -0, NaN);
var another = SIMD.float32x4(0, -0, 0, 0); var another = SIMD.float32x4(0, -0, 0, 0);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + F32D + "function f(x,y) {x=f4(x); y=f4(y); x=f4d(x,y); return f4(x);} return f"), this)(f32x4, another), [NaN, NaN, NaN, NaN]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + F32D + CF32 + "function f(x,y) {x=cf4(x); y=cf4(y); x=f4d(x,y); return cf4(x);} return f"), this)(f32x4, another), [NaN, NaN, NaN, NaN]);
// Unary arithmetic operators // Unary arithmetic operators
function CheckUnaryF4(op, checkFunc, assertFunc) { function CheckUnaryF4(op, checkFunc, assertFunc) {
var _ = asmLink(asmCompile('glob', USE_ASM + F32 + 'var op=f4.' + op + '; function f(x){x=f4(x); return f4(op(x)); } return f'), this); var _ = asmLink(asmCompile('glob', USE_ASM + F32 + CF32 + 'var op=f4.' + op + '; function f(x){x=cf4(x); return cf4(op(x)); } return f'), this);
return function(input) { return function(input) {
var simd = SIMD.float32x4(input[0], input[1], input[2], input[3]); var simd = SIMD.float32x4(input[0], input[1], input[2], input[3]);
@ -528,7 +520,7 @@ function CheckUnaryF4(op, checkFunc, assertFunc) {
} }
function CheckUnaryI4(op, checkFunc) { function CheckUnaryI4(op, checkFunc) {
var _ = asmLink(asmCompile('glob', USE_ASM + I32 + 'var op=i4.' + op + '; function f(x){x=i4(x); return i4(op(x)); } return f'), this); var _ = asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + 'var op=i4.' + op + '; function f(x){x=ci4(x); return ci4(op(x)); } return f'), this);
return function(input) { return function(input) {
var simd = SIMD.int32x4(input[0], input[1], input[2], input[3]); var simd = SIMD.int32x4(input[0], input[1], input[2], input[3]);
assertEqX4(_(simd), input.map(checkFunc).map(function(x) { return x | 0})); assertEqX4(_(simd), input.map(checkFunc).map(function(x) { return x | 0}));
@ -622,7 +614,6 @@ const WZF = 'var w = f4.withZ;';
const WWF = 'var w = f4.withW;'; const WWF = 'var w = f4.withW;';
assertAsmTypeFail('glob', USE_ASM + F32 + WXF + "function f() {var x = f4(1,2,3,4); x = w(x, 1);} return f"); assertAsmTypeFail('glob', USE_ASM + F32 + WXF + "function f() {var x = f4(1,2,3,4); x = w(x, 1);} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + WXF + "function f() {var x = f4(1,2,3,4); x = w(x, 1.0);} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + WXF + "function f() {var x = f4(1,2,3,4); x = w(x, x);} return f"); assertAsmTypeFail('glob', USE_ASM + F32 + WXF + "function f() {var x = f4(1,2,3,4); x = w(x, x);} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + WXF + FROUND + "function f() {var x = f4(1,2,3,4); x = w(1, f32(1));} return f"); assertAsmTypeFail('glob', USE_ASM + F32 + WXF + FROUND + "function f() {var x = f4(1,2,3,4); x = w(1, f32(1));} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + WXF + FROUND + "function f() {var x = f4(1,2,3,4); x = w(1., f32(1));} return f"); assertAsmTypeFail('glob', USE_ASM + F32 + WXF + FROUND + "function f() {var x = f4(1,2,3,4); x = w(1., f32(1));} return f");
@ -635,6 +626,11 @@ CheckF4(WZF + FROUND, 'var x = f4(1,2,3,4); x = w(x, f32(13.37));', [1, 2, Math.
CheckF4(WWF + FROUND, 'var x = f4(1,2,3,4); x = w(x, f32(13.37));', [1, 2, 3, Math.fround(13.37)]); CheckF4(WWF + FROUND, 'var x = f4(1,2,3,4); x = w(x, f32(13.37));', [1, 2, 3, Math.fround(13.37)]);
CheckF4(WWF + FROUND, 'var x = f4(1,2,3,4); x = w(x, f32(13.37) + f32(6.63));', [1, 2, 3, Math.fround(Math.fround(13.37) + Math.fround(6.63))]); CheckF4(WWF + FROUND, 'var x = f4(1,2,3,4); x = w(x, f32(13.37) + f32(6.63));', [1, 2, 3, Math.fround(Math.fround(13.37) + Math.fround(6.63))]);
CheckF4(WXF + FROUND, 'var x = f4(1,2,3,4); x = w(x, 13.37);', [Math.fround(13.37), 2, 3, 4]);
CheckF4(WYF + FROUND, 'var x = f4(1,2,3,4); x = w(x, 13.37);', [1, Math.fround(13.37), 3, 4]);
CheckF4(WZF + FROUND, 'var x = f4(1,2,3,4); x = w(x, 13.37);', [1, 2, Math.fround(13.37), 4]);
CheckF4(WWF + FROUND, 'var x = f4(1,2,3,4); x = w(x, 13.37);', [1, 2, 3, Math.fround(13.37)]);
const WXI = 'var w = i4.withX;'; const WXI = 'var w = i4.withX;';
const WYI = 'var w = i4.withY;'; const WYI = 'var w = i4.withY;';
const WZI = 'var w = i4.withZ;'; const WZI = 'var w = i4.withZ;';
@ -729,7 +725,7 @@ assertAsmTypeFail('glob', USE_ASM + F32 + "var cvt=f4.fromFloat32x4; return {}")
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CVTIF + "function f() {var x=i4(1,2,3,4); x=cvt(x);} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CVTIF + "function f() {var x=i4(1,2,3,4); x=cvt(x);} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CVTIF + "function f() {var x=f4(1,2,3,4); x=cvt(x);} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CVTIF + "function f() {var x=f4(1,2,3,4); x=cvt(x);} return f");
var f = asmLink(asmCompile('glob', USE_ASM + I32 + F32 + CVTIF + 'function f(x){x=i4(x); var y=f4(0,0,0,0); y=cvt(x); return f4(y);} return f'), this); var f = asmLink(asmCompile('glob', USE_ASM + I32 + F32 + CF32 + CI32 + CVTIF + 'function f(x){x=ci4(x); var y=f4(0,0,0,0); y=cvt(x); return cf4(y);} return f'), this);
assertEqX4(f(SIMD.int32x4(1,2,3,4)), [1, 2, 3, 4]); assertEqX4(f(SIMD.int32x4(1,2,3,4)), [1, 2, 3, 4]);
assertEqX4(f(SIMD.int32x4(0,INT32_MIN,INT32_MAX,-1)), [0, Math.fround(INT32_MIN), Math.fround(INT32_MAX), -1]); assertEqX4(f(SIMD.int32x4(0,INT32_MIN,INT32_MAX,-1)), [0, Math.fround(INT32_MIN), Math.fround(INT32_MAX), -1]);
@ -738,7 +734,7 @@ assertEqX4(f(SIMD.int32x4(0,INT32_MIN,INT32_MAX,-1)), [0, Math.fround(INT32_MIN)
// tests, we assume x86/x64, so a conversion which failed will return the // tests, we assume x86/x64, so a conversion which failed will return the
// undefined int32 value. See also bug 1068028. // undefined int32 value. See also bug 1068028.
const UNDEFINED_INT32 = 0x80000000 | 0; const UNDEFINED_INT32 = 0x80000000 | 0;
var f = asmLink(asmCompile('glob', USE_ASM + I32 + F32 + CVTFI + 'function f(x){x=f4(x); var y=i4(0,0,0,0); y=cvt(x); return i4(y);} return f'), this); var f = asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + F32 + CF32 + CVTFI + 'function f(x){x=cf4(x); var y=i4(0,0,0,0); y=cvt(x); return ci4(y);} return f'), this);
assertEqX4(f(SIMD.float32x4(1,2,3,4)), [1, 2, 3, 4]); assertEqX4(f(SIMD.float32x4(1,2,3,4)), [1, 2, 3, 4]);
assertEqX4(f(SIMD.float32x4(NaN,Infinity,-Infinity,-0)), [UNDEFINED_INT32, UNDEFINED_INT32, UNDEFINED_INT32, 0]); assertEqX4(f(SIMD.float32x4(NaN,Infinity,-Infinity,-0)), [UNDEFINED_INT32, UNDEFINED_INT32, UNDEFINED_INT32, 0]);
@ -771,19 +767,19 @@ assertAsmTypeFail('glob', USE_ASM + F32 + "var cvt=f4.fromFloat32x4; return {}")
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CVTIFB + "function f() {var x=i4(1,2,3,4); x=cvt(x);} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CVTIFB + "function f() {var x=i4(1,2,3,4); x=cvt(x);} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CVTIFB + "function f() {var x=f4(1,2,3,4); x=cvt(x);} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CVTIFB + "function f() {var x=f4(1,2,3,4); x=cvt(x);} return f");
var f = asmLink(asmCompile('glob', USE_ASM + I32 + F32 + CVTIFB + 'function f(x){x=i4(x); var y=f4(0,0,0,0); y=cvt(x); return f4(y);} return f'), this); var f = asmLink(asmCompile('glob', USE_ASM + I32 + F32 + CVTIFB + CF32 + CI32 + 'function f(x){x=ci4(x); var y=f4(0,0,0,0); y=cvt(x); return cf4(y);} return f'), this);
assertEqX4(f(SIMD.int32x4(1,2,3,4)), [1, 2, 3, 4].map(cast.fromInt32Bits)); assertEqX4(f(SIMD.int32x4(1,2,3,4)), [1, 2, 3, 4].map(cast.fromInt32Bits));
assertEqX4(f(SIMD.int32x4(0,INT32_MIN,INT32_MAX,-1)), [0, INT32_MIN, INT32_MAX, -1].map(cast.fromInt32Bits)); assertEqX4(f(SIMD.int32x4(0,INT32_MIN,INT32_MAX,-1)), [0, INT32_MIN, INT32_MAX, -1].map(cast.fromInt32Bits));
var f = asmLink(asmCompile('glob', USE_ASM + I32 + F32 + F32A + CVTIFB + 'function f(x){x=i4(x); var y=f4(0,0,0,0); var z=f4(1,1,1,1); y=cvt(x); y=f4a(y, z); return f4(y)} return f'), this); var f = asmLink(asmCompile('glob', USE_ASM + I32 + F32 + F32A + CVTIFB + CF32 + CI32 + 'function f(x){x=ci4(x); var y=f4(0,0,0,0); var z=f4(1,1,1,1); y=cvt(x); y=f4a(y, z); return cf4(y)} return f'), this);
assertEqX4(f(SIMD.int32x4(1,2,3,4)), [1, 2, 3, 4].map(cast.fromInt32Bits).map((x) => x+1)); assertEqX4(f(SIMD.int32x4(1,2,3,4)), [1, 2, 3, 4].map(cast.fromInt32Bits).map((x) => x+1));
assertEqX4(f(SIMD.int32x4(0,INT32_MIN,INT32_MAX,-1)), [0, INT32_MIN, INT32_MAX, -1].map(cast.fromInt32Bits).map((x) => x+1)); assertEqX4(f(SIMD.int32x4(0,INT32_MIN,INT32_MAX,-1)), [0, INT32_MIN, INT32_MAX, -1].map(cast.fromInt32Bits).map((x) => x+1));
var f = asmLink(asmCompile('glob', USE_ASM + I32 + F32 + CVTFIB + 'function f(x){x=f4(x); var y=i4(0,0,0,0); y=cvt(x); return i4(y);} return f'), this); var f = asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + F32 + CF32 + CVTFIB + 'function f(x){x=cf4(x); var y=i4(0,0,0,0); y=cvt(x); return ci4(y);} return f'), this);
assertEqX4(f(SIMD.float32x4(1,2,3,4)), [1, 2, 3, 4].map(cast.fromFloat32Bits)); assertEqX4(f(SIMD.float32x4(1,2,3,4)), [1, 2, 3, 4].map(cast.fromFloat32Bits));
assertEqX4(f(SIMD.float32x4(-0,NaN,+Infinity,-Infinity)), [-0, NaN, +Infinity, -Infinity].map(cast.fromFloat32Bits)); assertEqX4(f(SIMD.float32x4(-0,NaN,+Infinity,-Infinity)), [-0, NaN, +Infinity, -Infinity].map(cast.fromFloat32Bits));
var f = asmLink(asmCompile('glob', USE_ASM + I32 + F32 + I32A + CVTFIB + 'function f(x){x=f4(x); var y=i4(0,0,0,0); var z=i4(1,1,1,1); y=cvt(x); y=i4a(y,z); return i4(y);} return f'), this); var f = asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + F32 + CF32 + I32A + CVTFIB + 'function f(x){x=cf4(x); var y=i4(0,0,0,0); var z=i4(1,1,1,1); y=cvt(x); y=i4a(y,z); return ci4(y);} return f'), this);
assertEqX4(f(SIMD.float32x4(1,2,3,4)), [1, 2, 3, 4].map(cast.fromFloat32Bits).map((x) => x+1)); assertEqX4(f(SIMD.float32x4(1,2,3,4)), [1, 2, 3, 4].map(cast.fromFloat32Bits).map((x) => x+1));
assertEqX4(f(SIMD.float32x4(-0,NaN,+Infinity,-Infinity)), [-0, NaN, +Infinity, -Infinity].map(cast.fromFloat32Bits).map((x) => x+1)); assertEqX4(f(SIMD.float32x4(-0,NaN,+Infinity,-Infinity)), [-0, NaN, +Infinity, -Infinity].map(cast.fromFloat32Bits).map((x) => x+1));
@ -826,10 +822,10 @@ const LSHI = 'var lsh=i4.shiftLeftByScalar;'
const RSHI = 'var rsh=i4.shiftRightArithmeticByScalar;' const RSHI = 'var rsh=i4.shiftRightArithmeticByScalar;'
const URSHI = 'var ursh=i4.shiftRightLogicalByScalar;' const URSHI = 'var ursh=i4.shiftRightLogicalByScalar;'
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + FROUND + LSHI + "function f() {var x=f4(1,2,3,4); return i4(lsh(x,f32(42)));} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + F32 + FROUND + LSHI + "function f() {var x=f4(1,2,3,4); return ci4(lsh(x,f32(42)));} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + FROUND + LSHI + "function f() {var x=f4(1,2,3,4); return i4(lsh(x,42));} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + F32 + FROUND + LSHI + "function f() {var x=f4(1,2,3,4); return ci4(lsh(x,42));} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + FROUND + LSHI + "function f() {var x=i4(1,2,3,4); return i4(lsh(x,42.0));} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + FROUND + LSHI + "function f() {var x=i4(1,2,3,4); return ci4(lsh(x,42.0));} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + FROUND + LSHI + "function f() {var x=i4(1,2,3,4); return i4(lsh(x,f32(42)));} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + FROUND + LSHI + "function f() {var x=i4(1,2,3,4); return ci4(lsh(x,f32(42)));} return f");
var input = 'i4(0, 1, ' + INT32_MIN + ', ' + INT32_MAX + ')'; var input = 'i4(0, 1, ' + INT32_MIN + ', ' + INT32_MAX + ')';
var vinput = [0, 1, INT32_MIN, INT32_MAX]; var vinput = [0, 1, INT32_MIN, INT32_MAX];
@ -841,9 +837,9 @@ function Lsh(i) { if (i > 31) return () => 0; return function(x) { return (x <<
function Rsh(i) { if (i > 31) return (x) => (x<0)?-1:0; return function(x) { return (x >> i) | 0 } } function Rsh(i) { if (i > 31) return (x) => (x<0)?-1:0; return function(x) { return (x >> i) | 0 } }
function Ursh(i) { if (i > 31) return () => 0; return function(x) { return (x >>> i) | 0 } } function Ursh(i) { if (i > 31) return () => 0; return function(x) { return (x >>> i) | 0 } }
var asmLsh = asmLink(asmCompile('glob', USE_ASM + I32 + LSHI + 'function f(x, y){x=x|0;y=y|0; var v=' + input + ';return i4(lsh(v, x+y))} return f;'), this) var asmLsh = asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + LSHI + 'function f(x, y){x=x|0;y=y|0; var v=' + input + ';return ci4(lsh(v, x+y))} return f;'), this)
var asmRsh = asmLink(asmCompile('glob', USE_ASM + I32 + RSHI + 'function f(x, y){x=x|0;y=y|0; var v=' + input + ';return i4(rsh(v, x+y))} return f;'), this) var asmRsh = asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + RSHI + 'function f(x, y){x=x|0;y=y|0; var v=' + input + ';return ci4(rsh(v, x+y))} return f;'), this)
var asmUrsh = asmLink(asmCompile('glob', USE_ASM + I32 + URSHI + 'function f(x, y){x=x|0;y=y|0; var v=' + input + ';return i4(ursh(v, x+y))} return f;'), this) var asmUrsh = asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + URSHI + 'function f(x, y){x=x|0;y=y|0; var v=' + input + ';return ci4(ursh(v, x+y))} return f;'), this)
for (var i = 1; i < 64; i++) { for (var i = 1; i < 64; i++) {
CheckI4(LSHI, 'var x=' + input + '; x=lsh(x, ' + i + ')', vinput.map(Lsh(i))); CheckI4(LSHI, 'var x=' + input + '; x=lsh(x, ' + i + ')', vinput.map(Lsh(i)));
@ -861,51 +857,51 @@ const F32SEL = 'var f4sel = f4.select;'
const I32BSEL = 'var i4sel = i4.bitselect;' const I32BSEL = 'var i4sel = i4.bitselect;'
const F32BSEL = 'var f4sel = f4.bitselect;' const F32BSEL = 'var f4sel = f4.bitselect;'
assertAsmTypeFail('glob', USE_ASM + F32 + I32SEL + "function f() {var x=f4(1,2,3,4); return i4(i4sel(x,x,x));} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CI32 + I32SEL + "function f() {var x=f4(1,2,3,4); return ci4(i4sel(x,x,x));} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + I32SEL + "function f() {var m=f4(1,2,3,4); var x=i4(1,2,3,4); return i4(i4sel(m,x,x));} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CI32 + I32SEL + "function f() {var m=f4(1,2,3,4); var x=i4(1,2,3,4); return ci4(i4sel(m,x,x));} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + I32SEL + "function f() {var m=f4(1,2,3,4); var x=f4(1,2,3,4); return i4(i4sel(m,x,x));} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CI32 + I32SEL + "function f() {var m=f4(1,2,3,4); var x=f4(1,2,3,4); return ci4(i4sel(m,x,x));} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + I32SEL + "function f() {var m=i4(1,2,3,4); var x=f4(1,2,3,4); return i4(i4sel(m,x,x));} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CI32 + I32SEL + "function f() {var m=i4(1,2,3,4); var x=f4(1,2,3,4); return ci4(i4sel(m,x,x));} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + I32SEL + "function f() {var m=i4(1,2,3,4); var x=f4(1,2,3,4); var y=i4(5,6,7,8); return i4(i4sel(m,x,y));} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CI32 + I32SEL + "function f() {var m=i4(1,2,3,4); var x=f4(1,2,3,4); var y=i4(5,6,7,8); return ci4(i4sel(m,x,y));} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + I32SEL + "function f() {var m=i4(1,2,3,4); var x=i4(1,2,3,4); var y=f4(5,6,7,8); return i4(i4sel(m,x,y));} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CI32 + I32SEL + "function f() {var m=i4(1,2,3,4); var x=i4(1,2,3,4); var y=f4(5,6,7,8); return ci4(i4sel(m,x,y));} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + I32SEL + "function f() {var m=i4(1,2,3,4); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return i4(i4sel(m,x,y));} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CI32 + I32SEL + "function f() {var m=i4(1,2,3,4); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return ci4(i4sel(m,x,y));} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + F32SEL + "function f() {var m=f4(1,2,3,4); return f4(f4sel(x,x,x));} return f"); assertAsmTypeFail('glob', USE_ASM + F32 + CF32 + F32SEL + "function f() {var m=f4(1,2,3,4); return cf4(f4sel(x,x,x));} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + F32SEL + "function f() {var m=f4(1,2,3,4); var x=i4(1,2,3,4); return f4(f4sel(m,x,x));} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CF32 + F32SEL + "function f() {var m=f4(1,2,3,4); var x=i4(1,2,3,4); return cf4(f4sel(m,x,x));} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + F32SEL + "function f() {var m=f4(1,2,3,4); var x=f4(1,2,3,4); return f4(f4sel(m,x,x));} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CF32 + F32SEL + "function f() {var m=f4(1,2,3,4); var x=f4(1,2,3,4); return cf4(f4sel(m,x,x));} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + F32SEL + "function f() {var m=i4(1,2,3,4); var x=f4(1,2,3,4); var y=i4(5,6,7,8); return f4(f4sel(m,x,y));} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CF32 + F32SEL + "function f() {var m=i4(1,2,3,4); var x=f4(1,2,3,4); var y=i4(5,6,7,8); return cf4(f4sel(m,x,y));} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + F32SEL + "function f() {var m=i4(1,2,3,4); var x=i4(1,2,3,4); var y=f4(5,6,7,8); return f4(f4sel(m,x,y));} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CF32 + F32SEL + "function f() {var m=i4(1,2,3,4); var x=i4(1,2,3,4); var y=f4(5,6,7,8); return cf4(f4sel(m,x,y));} return f");
// These pass with select but not bitselect // These pass with select but not bitselect
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + I32SEL + "function f() {var m=i4(0,0,0,0); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return i4(i4sel(m,x,y)); } return f"), this)(), [5, 6, 7, 8]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + I32SEL + "function f() {var m=i4(0,0,0,0); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return ci4(i4sel(m,x,y)); } return f"), this)(), [5, 6, 7, 8]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + I32SEL + "function f() {var m=i4(-1,-2,-3,-42); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return i4(i4sel(m,x,y)); } return f"), this)(), [1, 2, 3, 4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + I32SEL + "function f() {var m=i4(-1,-2,-3,-42); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return ci4(i4sel(m,x,y)); } return f"), this)(), [1, 2, 3, 4]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + I32SEL + "function f() {var m=i4(1,-1,2,-2); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return i4(i4sel(m,x,y)); } return f"), this)(), [5, 2, 7, 4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + I32SEL + "function f() {var m=i4(1,-1,2,-2); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return ci4(i4sel(m,x,y)); } return f"), this)(), [5, 2, 7, 4]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + I32SEL + "function f() {var m=i4(42,45,-42,-47); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return i4(i4sel(m,x,y)); } return f"), this)(), [5, 6, 3, 4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + I32SEL + "function f() {var m=i4(42,45,-42,-47); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return ci4(i4sel(m,x,y)); } return f"), this)(), [5, 6, 3, 4]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + F32SEL + "function f() {var m=i4(0,0,0,0); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return f4(f4sel(m,x,y)); } return f"), this)(), [5, 6, 7, 8]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + CF32 + F32SEL + "function f() {var m=i4(0,0,0,0); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return cf4(f4sel(m,x,y)); } return f"), this)(), [5, 6, 7, 8]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + F32SEL + "function f() {var m=i4(-1,-2,-3,-42); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return f4(f4sel(m,x,y)); } return f"), this)(), [1, 2, 3, 4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + CF32 + F32SEL + "function f() {var m=i4(-1,-2,-3,-42); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return cf4(f4sel(m,x,y)); } return f"), this)(), [1, 2, 3, 4]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + F32SEL + "function f() {var m=i4(1,-1,2,-2); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return f4(f4sel(m,x,y)); } return f"), this)(), [5, 2, 7, 4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + CF32 + F32SEL + "function f() {var m=i4(1,-1,2,-2); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return cf4(f4sel(m,x,y)); } return f"), this)(), [5, 2, 7, 4]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + F32SEL + "function f() {var m=i4(42,45,-42,-47); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return f4(f4sel(m,x,y)); } return f"), this)(), [5, 6, 3, 4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + CF32 + F32SEL + "function f() {var m=i4(42,45,-42,-47); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return cf4(f4sel(m,x,y)); } return f"), this)(), [5, 6, 3, 4]);
// These pass for both select and bitselect // These pass for both select and bitselect
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + I32SEL + "function f() {var m=i4(0,0,0,0); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return i4(i4sel(m,x,y)); } return f"), this)(), [5, 6, 7, 8]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + I32SEL + "function f() {var m=i4(0,0,0,0); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return ci4(i4sel(m,x,y)); } return f"), this)(), [5, 6, 7, 8]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + I32SEL + "function f() {var m=i4(0xffffffff,0xffffffff,0xffffffff,0xffffffff); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return i4(i4sel(m,x,y)); } return f"), this)(), [1, 2, 3, 4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + I32SEL + "function f() {var m=i4(0xffffffff,0xffffffff,0xffffffff,0xffffffff); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return ci4(i4sel(m,x,y)); } return f"), this)(), [1, 2, 3, 4]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + I32SEL + "function f() {var m=i4(0,0xffffffff,0,0xffffffff); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return i4(i4sel(m,x,y)); } return f"), this)(), [5, 2, 7, 4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + I32SEL + "function f() {var m=i4(0,0xffffffff,0,0xffffffff); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return ci4(i4sel(m,x,y)); } return f"), this)(), [5, 2, 7, 4]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + I32SEL + "function f() {var m=i4(0,0,0xffffffff,0xffffffff); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return i4(i4sel(m,x,y)); } return f"), this)(), [5, 6, 3, 4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + I32SEL + "function f() {var m=i4(0,0,0xffffffff,0xffffffff); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return ci4(i4sel(m,x,y)); } return f"), this)(), [5, 6, 3, 4]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + F32SEL + "function f() {var m=i4(0,0,0,0); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return f4(f4sel(m,x,y)); } return f"), this)(), [5, 6, 7, 8]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + CF32 + F32SEL + "function f() {var m=i4(0,0,0,0); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return cf4(f4sel(m,x,y)); } return f"), this)(), [5, 6, 7, 8]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + F32SEL + "function f() {var m=i4(0xffffffff,0xffffffff,0xffffffff,0xffffffff); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return f4(f4sel(m,x,y)); } return f"), this)(), [1, 2, 3, 4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + CF32 + F32SEL + "function f() {var m=i4(0xffffffff,0xffffffff,0xffffffff,0xffffffff); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return cf4(f4sel(m,x,y)); } return f"), this)(), [1, 2, 3, 4]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + F32SEL + "function f() {var m=i4(0,0xffffffff,0,0xffffffff); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return f4(f4sel(m,x,y)); } return f"), this)(), [5, 2, 7, 4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + CF32 + F32SEL + "function f() {var m=i4(0,0xffffffff,0,0xffffffff); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return cf4(f4sel(m,x,y)); } return f"), this)(), [5, 2, 7, 4]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + F32SEL + "function f() {var m=i4(0,0,0xffffffff,0xffffffff); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return f4(f4sel(m,x,y)); } return f"), this)(), [5, 6, 3, 4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + CF32 + F32SEL + "function f() {var m=i4(0,0,0xffffffff,0xffffffff); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return cf4(f4sel(m,x,y)); } return f"), this)(), [5, 6, 3, 4]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + I32BSEL + "function f() {var m=i4(0,0,0,0); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return i4(i4sel(m,x,y)); } return f"), this)(), [5, 6, 7, 8]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + I32BSEL + "function f() {var m=i4(0,0,0,0); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return ci4(i4sel(m,x,y)); } return f"), this)(), [5, 6, 7, 8]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + I32BSEL + "function f() {var m=i4(0xffffffff,0xffffffff,0xffffffff,0xffffffff); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return i4(i4sel(m,x,y)); } return f"), this)(), [1, 2, 3, 4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + I32BSEL + "function f() {var m=i4(0xffffffff,0xffffffff,0xffffffff,0xffffffff); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return ci4(i4sel(m,x,y)); } return f"), this)(), [1, 2, 3, 4]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + I32BSEL + "function f() {var m=i4(0,0xffffffff,0,0xffffffff); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return i4(i4sel(m,x,y)); } return f"), this)(), [5, 2, 7, 4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + I32BSEL + "function f() {var m=i4(0,0xffffffff,0,0xffffffff); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return ci4(i4sel(m,x,y)); } return f"), this)(), [5, 2, 7, 4]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + I32BSEL + "function f() {var m=i4(0,0,0xffffffff,0xffffffff); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return i4(i4sel(m,x,y)); } return f"), this)(), [5, 6, 3, 4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + I32BSEL + "function f() {var m=i4(0,0,0xffffffff,0xffffffff); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return ci4(i4sel(m,x,y)); } return f"), this)(), [5, 6, 3, 4]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + F32BSEL + "function f() {var m=i4(0,0,0,0); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return f4(f4sel(m,x,y)); } return f"), this)(), [5, 6, 7, 8]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + CF32 + F32BSEL + "function f() {var m=i4(0,0,0,0); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return cf4(f4sel(m,x,y)); } return f"), this)(), [5, 6, 7, 8]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + F32BSEL + "function f() {var m=i4(0xffffffff,0xffffffff,0xffffffff,0xffffffff); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return f4(f4sel(m,x,y)); } return f"), this)(), [1, 2, 3, 4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + CF32 + F32BSEL + "function f() {var m=i4(0xffffffff,0xffffffff,0xffffffff,0xffffffff); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return cf4(f4sel(m,x,y)); } return f"), this)(), [1, 2, 3, 4]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + F32BSEL + "function f() {var m=i4(0,0xffffffff,0,0xffffffff); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return f4(f4sel(m,x,y)); } return f"), this)(), [5, 2, 7, 4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + CF32 + F32BSEL + "function f() {var m=i4(0,0xffffffff,0,0xffffffff); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return cf4(f4sel(m,x,y)); } return f"), this)(), [5, 2, 7, 4]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + F32BSEL + "function f() {var m=i4(0,0,0xffffffff,0xffffffff); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return f4(f4sel(m,x,y)); } return f"), this)(), [5, 6, 3, 4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + CF32 + F32BSEL + "function f() {var m=i4(0,0,0xffffffff,0xffffffff); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return cf4(f4sel(m,x,y)); } return f"), this)(), [5, 6, 3, 4]);
// Specific bitselect tests // Specific bitselect tests
var masks = [ var masks = [
@ -921,7 +917,7 @@ var inputs = [
[SIMD.int32x4(-1, 2, INT32_MAX, INT32_MIN), SIMD.int32x4(INT32_MAX, -4, INT32_MIN, 42)] [SIMD.int32x4(-1, 2, INT32_MAX, INT32_MIN), SIMD.int32x4(INT32_MAX, -4, INT32_MIN, 42)]
]; ];
var i32bsel = asmLink(asmCompile('glob', USE_ASM + I32 + I32BSEL + "function f(mask, ifTrue, ifFalse) {mask=i4(mask); ifTrue=i4(ifTrue); ifFalse=i4(ifFalse); return i4(i4sel(mask,ifTrue,ifFalse)); } return f"), this) var i32bsel = asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + I32BSEL + "function f(mask, ifTrue, ifFalse) {mask=ci4(mask); ifTrue=ci4(ifTrue); ifFalse=ci4(ifFalse); return ci4(i4sel(mask,ifTrue,ifFalse)); } return f"), this)
for (var mask of masks) { for (var mask of masks) {
for (var [x, y] of inputs) { for (var [x, y] of inputs) {
@ -935,7 +931,7 @@ inputs = [
[SIMD.float32x4(1.5,2.75,NaN,Infinity), SIMD.float32x4(-NaN,-Infinity,9.75,16.125)] [SIMD.float32x4(1.5,2.75,NaN,Infinity), SIMD.float32x4(-NaN,-Infinity,9.75,16.125)]
]; ];
var f32bsel = asmLink(asmCompile('glob', USE_ASM + I32 + F32 + F32BSEL + "function f(mask, ifTrue, ifFalse) {mask=i4(mask); ifTrue=f4(ifTrue); ifFalse=f4(ifFalse); return f4(f4sel(mask,ifTrue,ifFalse)); } return f"), this) var f32bsel = asmLink(asmCompile('glob', USE_ASM + I32 + F32 + CI32 + CF32 + F32BSEL + "function f(mask, ifTrue, ifFalse) {mask=ci4(mask); ifTrue=cf4(ifTrue); ifFalse=cf4(ifFalse); return cf4(f4sel(mask,ifTrue,ifFalse)); } return f"), this)
for (var mask of masks) for (var mask of masks)
for (var [x, y] of inputs) for (var [x, y] of inputs)
@ -953,28 +949,28 @@ assertAsmTypeFail('glob', USE_ASM + I32 + I32SPLAT + "function f() {var m=i4(1,2
assertAsmTypeFail('glob', USE_ASM + I32 + I32SPLAT + "function f() {var m=i4(1,2,3,4); m=splat(1.0);} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + I32SPLAT + "function f() {var m=i4(1,2,3,4); m=splat(1.0);} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + I32SPLAT + FROUND + "function f() {var m=i4(1,2,3,4); m=splat(f32(1.0));} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + I32SPLAT + FROUND + "function f() {var m=i4(1,2,3,4); m=splat(f32(1.0));} return f");
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + I32SPLAT + 'function f(){return i4(splat(42));} return f'), this)(), [42, 42, 42, 42]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + I32SPLAT + 'function f(){return ci4(splat(42));} return f'), this)(), [42, 42, 42, 42]);
const l33t = Math.fround(13.37); const l33t = Math.fround(13.37);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + F32SPLAT + FROUND + 'function f(){return f4(splat(f32(1)));} return f'), this)(), [1, 1, 1, 1]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + CF32 + F32SPLAT + FROUND + 'function f(){return cf4(splat(f32(1)));} return f'), this)(), [1, 1, 1, 1]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + F32SPLAT + FROUND + 'function f(){return f4(splat(1.0));} return f'), this)(), [1, 1, 1, 1]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + CF32 + F32SPLAT + FROUND + 'function f(){return cf4(splat(1.0));} return f'), this)(), [1, 1, 1, 1]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + F32SPLAT + FROUND + 'function f(){return f4(splat(f32(1 >>> 0)));} return f'), this)(), [1, 1, 1, 1]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + CF32 + F32SPLAT + FROUND + 'function f(){return cf4(splat(f32(1 >>> 0)));} return f'), this)(), [1, 1, 1, 1]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + F32SPLAT + FROUND + 'function f(){return f4(splat(f32(13.37)));} return f'), this)(), [l33t, l33t, l33t, l33t]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + CF32 + F32SPLAT + FROUND + 'function f(){return cf4(splat(f32(13.37)));} return f'), this)(), [l33t, l33t, l33t, l33t]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + F32SPLAT + FROUND + 'function f(){return f4(splat(13.37));} return f'), this)(), [l33t, l33t, l33t, l33t]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + CF32 + F32SPLAT + FROUND + 'function f(){return cf4(splat(13.37));} return f'), this)(), [l33t, l33t, l33t, l33t]);
var i32view = new Int32Array(heap); var i32view = new Int32Array(heap);
var f32view = new Float32Array(heap); var f32view = new Float32Array(heap);
i32view[0] = 42; i32view[0] = 42;
assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + I32 + I32SPLAT + 'var i32=new glob.Int32Array(heap); function f(){return i4(splat(i32[0]));} return f'), this, {}, heap)(), [42, 42, 42, 42]); assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + I32 + CI32 + I32SPLAT + 'var i32=new glob.Int32Array(heap); function f(){return ci4(splat(i32[0]));} return f'), this, {}, heap)(), [42, 42, 42, 42]);
f32view[0] = 42; f32view[0] = 42;
assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + F32 + F32SPLAT + 'var f32=new glob.Float32Array(heap); function f(){return f4(splat(f32[0]));} return f'), this, {}, heap)(), [42, 42, 42, 42]); assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + F32 + CF32 + F32SPLAT + 'var f32=new glob.Float32Array(heap); function f(){return cf4(splat(f32[0]));} return f'), this, {}, heap)(), [42, 42, 42, 42]);
assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + F32 + F32SPLAT + FROUND + 'function f(){return f4(splat(f32(1) + f32(2)));} return f'), this, {}, heap)(), [3, 3, 3, 3]); assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + F32 + CF32 + F32SPLAT + FROUND + 'function f(){return cf4(splat(f32(1) + f32(2)));} return f'), this, {}, heap)(), [3, 3, 3, 3]);
// Dead code // Dead code
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + 'function f(){var x=i4(1,2,3,4); return i4(x); x=i4(5,6,7,8); return i4(x);} return f'), this)(), [1, 2, 3, 4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + 'function f(){var x=i4(1,2,3,4); return ci4(x); x=i4(5,6,7,8); return ci4(x);} return f'), this)(), [1, 2, 3, 4]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + 'function f(){var x=i4(1,2,3,4); var c=0; return i4(x); c=x.x|0; return i4(x);} return f'), this)(), [1, 2, 3, 4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + 'function f(){var x=i4(1,2,3,4); var c=0; return ci4(x); c=x.x|0; return ci4(x);} return f'), this)(), [1, 2, 3, 4]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + I32A + 'function f(){var x=i4(1,2,3,4); var c=0; return i4(x); x=i4a(x,x); return i4(x);} return f'), this)(), [1, 2, 3, 4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + I32A + 'function f(){var x=i4(1,2,3,4); var c=0; return ci4(x); x=i4a(x,x); return ci4(x);} return f'), this)(), [1, 2, 3, 4]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + I32S + 'function f(){var x=i4(1,2,3,4); var c=0; return i4(x); x=i4s(x,x); return i4(x);} return f'), this)(), [1, 2, 3, 4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + I32S + 'function f(){var x=i4(1,2,3,4); var c=0; return ci4(x); x=i4s(x,x); return ci4(x);} return f'), this)(), [1, 2, 3, 4]);
// Swizzle // Swizzle
assertAsmTypeFail('glob', USE_ASM + I32 + "var swizzle=i4.swizzle; function f() {var x=i4(1,2,3,4); x=swizzle(x, -1, 0, 0, 0);} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + "var swizzle=i4.swizzle; function f() {var x=i4(1,2,3,4); x=swizzle(x, -1, 0, 0, 0);} return f");
@ -1093,7 +1089,7 @@ assertAsmTypeFail('glob', 'ffi', USE_ASM + F32 + "var func=ffi.func; function f(
// 3.3 Internal calls // 3.3 Internal calls
// asm.js -> asm.js // asm.js -> asm.js
// Retrieving values from asm.js // Retrieving values from asm.js
var code = USE_ASM + I32 + I32A + ` var code = USE_ASM + I32 + CI32 + I32A + `
var check = ffi.check; var check = ffi.check;
function g() { function g() {
@ -1105,15 +1101,15 @@ var code = USE_ASM + I32 + I32A + `
y = i4a(z, y); y = i4a(z, y);
y = i4a(w, y); y = i4a(w, y);
check(y.x | 0, y.y | 0, y.z | 0, y.w | 0); check(y.x | 0, y.y | 0, y.z | 0, y.w | 0);
return i4(y); return ci4(y);
} }
function f(x) { function f(x) {
x = i4(x); x = ci4(x);
var y = i4(0,0,0,0); var y = i4(0,0,0,0);
y = i4(g()); y = ci4(g());
check(y.x | 0, y.y | 0, y.z | 0, y.w | 0); check(y.x | 0, y.y | 0, y.z | 0, y.w | 0);
return i4(x); return ci4(x);
} }
return f; return f;
`; `;
@ -1130,7 +1126,7 @@ assertEqX4(asmLink(asmCompile('glob', 'ffi', code), this, ffi)(v4), [1,2,3,4]);
// Passing arguments from asm.js to asm.js // Passing arguments from asm.js to asm.js
// TODO make this code look better with templatized strings // TODO make this code look better with templatized strings
var code = USE_ASM + I32 + I32A + ` var code = USE_ASM + I32 + CI32 + I32A + `
var assertEq = ffi.assertEq; var assertEq = ffi.assertEq;
function internal([args]) { function internal([args]) {
@ -1156,7 +1152,7 @@ for (var i = 1; i < 10; ++i) {
var j = i; var j = i;
args += ((i > 1) ? ', ':'') + 'x' + i; args += ((i > 1) ? ', ':'') + 'x' + i;
decls += 'var x' + i + ' = i4(' + j++ + ', ' + j++ + ', ' + j++ + ', ' + j++ + ');\n'; decls += 'var x' + i + ' = i4(' + j++ + ', ' + j++ + ', ' + j++ + ', ' + j++ + ');\n';
coerc += 'x' + i + ' = i4(x' + i + ');\n'; coerc += 'x' + i + ' = ci4(x' + i + ');\n';
last = 'x' + i; last = 'x' + i;
var c = code.replace(/\[args\]/g, args) var c = code.replace(/\[args\]/g, args)
.replace(/\[last\]/g, last) .replace(/\[last\]/g, last)
@ -1232,6 +1228,7 @@ asmLink(asmCompile('glob', 'ffi', code), this, assertEqFFI)();
var code = ` var code = `
"use asm"; "use asm";
var i4 = glob.SIMD.int32x4; var i4 = glob.SIMD.int32x4;
var ci4 = i4.check;
function h( function h(
// In registers: // In registers:
gpr1, gpr2, gpr3, gpr4, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, xmm8, gpr1, gpr2, gpr3, gpr4, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, xmm8,
@ -1254,13 +1251,13 @@ asmLink(asmCompile('glob', 'ffi', code), this, assertEqFFI)();
xmm8=+xmm8; xmm8=+xmm8;
sint1=sint1|0; sint1=sint1|0;
ssimd1=i4(ssimd1); ssimd1=ci4(ssimd1);
sdouble1=+sdouble1; sdouble1=+sdouble1;
ssimd2=i4(ssimd2); ssimd2=ci4(ssimd2);
sint2=sint2|0; sint2=sint2|0;
sint3=sint3|0; sint3=sint3|0;
sint4=sint4|0; sint4=sint4|0;
ssimd3=i4(ssimd3); ssimd3=ci4(ssimd3);
sdouble2=+sdouble2; sdouble2=+sdouble2;
return (ssimd1.x|0) + (ssimd2.y|0) + (ssimd3.z|0) + sint2 + gpr3 | 0; return (ssimd1.x|0) + (ssimd2.y|0) + (ssimd3.z|0) + sint2 + gpr3 | 0;
@ -1290,6 +1287,7 @@ asmLink(asmCompile('glob', 'ffi', code), this, assertEqFFI)();
"use asm"; "use asm";
var i4 = glob.SIMD.int32x4; var i4 = glob.SIMD.int32x4;
var i4a = i4.add; var i4a = i4.add;
var ci4 = i4.check;
function _() { function _() {
var i = 0; var i = 0;
var n = i4(0,0,0,0); var n = i4(0,0,0,0);
@ -1297,7 +1295,7 @@ asmLink(asmCompile('glob', 'ffi', code), this, assertEqFFI)();
for (; (i>>>0) < ` + iters + `; i=(i+1)>>>0) { for (; (i>>>0) < ` + iters + `; i=(i+1)>>>0) {
n = i4a(n, one); n = i4a(n, one);
} }
return i4(n); return ci4(n);
} }
return _;`; return _;`;
// This test relies on the fact that setting the timeout will call the // This test relies on the fact that setting the timeout will call the

View File

@ -665,17 +665,26 @@ CodeGenerator::getJumpLabelForBranch(MBasicBlock *block)
void void
CodeGenerator::visitTestOAndBranch(LTestOAndBranch *lir) CodeGenerator::visitTestOAndBranch(LTestOAndBranch *lir)
{ {
MOZ_ASSERT(lir->mir()->operandMightEmulateUndefined(), MIRType inputType = lir->mir()->input()->type();
"Objects which can't emulate undefined should have been constant-folded"); MOZ_ASSERT(inputType == MIRType_ObjectOrNull || lir->mir()->operandMightEmulateUndefined(),
"If the object couldn't emulate undefined, this should have been folded.");
OutOfLineTestObject *ool = new(alloc()) OutOfLineTestObject();
addOutOfLineCode(ool, lir->mir());
Label *truthy = getJumpLabelForBranch(lir->ifTruthy()); Label *truthy = getJumpLabelForBranch(lir->ifTruthy());
Label *falsy = getJumpLabelForBranch(lir->ifFalsy()); Label *falsy = getJumpLabelForBranch(lir->ifFalsy());
Register input = ToRegister(lir->input());
testObjectEmulatesUndefined(ToRegister(lir->input()), falsy, truthy, if (lir->mir()->operandMightEmulateUndefined()) {
ToRegister(lir->temp()), ool); if (inputType == MIRType_ObjectOrNull)
masm.branchTestPtr(Assembler::Zero, input, input, falsy);
OutOfLineTestObject *ool = new(alloc()) OutOfLineTestObject();
addOutOfLineCode(ool, lir->mir());
testObjectEmulatesUndefined(input, falsy, truthy, ToRegister(lir->temp()), ool);
} else {
MOZ_ASSERT(inputType == MIRType_ObjectOrNull);
testZeroEmitBranch(Assembler::NotEqual, input, lir->ifTruthy(), lir->ifFalsy());
}
} }
void void
@ -2553,14 +2562,23 @@ CodeGenerator::visitTypeBarrierV(LTypeBarrierV *lir)
void void
CodeGenerator::visitTypeBarrierO(LTypeBarrierO *lir) CodeGenerator::visitTypeBarrierO(LTypeBarrierO *lir)
{ {
MOZ_ASSERT(lir->mir()->barrierKind() != BarrierKind::TypeTagOnly);
Register obj = ToRegister(lir->object()); Register obj = ToRegister(lir->object());
Register scratch = ToTempRegisterOrInvalid(lir->temp()); Register scratch = ToTempRegisterOrInvalid(lir->temp());
Label miss, ok;
if (lir->mir()->type() == MIRType_ObjectOrNull) {
Label *nullTarget = lir->mir()->resultTypeSet()->mightBeMIRType(MIRType_Null) ? &ok : &miss;
masm.branchTestPtr(Assembler::Zero, obj, obj, nullTarget);
} else {
MOZ_ASSERT(lir->mir()->type() == MIRType_Object);
MOZ_ASSERT(lir->mir()->barrierKind() != BarrierKind::TypeTagOnly);
}
if (lir->mir()->barrierKind() != BarrierKind::TypeTagOnly)
masm.guardObjectType(obj, lir->mir()->resultTypeSet(), scratch, &miss);
Label miss;
masm.guardObjectType(obj, lir->mir()->resultTypeSet(), scratch, &miss);
bailoutFrom(&miss, lir->snapshot()); bailoutFrom(&miss, lir->snapshot());
masm.bind(&ok);
} }
void void

View File

@ -2519,11 +2519,18 @@ TryEliminateTypeBarrier(MTypeBarrier *barrier, bool *eliminated)
} }
static bool static bool
TryOptimizeLoadUnboxedObjectOrNull(MLoadUnboxedObjectOrNull *def, MDefinitionVector *peliminateList) TryOptimizeLoadObjectOrNull(MDefinition *def, MDefinitionVector *peliminateList)
{ {
if (def->type() != MIRType_Value) if (def->type() != MIRType_Value)
return true; return true;
// Check if this definition can only produce object or null values.
TemporaryTypeSet *types = def->resultTypeSet();
if (!types)
return true;
if (types->baseFlags() & ~(TYPE_FLAG_NULL | TYPE_FLAG_ANYOBJECT))
return true;
MDefinitionVector eliminateList(def->block()->graph().alloc()); MDefinitionVector eliminateList(def->block()->graph().alloc());
for (MUseDefIterator iter(def); iter; ++iter) { for (MUseDefIterator iter(def); iter; ++iter) {
@ -2533,6 +2540,8 @@ TryOptimizeLoadUnboxedObjectOrNull(MLoadUnboxedObjectOrNull *def, MDefinitionVec
if (ndef->toCompare()->compareType() != MCompare::Compare_Null) if (ndef->toCompare()->compareType() != MCompare::Compare_Null)
return true; return true;
break; break;
case MDefinition::Op_Test:
break;
case MDefinition::Op_PostWriteBarrier: case MDefinition::Op_PostWriteBarrier:
break; break;
case MDefinition::Op_StoreFixedSlot: case MDefinition::Op_StoreFixedSlot:
@ -2546,13 +2555,42 @@ TryOptimizeLoadUnboxedObjectOrNull(MLoadUnboxedObjectOrNull *def, MDefinitionVec
case MDefinition::Op_Unbox: case MDefinition::Op_Unbox:
MOZ_ASSERT(ndef->type() == MIRType_Object); MOZ_ASSERT(ndef->type() == MIRType_Object);
break; break;
case MDefinition::Op_TypeBarrier:
// For now, only handle type barriers which are not consumed
// anywhere and only test that the value is null.
if (ndef->hasUses() || ndef->resultTypeSet()->getKnownMIRType() != MIRType_Null)
return true;
break;
default: default:
return true; return true;
} }
} }
// On punboxing systems we are better off leaving the value boxed if it
// is only stored back to the heap.
#ifdef JS_PUNBOX64
bool foundUse = false;
for (MUseDefIterator iter(def); iter; ++iter) {
MDefinition *ndef = iter.def();
if (!ndef->isStoreFixedSlot() && !ndef->isStoreSlot()) {
foundUse = true;
break;
}
}
if (!foundUse)
return true;
#endif // JS_PUNBOX64
def->setResultType(MIRType_ObjectOrNull); def->setResultType(MIRType_ObjectOrNull);
// Fixup the result type of MTypeBarrier uses.
for (MUseDefIterator iter(def); iter; ++iter) {
MDefinition *ndef = iter.def();
if (ndef->isTypeBarrier())
ndef->setResultType(MIRType_ObjectOrNull);
}
// Eliminate MToObjectOrNull instruction uses.
for (size_t i = 0; i < eliminateList.length(); i++) { for (size_t i = 0; i < eliminateList.length(); i++) {
MDefinition *ndef = eliminateList[i]; MDefinition *ndef = eliminateList[i];
ndef->replaceAllUsesWith(def); ndef->replaceAllUsesWith(def);
@ -2627,21 +2665,28 @@ jit::EliminateRedundantChecks(MIRGraph &graph)
bool eliminated = false; bool eliminated = false;
if (def->isBoundsCheck()) { switch (def->op()) {
case MDefinition::Op_BoundsCheck:
if (!TryEliminateBoundsCheck(checks, index, def->toBoundsCheck(), &eliminated)) if (!TryEliminateBoundsCheck(checks, index, def->toBoundsCheck(), &eliminated))
return false; return false;
} else if (def->isTypeBarrier()) { break;
case MDefinition::Op_TypeBarrier:
if (!TryEliminateTypeBarrier(def->toTypeBarrier(), &eliminated)) if (!TryEliminateTypeBarrier(def->toTypeBarrier(), &eliminated))
return false; return false;
} else if (def->isLoadUnboxedObjectOrNull()) { break;
if (!TryOptimizeLoadUnboxedObjectOrNull(def->toLoadUnboxedObjectOrNull(), &eliminateList)) case MDefinition::Op_LoadFixedSlot:
case MDefinition::Op_LoadSlot:
case MDefinition::Op_LoadUnboxedObjectOrNull:
if (!TryOptimizeLoadObjectOrNull(def, &eliminateList))
return false; return false;
} else { break;
default:
// Now that code motion passes have finished, replace // Now that code motion passes have finished, replace
// instructions which pass through one of their operands // instructions which pass through one of their operands
// (and perform additional checks) with that operand. // (and perform additional checks) with that operand.
if (MDefinition *passthrough = PassthroughOperand(def)) if (MDefinition *passthrough = PassthroughOperand(def))
def->replaceAllUsesWith(passthrough); def->replaceAllUsesWith(passthrough);
break;
} }
if (eliminated) if (eliminated)

View File

@ -658,6 +658,12 @@ LIRGenerator::visitTest(MTest *test)
return; return;
} }
if (opd->type() == MIRType_ObjectOrNull) {
LDefinition temp0 = test->operandMightEmulateUndefined() ? temp() : LDefinition::BogusTemp();
add(new(alloc()) LTestOAndBranch(useRegister(opd), ifTrue, ifFalse, temp0), test);
return;
}
// Objects are truthy, except if it might emulate undefined. // Objects are truthy, except if it might emulate undefined.
if (opd->type() == MIRType_Object) { if (opd->type() == MIRType_Object) {
if (test->operandMightEmulateUndefined()) if (test->operandMightEmulateUndefined())
@ -2327,10 +2333,18 @@ LIRGenerator::visitTypeBarrier(MTypeBarrier *ins)
return; return;
} }
// Handle typebarrier with specific ObjectGroup/SingleObjects. // The payload needs to be tested if it either might be null or might have
// an object that should be excluded from the barrier.
bool needsObjectBarrier = false;
if (inputType == MIRType_ObjectOrNull)
needsObjectBarrier = true;
if (inputType == MIRType_Object && !types->hasType(TypeSet::AnyObjectType()) && if (inputType == MIRType_Object && !types->hasType(TypeSet::AnyObjectType()) &&
ins->barrierKind() != BarrierKind::TypeTagOnly) ins->barrierKind() != BarrierKind::TypeTagOnly)
{ {
needsObjectBarrier = true;
}
if (needsObjectBarrier) {
LDefinition tmp = needTemp ? temp() : LDefinition::BogusTemp(); LDefinition tmp = needTemp ? temp() : LDefinition::BogusTemp();
LTypeBarrierO *barrier = new(alloc()) LTypeBarrierO(useRegister(ins->getOperand(0)), tmp); LTypeBarrierO *barrier = new(alloc()) LTypeBarrierO(useRegister(ins->getOperand(0)), tmp);
assignSnapshot(barrier, Bailout_TypeBarrierO); assignSnapshot(barrier, Bailout_TypeBarrierO);

View File

@ -3348,7 +3348,7 @@ NewArray(ExclusiveContext *cxArg, uint32_t length,
cache.lookupGlobal(&ArrayObject::class_, cx->global(), allocKind, &entry)) cache.lookupGlobal(&ArrayObject::class_, cx->global(), allocKind, &entry))
{ {
gc::InitialHeap heap = GetInitialHeap(newKind, &ArrayObject::class_); gc::InitialHeap heap = GetInitialHeap(newKind, &ArrayObject::class_);
JSObject *obj = cache.newObjectFromHit<NoGC>(cx, entry, heap); JSObject *obj = cache.newObjectFromHit(cx, entry, heap);
if (obj) { if (obj) {
/* Fixup the elements pointer and length, which may be incorrect. */ /* Fixup the elements pointer and length, which may be incorrect. */
ArrayObject *arr = &obj->as<ArrayObject>(); ArrayObject *arr = &obj->as<ArrayObject>();
@ -3360,9 +3360,6 @@ NewArray(ExclusiveContext *cxArg, uint32_t length,
return nullptr; return nullptr;
} }
return arr; return arr;
} else {
obj = cache.newObjectFromHit<CanGC>(cx, entry, heap);
MOZ_ASSERT(!obj);
} }
} else { } else {
gcNumber = rt->gc.gcNumber(); gcNumber = rt->gc.gcNumber();

View File

@ -1251,13 +1251,9 @@ js::NewObjectWithGivenTaggedProto(ExclusiveContext *cxArg, const Class *clasp,
!proto.toObject()->is<GlobalObject>()) !proto.toObject()->is<GlobalObject>())
{ {
if (cache.lookupProto(clasp, proto.toObject(), allocKind, &entry)) { if (cache.lookupProto(clasp, proto.toObject(), allocKind, &entry)) {
JSObject *obj = cache.newObjectFromHit<NoGC>(cx, entry, GetInitialHeap(newKind, clasp)); JSObject *obj = cache.newObjectFromHit(cx, entry, GetInitialHeap(newKind, clasp));
if (obj) { if (obj)
return obj; return obj;
} else {
obj = cache.newObjectFromHit<CanGC>(cx, entry, GetInitialHeap(newKind, clasp));
MOZ_ASSERT(!obj);
}
} else { } else {
gcNumber = rt->gc.gcNumber(); gcNumber = rt->gc.gcNumber();
} }
@ -1424,13 +1420,9 @@ js::NewObjectWithClassProtoCommon(ExclusiveContext *cxArg, const Class *clasp,
!cx->compartment()->hasObjectMetadataCallback()) !cx->compartment()->hasObjectMetadataCallback())
{ {
if (cache.lookupGlobal(clasp, &parent->as<GlobalObject>(), allocKind, &entry)) { if (cache.lookupGlobal(clasp, &parent->as<GlobalObject>(), allocKind, &entry)) {
JSObject *obj = cache.newObjectFromHit<NoGC>(cx, entry, GetInitialHeap(newKind, clasp)); JSObject *obj = cache.newObjectFromHit(cx, entry, GetInitialHeap(newKind, clasp));
if (obj) { if (obj)
return obj; return obj;
} else {
obj = cache.newObjectFromHit<CanGC>(cx, entry, GetInitialHeap(newKind, clasp));
MOZ_ASSERT(!obj);
}
} else { } else {
gcNumber = rt->gc.gcNumber(); gcNumber = rt->gc.gcNumber();
} }
@ -1478,6 +1470,7 @@ js::NewObjectWithGroupCommon(JSContext *cx, HandleObjectGroup group, HandleObjec
NewObjectCache &cache = cx->runtime()->newObjectCache; NewObjectCache &cache = cx->runtime()->newObjectCache;
NewObjectCache::EntryIndex entry = -1; NewObjectCache::EntryIndex entry = -1;
uint64_t gcNumber = 0;
if (group->proto().isObject() && if (group->proto().isObject() &&
parent == group->proto().toObject()->getParent() && parent == group->proto().toObject()->getParent() &&
newKind == GenericObject && newKind == GenericObject &&
@ -1486,13 +1479,12 @@ js::NewObjectWithGroupCommon(JSContext *cx, HandleObjectGroup group, HandleObjec
!cx->compartment()->hasObjectMetadataCallback()) !cx->compartment()->hasObjectMetadataCallback())
{ {
if (cache.lookupGroup(group, allocKind, &entry)) { if (cache.lookupGroup(group, allocKind, &entry)) {
JSObject *obj = cache.newObjectFromHit<NoGC>(cx, entry, GetInitialHeap(newKind, group->clasp())); JSObject *obj = cache.newObjectFromHit(cx, entry,
if (obj) { GetInitialHeap(newKind, group->clasp()));
if (obj)
return obj; return obj;
} else { } else {
obj = cache.newObjectFromHit<CanGC>(cx, entry, GetInitialHeap(newKind, group->clasp())); gcNumber = cx->runtime()->gc.gcNumber();
MOZ_ASSERT(!obj);
}
} }
} }
@ -1500,8 +1492,11 @@ js::NewObjectWithGroupCommon(JSContext *cx, HandleObjectGroup group, HandleObjec
if (!obj) if (!obj)
return nullptr; return nullptr;
if (entry != -1 && !obj->as<NativeObject>().hasDynamicSlots()) if (entry != -1 && !obj->as<NativeObject>().hasDynamicSlots() &&
cx->runtime()->gc.gcNumber() == gcNumber)
{
cache.fillGroup(entry, group, allocKind, &obj->as<NativeObject>()); cache.fillGroup(entry, group, allocKind, &obj->as<NativeObject>());
}
return obj; return obj;
} }

View File

@ -9,85 +9,31 @@ function TestInt32x4Ctor() {
assertEqX4(int32x4(1, 2, 3, 4), [1,2,3,4]); assertEqX4(int32x4(1, 2, 3, 4), [1,2,3,4]);
assertEqX4(int32x4(1, 2, 3), [1,2,3,0]); assertEqX4(int32x4(1, 2, 3), [1,2,3,0]);
assertEqX4(int32x4(1, 2), [1,2,0,0]); assertEqX4(int32x4(1, 2), [1,2,0,0]);
// The 1-argument form is reserved for coercions. assertEqX4(int32x4(1), [1,0,0,0]);
assertEqX4(int32x4(), [0,0,0,0]); assertEqX4(int32x4(), [0,0,0,0]);
assertEqX4(int32x4(1, 2, 3, 4, 5), [1,2,3,4]); assertEqX4(int32x4(1, 2, 3, 4, 5), [1,2,3,4]);
assertEqX4(int32x4(1, 2, 3, 4, 5, 6), [1,2,3,4]); assertEqX4(int32x4(1, 2, 3, 4, 5, 6), [1,2,3,4]);
// Constructors used as coercion.
var x = int32x4(1, 2, 3, 4);
var y = int32x4(x);
assertEq(x, y);
assertEq(y.x, x.x);
assertEq(y.x, 1);
assertEq(y.y, x.y);
assertEq(y.y, 2);
assertEq(y.z, x.z);
assertEq(y.z, 3);
assertEq(y.w, x.w);
assertEq(y.w, 4);
assertThrowsInstanceOf(() => int32x4(3), TypeError);
assertThrowsInstanceOf(() => int32x4(float32x4(1,2,3,4)), TypeError);
assertThrowsInstanceOf(() => int32x4(float64x2(1,2)), TypeError);
assertThrowsInstanceOf(() => int32x4('pony x 4'), TypeError);
} }
function TestFloat32x4Ctor() { function TestFloat32x4Ctor() {
assertEqX4(float32x4(1, 2, 3, 4), [1,2,3,4]); assertEqX4(float32x4(1, 2, 3, 4), [1,2,3,4]);
assertEqX4(float32x4(1, 2, 3), [1,2,3,NaN]); assertEqX4(float32x4(1, 2, 3), [1,2,3,NaN]);
assertEqX4(float32x4(1, 2), [1,2,NaN,NaN]); assertEqX4(float32x4(1, 2), [1,2,NaN,NaN]);
// The 1-argument form is reserved for coercions. assertEqX4(float32x4(1), [1,NaN,NaN,NaN]);
assertEqX4(float32x4(), [NaN,NaN,NaN,NaN]); assertEqX4(float32x4(), [NaN,NaN,NaN,NaN]);
assertEqX4(float32x4(1, 2, 3, 4, 5), [1,2,3,4]); assertEqX4(float32x4(1, 2, 3, 4, 5), [1,2,3,4]);
assertEqX4(float32x4(1, 2, 3, 4, 5, 6), [1,2,3,4]); assertEqX4(float32x4(1, 2, 3, 4, 5, 6), [1,2,3,4]);
var x = float32x4(NaN, 13.37, -Infinity, 4);
var y = float32x4(x);
assertEq(x, y);
assertEq(y.x, x.x);
assertEq(y.x, Math.fround(NaN));
assertEq(y.y, x.y);
assertEq(y.y, Math.fround(13.37));
assertEq(y.z, x.z);
assertEq(y.z, Math.fround(-Infinity));
assertEq(y.w, x.w);
assertEq(y.w, Math.fround(4));
assertThrowsInstanceOf(() => float32x4(3), TypeError);
assertThrowsInstanceOf(() => float32x4(int32x4(1,2,3,4)), TypeError);
assertThrowsInstanceOf(() => float32x4(float64x2(1,2)), TypeError);
assertThrowsInstanceOf(() => float32x4('pony x 4'), TypeError);
} }
function TestFloat64x2Ctor() { function TestFloat64x2Ctor() {
assertEqX2(float64x2(1, 2), [1,2]); assertEqX2(float64x2(1, 2), [1,2]);
// The 1-argument form is reserved for coercions. assertEqX2(float64x2(1), [1,NaN]);
assertEqX2(float64x2(), [NaN,NaN]); assertEqX2(float64x2(), [NaN,NaN]);
assertEqX2(float64x2(1, 2, 3), [1,2]); assertEqX2(float64x2(1, 2, 3), [1,2]);
assertEqX2(float64x2(1, 2, 3, 4), [1,2]); assertEqX2(float64x2(1, 2, 3, 4), [1,2]);
assertEqX2(float64x2(1, 2, 3, 4, 5), [1,2]); assertEqX2(float64x2(1, 2, 3, 4, 5), [1,2]);
assertEqX2(float64x2(1, 2, 3, 4, 5), [1,2]); assertEqX2(float64x2(1, 2, 3, 4, 5), [1,2]);
assertEqX2(float64x2(1, 2, 3, 4, 5, 6), [1,2]); assertEqX2(float64x2(1, 2, 3, 4, 5, 6), [1,2]);
var x = float64x2(NaN, 13.37);
var y = float64x2(x);
assertEq(x, y);
assertEq(y.x, x.x);
assertEq(y.x, NaN);
assertEq(y.y, x.y);
assertEq(y.y, 13.37);
assertThrowsInstanceOf(() => float64x2(3), TypeError);
assertThrowsInstanceOf(() => float64x2(int32x4(1,2,3,4)), TypeError);
assertThrowsInstanceOf(() => float64x2(float32x4(1,2,3,4)), TypeError);
assertThrowsInstanceOf(() => float64x2('pony x 4'), TypeError);
} }
function test() { function test() {

View File

@ -1,91 +0,0 @@
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
var BUGNUMBER = 946042;
var float32x4 = SIMD.float32x4;
var int32x4 = SIMD.int32x4;
var summary = 'float32x4 with';
function test() {
print(BUGNUMBER + ": " + summary);
var a = float32x4(1, 2, 3, 4);
var x = SIMD.float32x4.withX(a, 5);
var y = SIMD.float32x4.withY(a, 5);
var z = SIMD.float32x4.withZ(a, 5);
var w = SIMD.float32x4.withW(a, 5);
assertEq(x.x, 5);
assertEq(x.y, 2);
assertEq(x.z, 3);
assertEq(x.w, 4);
assertEq(y.x, 1);
assertEq(y.y, 5);
assertEq(y.z, 3);
assertEq(y.w, 4);
assertEq(z.x, 1);
assertEq(z.y, 2);
assertEq(z.z, 5);
assertEq(z.w, 4);
assertEq(w.x, 1);
assertEq(w.y, 2);
assertEq(w.z, 3);
assertEq(w.w, 5);
var b = float32x4(1.87, 2.08, 3.84, 4.17);
var x1 = SIMD.float32x4.withX(b, 5.38);
var y1 = SIMD.float32x4.withY(b, 5.19);
var z1 = SIMD.float32x4.withZ(b, 5.11);
var w1 = SIMD.float32x4.withW(b, 5.07);
assertEq(x1.x, Math.fround(5.38));
assertEq(x1.y, Math.fround(2.08));
assertEq(x1.z, Math.fround(3.84));
assertEq(x1.w, Math.fround(4.17));
assertEq(y1.x, Math.fround(1.87));
assertEq(y1.y, Math.fround(5.19));
assertEq(y1.z, Math.fround(3.84));
assertEq(y1.w, Math.fround(4.17));
assertEq(z1.x, Math.fround(1.87));
assertEq(z1.y, Math.fround(2.08));
assertEq(z1.z, Math.fround(5.11));
assertEq(z1.w, Math.fround(4.17));
assertEq(w1.x, Math.fround(1.87));
assertEq(w1.y, Math.fround(2.08));
assertEq(w1.z, Math.fround(3.84));
assertEq(w1.w, Math.fround(5.07));
var c = float32x4(NaN, -0, Infinity, -Infinity);
var x2 = SIMD.float32x4.withX(c, 0);
var y2 = SIMD.float32x4.withY(c, 0);
var z2 = SIMD.float32x4.withZ(c, 0);
var w2 = SIMD.float32x4.withW(c, 0);
assertEq(x2.x, 0);
assertEq(x2.y, -0);
assertEq(x2.z, Infinity);
assertEq(x2.w, -Infinity);
assertEq(y2.x, NaN);
assertEq(y2.y, 0);
assertEq(y2.z, Infinity);
assertEq(y2.w, -Infinity);
assertEq(z2.x, NaN);
assertEq(z2.y, -0);
assertEq(z2.z, 0);
assertEq(z2.w, -Infinity);
assertEq(w2.x, NaN);
assertEq(w2.y, -0);
assertEq(w2.z, Infinity);
assertEq(w2.w, 0);
if (typeof reportCompare === "function")
reportCompare(true, true);
}
test();

View File

@ -1,36 +0,0 @@
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
var BUGNUMBER = 1031203;
var float64x2 = SIMD.float64x2;
var summary = 'float64x2 with';
/*
* Any copyright is dedicated to the Public Domain.
* https://creativecommons.org/publicdomain/zero/1.0/
*/
function test() {
print(BUGNUMBER + ": " + summary);
var a = float64x2(1, 2);
var x = float64x2.withX(a, 5);
var y = float64x2.withY(a, 5);
assertEq(x.x, 5);
assertEq(x.y, 2);
assertEq(y.x, 1);
assertEq(y.y, 5);
var b = float64x2(NaN, -0);
var x1 = float64x2.withX(b, Infinity);
var y1 = float64x2.withY(b, -Infinity);
assertEq(x1.x, Infinity);
assertEq(x1.y, -0);
assertEq(y1.x, NaN);
assertEq(y1.y, -Infinity);
if (typeof reportCompare === "function")
reportCompare(true, true);
}
test();

View File

@ -1,28 +0,0 @@
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
var BUGNUMBER = 946042;
var float32x4 = SIMD.float32x4;
var int32x4 = SIMD.int32x4;
var summary = 'int32x4 with';
function test() {
print(BUGNUMBER + ": " + summary);
var INT32_MAX = Math.pow(2, 31) - 1;
var a = int32x4(1, 2, 3, 4);
var x = SIMD.int32x4.withX(a, 5);
var y = SIMD.int32x4.withY(a, 5);
var z = SIMD.int32x4.withZ(a, 5);
var w = SIMD.int32x4.withW(a, INT32_MAX + 1);
assertEq(x.x, 5);
assertEq(y.y, 5);
assertEq(z.z, 5);
assertEq(w.w, (INT32_MAX + 1) | 0);
if (typeof reportCompare === "function")
reportCompare(true, true);
}
test();

View File

@ -18,6 +18,11 @@ function ursh(a, b) {
} }
function test() { function test() {
function TestError() {};
var good = {valueOf: () => 21};
var bad = {valueOf: () => {throw new TestError(); }};
for (var v of [ for (var v of [
int32x4(-1, 2, -3, 4), int32x4(-1, 2, -3, 4),
int32x4(INT32_MAX, INT32_MIN, INT32_MAX - 1, INT32_MIN + 1) int32x4(INT32_MAX, INT32_MIN, INT32_MAX - 1, INT32_MIN + 1)
@ -28,8 +33,17 @@ function test() {
testBinaryScalarFunc(v, bits, int32x4.shiftRightArithmeticByScalar, rsh); testBinaryScalarFunc(v, bits, int32x4.shiftRightArithmeticByScalar, rsh);
testBinaryScalarFunc(v, bits, int32x4.shiftRightLogicalByScalar, ursh); testBinaryScalarFunc(v, bits, int32x4.shiftRightLogicalByScalar, ursh);
} }
// Test that the shift count is coerced to an int32.
testBinaryScalarFunc(v, undefined, int32x4.shiftLeftByScalar, lsh);
testBinaryScalarFunc(v, 3.5, int32x4.shiftLeftByScalar, lsh);
testBinaryScalarFunc(v, good, int32x4.shiftLeftByScalar, lsh);
} }
var v = SIMD.int32x4(1,2,3,4);
assertThrowsInstanceOf(() => SIMD.int32x4.shiftLeftByScalar(v, bad), TestError);
assertThrowsInstanceOf(() => SIMD.int32x4.shiftRightArithmeticByScalar(v, bad), TestError);
assertThrowsInstanceOf(() => SIMD.int32x4.shiftRightLogicalByScalar(v, bad), TestError);
if (typeof reportCompare === "function") if (typeof reportCompare === "function")
reportCompare(true, true); reportCompare(true, true);
} }

View File

@ -0,0 +1,38 @@
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
var float64x2 = SIMD.float64x2;
var float32x4 = SIMD.float32x4;
var int32x4 = SIMD.int32x4;
function TestSplatX4(type, inputs, coerceFunc) {
for (var x of inputs) {
assertEqX4(SIMD[type].splat(x), [x, x, x, x].map(coerceFunc));
}
}
function TestSplatX2(type, inputs, coerceFunc) {
for (var x of inputs) {
assertEqX2(SIMD[type].splat(x), [x, x].map(coerceFunc));
}
}
function test() {
function TestError(){};
var good = {valueOf: () => 19.89};
var bad = {valueOf: () => { throw new TestError(); }};
TestSplatX4('int32x4', [0, undefined, 3.5, 42, -1337, INT32_MAX, INT32_MAX + 1, good], (x) => x | 0);
assertThrowsInstanceOf(() => SIMD.int32x4.splat(bad), TestError);
TestSplatX4('float32x4', [0, undefined, 3.5, 42, -13.37, Infinity, NaN, -0, good], (x) => Math.fround(x));
assertThrowsInstanceOf(() => SIMD.float32x4.splat(bad), TestError);
TestSplatX2('float64x2', [0, undefined, 3.5, 42, -13.37, Infinity, NaN, -0, good], (x) => +x);
assertThrowsInstanceOf(() => SIMD.float64x2.splat(bad), TestError);
if (typeof reportCompare === "function")
reportCompare(true, true);
}
test();

View File

@ -0,0 +1,90 @@
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
var BUGNUMBER = 946042;
var float32x4 = SIMD.float32x4;
var int32x4 = SIMD.int32x4;
var float64x2 = SIMD.float64x2;
var summary = 'with{X,Y,Z,W}';
function withX(arr, x) {
if (arr.length == 2)
return [x, arr[1]];
return [x, arr[1], arr[2], arr[3]];
}
function withY(arr, x) {
if (arr.length == 2)
return [arr[0], x];
return [arr[0], x, arr[2], arr[3]];
}
function withZ(arr, x) {
return [arr[0], arr[1], x, arr[3]];
}
function withW(arr, x) {
return [arr[0], arr[1], arr[2], x];
}
function testWith(vec, scalar, simdFunc, func) {
var varr = simdToArray(vec);
var observed = simdToArray(simdFunc(vec, scalar));
var expected = func(varr, scalar);
for (var i = 0; i < observed.length; i++)
assertEq(observed[i], expected[i]);
}
function test() {
print(BUGNUMBER + ": " + summary);
function testType(type, inputs) {
var length = simdToArray(inputs[0][0]).length;
for (var [vec, s] of inputs) {
testWith(vec, s, SIMD[type].withX, withX);
testWith(vec, s, SIMD[type].withY, withY);
if (length <= 2)
continue;
testWith(vec, s, SIMD[type].withZ, withZ);
testWith(vec, s, SIMD[type].withW, withW);
}
}
function TestError(){};
var good = {valueOf: () => 42};
var bad = {valueOf: () => {throw new TestError(); }};
var float32x4inputs = [
[float32x4(1, 2, 3, 4), 5],
[float32x4(1.87, 2.08, 3.84, 4.17), Math.fround(13.37)],
[float32x4(NaN, -0, Infinity, -Infinity), 0]
];
testType('float32x4', float32x4inputs);
var v = float32x4inputs[1][0];
assertEqX4(float32x4.withX(v, good), withX(simdToArray(v), good | 0));
assertThrowsInstanceOf(() => float32x4.withX(v, bad), TestError);
var float64x2inputs = [
[float64x2(1, 2), 5],
[float64x2(1.87, 2.08), Math.fround(13.37)],
[float64x2(NaN, -0), 0]
];
testType('float64x2', float64x2inputs);
var v = float64x2inputs[1][0];
assertEqX4(float64x2.withX(v, good), withX(simdToArray(v), good | 0));
assertThrowsInstanceOf(() => float64x2.withX(v, bad), TestError);
var int32x4inputs = [
[int32x4(1, 2, 3, 4), 5],
[int32x4(INT32_MIN, INT32_MAX, 3, 4), INT32_MIN],
];
testType('int32x4', int32x4inputs);
var v = int32x4inputs[1][0];
assertEqX4(int32x4.withX(v, good), withX(simdToArray(v), good | 0));
assertThrowsInstanceOf(() => int32x4.withX(v, bad), TestError);
if (typeof reportCompare === "function")
reportCompare(true, true);
}
test();

View File

@ -38,15 +38,14 @@ NewObjectCache::fillGlobal(EntryIndex entry, const Class *clasp, js::GlobalObjec
return fill(entry, clasp, global, kind, obj); return fill(entry, clasp, global, kind, obj);
} }
template <AllowGC allowGC>
inline JSObject * inline JSObject *
NewObjectCache::newObjectFromHit(JSContext *cx, EntryIndex entry_, js::gc::InitialHeap heap) NewObjectCache::newObjectFromHit(JSContext *cx, EntryIndex entryIndex, js::gc::InitialHeap heap)
{ {
// The new object cache does not account for metadata attached via callbacks. // The new object cache does not account for metadata attached via callbacks.
MOZ_ASSERT(!cx->compartment()->hasObjectMetadataCallback()); MOZ_ASSERT(!cx->compartment()->hasObjectMetadataCallback());
MOZ_ASSERT(unsigned(entry_) < mozilla::ArrayLength(entries)); MOZ_ASSERT(unsigned(entryIndex) < mozilla::ArrayLength(entries));
Entry *entry = &entries[entry_]; Entry *entry = &entries[entryIndex];
JSObject *templateObj = reinterpret_cast<JSObject *>(&entry->templateObject); JSObject *templateObj = reinterpret_cast<JSObject *>(&entry->templateObject);
@ -60,16 +59,6 @@ NewObjectCache::newObjectFromHit(JSContext *cx, EntryIndex entry_, js::gc::Initi
if (cx->runtime()->gc.upcomingZealousGC()) if (cx->runtime()->gc.upcomingZealousGC())
return nullptr; return nullptr;
// Trigger an identical allocation to the one that notified us of OOM
// so that we trigger the right kind of GC automatically.
if (allowGC) {
mozilla::DebugOnly<JSObject *> obj =
js::gc::AllocateObjectForCacheHit<allowGC>(cx, entry->kind, heap, group->clasp());
MOZ_ASSERT(!obj);
return nullptr;
}
MOZ_ASSERT(allowGC == NoGC);
JSObject *obj = js::gc::AllocateObjectForCacheHit<NoGC>(cx, entry->kind, heap, group->clasp()); JSObject *obj = js::gc::AllocateObjectForCacheHit<NoGC>(cx, entry->kind, heap, group->clasp());
if (obj) { if (obj) {
copyCachedToObject(obj, templateObj, entry->kind); copyCachedToObject(obj, templateObj, entry->kind);
@ -78,6 +67,11 @@ NewObjectCache::newObjectFromHit(JSContext *cx, EntryIndex entry_, js::gc::Initi
return obj; return obj;
} }
// Trigger an identical allocation to the one that notified us of OOM
// so that we trigger the right kind of GC automatically.
mozilla::DebugOnly<JSObject *> obj2 =
js::gc::AllocateObjectForCacheHit<CanGC>(cx, entry->kind, heap, group->clasp());
MOZ_ASSERT(!obj2);
return nullptr; return nullptr;
} }

View File

@ -296,7 +296,6 @@ class NewObjectCache
* nullptr if returning the object could possibly trigger GC (does not * nullptr if returning the object could possibly trigger GC (does not
* indicate failure). * indicate failure).
*/ */
template <AllowGC allowGC>
inline JSObject *newObjectFromHit(JSContext *cx, EntryIndex entry, js::gc::InitialHeap heap); inline JSObject *newObjectFromHit(JSContext *cx, EntryIndex entry, js::gc::InitialHeap heap);
/* Fill an entry after a cache miss. */ /* Fill an entry after a cache miss. */

View File

@ -237,7 +237,15 @@ class MOZ_STACK_CLASS ComponentLoaderInfo {
nsIChannel* ScriptChannel() { MOZ_ASSERT(mScriptChannel); return mScriptChannel; } nsIChannel* ScriptChannel() { MOZ_ASSERT(mScriptChannel); return mScriptChannel; }
nsresult EnsureScriptChannel() { nsresult EnsureScriptChannel() {
BEGIN_ENSURE(ScriptChannel, IOService, URI); BEGIN_ENSURE(ScriptChannel, IOService, URI);
return mIOService->NewChannelFromURI(mURI, getter_AddRefs(mScriptChannel)); return NS_NewChannel(getter_AddRefs(mScriptChannel),
mURI,
nsContentUtils::GetSystemPrincipal(),
nsILoadInfo::SEC_NORMAL,
nsIContentPolicy::TYPE_SCRIPT,
nullptr, // aLoadGroup
nullptr, // aCallbacks
nsIRequest::LOAD_NORMAL,
mIOService);
} }
nsIURI* ResolvedURI() { MOZ_ASSERT(mResolvedURI); return mResolvedURI; } nsIURI* ResolvedURI() { MOZ_ASSERT(mResolvedURI); return mResolvedURI; }

View File

@ -696,6 +696,7 @@ static void UnmarkFrameForDisplay(nsIFrame* aFrame) {
/* static */ FrameMetrics /* static */ FrameMetrics
nsDisplayScrollLayer::ComputeFrameMetrics(nsIFrame* aForFrame, nsDisplayScrollLayer::ComputeFrameMetrics(nsIFrame* aForFrame,
nsIFrame* aScrollFrame, nsIFrame* aScrollFrame,
nsIContent* aContent,
const nsIFrame* aReferenceFrame, const nsIFrame* aReferenceFrame,
Layer* aLayer, Layer* aLayer,
ViewID aScrollParentId, ViewID aScrollParentId,
@ -711,20 +712,19 @@ nsDisplayScrollLayer::ComputeFrameMetrics(nsIFrame* aForFrame,
metrics.SetViewport(CSSRect::FromAppUnits(aViewport)); metrics.SetViewport(CSSRect::FromAppUnits(aViewport));
ViewID scrollId = FrameMetrics::NULL_SCROLL_ID; ViewID scrollId = FrameMetrics::NULL_SCROLL_ID;
nsIContent* content = aScrollFrame ? aScrollFrame->GetContent() : nullptr; if (aContent) {
if (content) { scrollId = nsLayoutUtils::FindOrCreateIDFor(aContent);
scrollId = nsLayoutUtils::FindOrCreateIDFor(content);
nsRect dp; nsRect dp;
if (nsLayoutUtils::GetDisplayPort(content, &dp)) { if (nsLayoutUtils::GetDisplayPort(aContent, &dp)) {
metrics.SetDisplayPort(CSSRect::FromAppUnits(dp)); metrics.SetDisplayPort(CSSRect::FromAppUnits(dp));
nsLayoutUtils::LogTestDataForPaint(aLayer->Manager(), scrollId, "displayport", nsLayoutUtils::LogTestDataForPaint(aLayer->Manager(), scrollId, "displayport",
metrics.GetDisplayPort()); metrics.GetDisplayPort());
} }
if (nsLayoutUtils::GetCriticalDisplayPort(content, &dp)) { if (nsLayoutUtils::GetCriticalDisplayPort(aContent, &dp)) {
metrics.SetCriticalDisplayPort(CSSRect::FromAppUnits(dp)); metrics.SetCriticalDisplayPort(CSSRect::FromAppUnits(dp));
} }
DisplayPortMarginsPropertyData* marginsData = DisplayPortMarginsPropertyData* marginsData =
static_cast<DisplayPortMarginsPropertyData*>(content->GetProperty(nsGkAtoms::DisplayPortMargins)); static_cast<DisplayPortMarginsPropertyData*>(aContent->GetProperty(nsGkAtoms::DisplayPortMargins));
if (marginsData) { if (marginsData) {
metrics.SetDisplayPortMargins(marginsData->mMargins); metrics.SetDisplayPortMargins(marginsData->mMargins);
} }
@ -1634,15 +1634,31 @@ already_AddRefed<LayerManager> nsDisplayList::PaintRoot(nsDisplayListBuilder* aB
root->SetEventRegionsOverride(EventRegionsOverride::ForceDispatchToContent); root->SetEventRegionsOverride(EventRegionsOverride::ForceDispatchToContent);
} }
if (gfxPrefs::LayoutUseContainersForRootFrames()) { // If we're using containerless scrolling, there is still one case where we
// want the root container layer to have metrics. If the parent process is
// using XUL windows, there is no root scrollframe, and without explicitly
// creating metrics there will be no guaranteed top-level APZC.
if (gfxPrefs::LayoutUseContainersForRootFrames() ||
(XRE_IsParentProcess() && !presShell->GetRootScrollFrame()))
{
bool isRoot = presContext->IsRootContentDocument(); bool isRoot = presContext->IsRootContentDocument();
nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
nsRect viewport(aBuilder->ToReferenceFrame(frame), frame->GetSize()); nsRect viewport(aBuilder->ToReferenceFrame(frame), frame->GetSize());
nsIFrame* scrollFrame = presShell->GetRootScrollFrame();
nsIContent* content = nullptr;
if (scrollFrame) {
content = scrollFrame->GetContent();
} else if (!gfxPrefs::LayoutUseContainersForRootFrames()) {
// If there is no root scroll frame, and we're using containerless
// scrolling, pick the document element instead.
content = document->GetDocumentElement();
}
root->SetFrameMetrics( root->SetFrameMetrics(
nsDisplayScrollLayer::ComputeFrameMetrics(frame, rootScrollFrame, nsDisplayScrollLayer::ComputeFrameMetrics(frame,
presShell->GetRootScrollFrame(),
content,
aBuilder->FindReferenceFrameFor(frame), aBuilder->FindReferenceFrameFor(frame),
root, FrameMetrics::NULL_SCROLL_ID, viewport, root, FrameMetrics::NULL_SCROLL_ID, viewport,
isRoot, containerParameters)); isRoot, containerParameters));
@ -4121,9 +4137,10 @@ nsDisplaySubDocument::ComputeFrameMetrics(Layer* aLayer,
mFrame->GetOffsetToCrossDoc(ReferenceFrame()); mFrame->GetOffsetToCrossDoc(ReferenceFrame());
return MakeUnique<FrameMetrics>( return MakeUnique<FrameMetrics>(
nsDisplayScrollLayer::ComputeFrameMetrics(mFrame, rootScrollFrame, ReferenceFrame(), nsDisplayScrollLayer::ComputeFrameMetrics(
aLayer, mScrollParentId, viewport, mFrame, rootScrollFrame, rootScrollFrame->GetContent(), ReferenceFrame(),
isRootContentDocument, params)); aLayer, mScrollParentId, viewport,
isRootContentDocument, params));
} }
static bool static bool
@ -4456,8 +4473,10 @@ nsDisplayScrollLayer::ComputeFrameMetrics(Layer* aLayer,
mScrollFrame->GetOffsetToCrossDoc(ReferenceFrame()); mScrollFrame->GetOffsetToCrossDoc(ReferenceFrame());
return UniquePtr<FrameMetrics>(new FrameMetrics( return UniquePtr<FrameMetrics>(new FrameMetrics(
ComputeFrameMetrics(mScrolledFrame, mScrollFrame, ReferenceFrame(), aLayer, ComputeFrameMetrics(
mScrollParentId, viewport, false, params))); mScrolledFrame, mScrollFrame, mScrollFrame->GetContent(),
ReferenceFrame(), aLayer,
mScrollParentId, viewport, false, params)));
} }
bool bool

View File

@ -3175,6 +3175,7 @@ public:
static FrameMetrics ComputeFrameMetrics(nsIFrame* aForFrame, static FrameMetrics ComputeFrameMetrics(nsIFrame* aForFrame,
nsIFrame* aScrollFrame, nsIFrame* aScrollFrame,
nsIContent* aContent,
const nsIFrame* aReferenceFrame, const nsIFrame* aReferenceFrame,
Layer* aLayer, Layer* aLayer,
ViewID aScrollParentId, ViewID aScrollParentId,

View File

@ -867,6 +867,11 @@ GetDisplayPortFromMarginsData(nsIContent* aContent,
// We want the scroll frame, the root scroll frame differs from all // We want the scroll frame, the root scroll frame differs from all
// others in that the primary frame is not the scroll frame. // others in that the primary frame is not the scroll frame.
frame = frame->PresContext()->PresShell()->GetRootScrollFrame(); frame = frame->PresContext()->PresShell()->GetRootScrollFrame();
if (!frame) {
// If there is no root scrollframe, just exit.
return ApplyRectMultiplier(base, aMultiplier);
}
isRoot = true; isRoot = true;
} }

View File

@ -3177,7 +3177,8 @@ ScrollFrameHelper::ComputeFrameMetrics(Layer* aLayer,
bool isRoot = mIsRoot && mOuter->PresContext()->IsRootContentDocument(); bool isRoot = mIsRoot && mOuter->PresContext()->IsRootContentDocument();
*aOutput->AppendElement() = *aOutput->AppendElement() =
nsDisplayScrollLayer::ComputeFrameMetrics(mScrolledFrame, mOuter, nsDisplayScrollLayer::ComputeFrameMetrics(
mScrolledFrame, mOuter, mOuter->GetContent(),
aContainerReferenceFrame, aLayer, mScrollParentID, aContainerReferenceFrame, aLayer, mScrollParentID,
scrollport, isRoot, aParameters); scrollport, isRoot, aParameters);
} }

View File

@ -649,8 +649,8 @@ nsChangeHint nsStyleOutline::CalcDifference(const nsStyleOutline& aOther) const
(outlineIsVisible && (mOutlineOffset != aOther.mOutlineOffset || (outlineIsVisible && (mOutlineOffset != aOther.mOutlineOffset ||
mOutlineWidth != aOther.mOutlineWidth || mOutlineWidth != aOther.mOutlineWidth ||
mTwipsPerPixel != aOther.mTwipsPerPixel))) { mTwipsPerPixel != aOther.mTwipsPerPixel))) {
return NS_CombineHint(nsChangeHint_AllReflowHints, return NS_CombineHint(nsChangeHint_UpdateOverflow,
nsChangeHint_RepaintFrame); nsChangeHint_SchedulePaint);
} }
if ((mOutlineStyle != aOther.mOutlineStyle) || if ((mOutlineStyle != aOther.mOutlineStyle) ||

View File

@ -1065,15 +1065,15 @@ struct nsStyleOutline {
void RecalcData(nsPresContext* aContext); void RecalcData(nsPresContext* aContext);
nsChangeHint CalcDifference(const nsStyleOutline& aOther) const; nsChangeHint CalcDifference(const nsStyleOutline& aOther) const;
static nsChangeHint MaxDifference() { static nsChangeHint MaxDifference() {
return NS_CombineHint(nsChangeHint_AllReflowHints, return NS_CombineHint(NS_CombineHint(nsChangeHint_UpdateOverflow,
nsChangeHint_SchedulePaint),
NS_CombineHint(nsChangeHint_RepaintFrame, NS_CombineHint(nsChangeHint_RepaintFrame,
nsChangeHint_NeutralChange)); nsChangeHint_NeutralChange));
} }
static nsChangeHint MaxDifferenceNeverInherited() { static nsChangeHint MaxDifferenceNeverInherited() {
// CalcDifference never returns nsChangeHint_NeedReflow or // CalcDifference never returns nsChangeHint_NeedReflow or
// nsChangeHint_ClearAncestorIntrinsics as inherited hints. // nsChangeHint_ClearAncestorIntrinsics at all.
return NS_CombineHint(nsChangeHint_NeedReflow, return nsChangeHint(0);
nsChangeHint_ClearAncestorIntrinsics);
} }
nsStyleCorners mOutlineRadius; // [reset] coord, percent, calc nsStyleCorners mOutlineRadius; // [reset] coord, percent, calc

View File

@ -131,6 +131,7 @@ skip-if = toolkit == 'android' #bug 536603
[test_descriptor_storage.html] [test_descriptor_storage.html]
[test_descriptor_syntax_errors.html] [test_descriptor_syntax_errors.html]
[test_dont_use_document_colors.html] [test_dont_use_document_colors.html]
[test_dynamic_change_causing_reflow.html]
[test_exposed_prop_accessors.html] [test_exposed_prop_accessors.html]
[test_extra_inherit_initial.html] [test_extra_inherit_initial.html]
[test_flexbox_align_self_auto.html] [test_flexbox_align_self_auto.html]

View File

@ -0,0 +1,143 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1131371
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1131371</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1131371">Mozilla Bug 1131371</a>
<div id="display">
<div id="content">
</div>
</div>
<pre id="test">
<script type="application/javascript;version=1.7">
"use strict";
/** Test for Bug 1131371 **/
/**
* This test verifies that certain style changes do or don't cause reflow
* and/or frame construction. We do this by checking the framesReflowed &
* framesConstructed counts, before & after a style-change, and verifying
* that any change to these counts is in line with our expectations.
*
* Each entry in gTestcases contains these member-values:
* - beforeStyle (optional): initial value to use for "style" attribute.
* - afterStyle: value to change the "style" attribute to.
*
* Testcases may also include two optional member-values to express that reflow
* and/or frame construction *are* in fact expected:
* - expectConstruction (optional): if set to something truthy, then we expect
* frame construction to occur when afterStyle is set. Otherwise, we
* expect that frame construction should *not* occur.
* - expectReflow (optional): if set to something truthy, then we expect
* reflow to occur when afterStyle is set. Otherwise, we expect that
* reflow should *not* occur.
*/
const gTestcases = [
// Things that shouldn't cause reflow:
// -----------------------------------
// * Adding an outline (e.g. for focus ring).
{
afterStyle: "outline: 1px dotted black",
},
// * Changing between completely-different outlines.
{
beforeStyle: "outline: 2px solid black",
afterStyle: "outline: 6px dashed yellow",
},
// Things that *should* cause reflow:
// ----------------------------------
// (e.g. to make sure our counts are actually measuring something)
// * Changing 'height' should cause reflow, but not frame construction.
{
beforeStyle: "height: 10px",
afterStyle: "height: 15px",
expectReflow: true,
},
// * Changing 'display' should cause frame construction and reflow.
{
beforeStyle: "display: inline",
afterStyle: "display: table",
expectConstruction: true,
expectReflow: true,
},
];
// Helper function to let us call either "is" or "isnot" & assemble
// the failure message, based on the provided parameters.
function checkFinalCount(aFinalCount, aExpectedCount,
aExpectChange, aMsgPrefix, aCountDescription)
{
let compareFunc;
let msg = aMsgPrefix;
if (aExpectChange) {
compareFunc = isnot;
msg += "should cause " + aCountDescription;
} else {
compareFunc = is;
msg += "should not cause " + aCountDescription;
}
compareFunc(aFinalCount, aExpectedCount, msg);
}
// Vars used in runOneTest that we really only have to look up once:
const gUtils = SpecialPowers.getDOMWindowUtils(window);
const gElem = document.getElementById("content");
function runOneTest(aTestcase)
{
// sanity-check that we have the one main thing we need:
if (!aTestcase.afterStyle) {
ok(false, "testcase is missing an 'afterStyle' to change to");
return;
}
// Set the "before" style, and compose the first part of the message
// to be used in our "is"/"isnot" invocations:
let msgPrefix = "Changing style ";
if (aTestcase.beforeStyle) {
gElem.setAttribute("style", aTestcase.beforeStyle);
msgPrefix += "from '" + aTestcase.beforeStyle + "' ";
}
msgPrefix += "to '" + aTestcase.afterStyle + "' ";
// Establish initial counts:
let unusedVal = gElem.offsetHeight; // flush layout
let origFramesConstructed = gUtils.framesConstructed;
let origFramesReflowed = gUtils.framesReflowed;
// Make the change and flush:
gElem.setAttribute("style", aTestcase.afterStyle);
unusedVal = gElem.offsetHeight; // flush layout
// Make our is/isnot assertions about whether things should have changed:
checkFinalCount(gUtils.framesConstructed, origFramesConstructed,
aTestcase.expectConstruction, msgPrefix,
"frame construction");
checkFinalCount(gUtils.framesReflowed, origFramesReflowed,
aTestcase.expectReflow, msgPrefix,
"reflow");
// Clean up!
gElem.removeAttribute("style");
}
gTestcases.forEach(runOneTest);
</script>
</pre>
</body>
</html>

View File

@ -366,8 +366,10 @@ void NrIceCtx::trickle_cb(void *arg, nr_ice_ctx *ice_ctx,
NrIceCtx *ctx = static_cast<NrIceCtx *>(arg); NrIceCtx *ctx = static_cast<NrIceCtx *>(arg);
RefPtr<NrIceMediaStream> s = ctx->FindStream(stream); RefPtr<NrIceMediaStream> s = ctx->FindStream(stream);
// Streams which do not exist shouldn't have candidates. if (!s) {
MOZ_ASSERT(s); // This stream has been removed because it is inactive
return;
}
// Format the candidate. // Format the candidate.
char candidate_str[NR_ICE_MAX_ATTRIBUTE_SIZE]; char candidate_str[NR_ICE_MAX_ATTRIBUTE_SIZE];
@ -668,32 +670,26 @@ abort:
nsresult NrIceCtx::StartGathering() { nsresult NrIceCtx::StartGathering() {
ASSERT_ON_THREAD(sts_target_); ASSERT_ON_THREAD(sts_target_);
MOZ_ASSERT(connection_state_ == ICE_CTX_INIT); // This might start gathering for the first time, or again after
if (connection_state_ != ICE_CTX_INIT) { // renegotiation, or might do nothing at all if gathering has already
MOZ_MTLOG(ML_ERROR, "ICE ctx in the wrong state for gathering: '" // finished.
<< name_ << "' state: " << connection_state_); int r = nr_ice_gather(ctx_, &NrIceCtx::gather_cb, this);
if (r && (r != R_WOULDBLOCK)) {
MOZ_MTLOG(ML_ERROR, "Couldn't gather ICE candidates for '"
<< name_ << "', error=" << r);
SetConnectionState(ICE_CTX_FAILED); SetConnectionState(ICE_CTX_FAILED);
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
int r = nr_ice_initialize(ctx_, &NrIceCtx::initialized_cb, this);
if (r && r != R_WOULDBLOCK) {
MOZ_MTLOG(ML_ERROR, "Couldn't gather ICE candidates for '"
<< name_ << "'");
SetConnectionState(ICE_CTX_FAILED);
return NS_ERROR_FAILURE;
}
SetGatheringState(ICE_CTX_GATHER_STARTED); SetGatheringState(ICE_CTX_GATHER_STARTED);
return NS_OK; return NS_OK;
} }
RefPtr<NrIceMediaStream> NrIceCtx::FindStream( RefPtr<NrIceMediaStream> NrIceCtx::FindStream(
nr_ice_media_stream *stream) { nr_ice_media_stream *stream) {
for (size_t i=0; i<streams_.size(); ++i) { for (size_t i=0; i<streams_.size(); ++i) {
if (streams_[i]->stream() == stream) { if (streams_[i] && (streams_[i]->stream() == stream)) {
return streams_[i]; return streams_[i];
} }
} }
@ -771,7 +767,7 @@ nsresult NrIceCtx::StartChecks() {
} }
void NrIceCtx::initialized_cb(NR_SOCKET s, int h, void *arg) { void NrIceCtx::gather_cb(NR_SOCKET s, int h, void *arg) {
NrIceCtx *ctx = static_cast<NrIceCtx *>(arg); NrIceCtx *ctx = static_cast<NrIceCtx *>(arg);
ctx->SetGatheringState(ICE_CTX_GATHER_COMPLETE); ctx->SetGatheringState(ICE_CTX_GATHER_COMPLETE);

View File

@ -225,7 +225,7 @@ class NrIceCtx {
// Create a media stream // Create a media stream
RefPtr<NrIceMediaStream> CreateStream(const std::string& name, RefPtr<NrIceMediaStream> CreateStream(const std::string& name,
int components); int components);
RefPtr<NrIceMediaStream> GetStream(size_t index) { RefPtr<NrIceMediaStream> GetStream(size_t index) {
if (index < streams_.size()) { if (index < streams_.size()) {
@ -234,6 +234,11 @@ class NrIceCtx {
return nullptr; return nullptr;
} }
void RemoveStream(size_t index)
{
streams_[index] = nullptr;
}
// The name of the ctx // The name of the ctx
const std::string& name() const { return name_; } const std::string& name() const { return name_; }
@ -325,7 +330,7 @@ class NrIceCtx {
DISALLOW_COPY_ASSIGN(NrIceCtx); DISALLOW_COPY_ASSIGN(NrIceCtx);
// Callbacks for nICEr // Callbacks for nICEr
static void initialized_cb(NR_SOCKET s, int h, void *arg); // ICE initialized static void gather_cb(NR_SOCKET s, int h, void *arg); // ICE gather complete
// Handler implementation // Handler implementation
static int select_pair(void *obj,nr_ice_media_stream *stream, static int select_pair(void *obj,nr_ice_media_stream *stream,

View File

@ -209,6 +209,7 @@ nsresult NrIceMediaStream::ParseAttributes(std::vector<std::string>&
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
has_parsed_attrs_ = true;
return NS_OK; return NS_OK;
} }

View File

@ -142,6 +142,7 @@ class NrIceMediaStream {
// Parse remote attributes // Parse remote attributes
nsresult ParseAttributes(std::vector<std::string>& candidates); nsresult ParseAttributes(std::vector<std::string>& candidates);
bool HasParsedAttributes() const { return has_parsed_attrs_; }
// Parse trickle ICE candidate // Parse trickle ICE candidate
nsresult ParseTrickleCandidate(const std::string& candidate); nsresult ParseTrickleCandidate(const std::string& candidate);
@ -198,7 +199,8 @@ class NrIceMediaStream {
name_(name), name_(name),
components_(components), components_(components),
stream_(nullptr), stream_(nullptr),
level_(0) {} level_(0),
has_parsed_attrs_(false) {}
~NrIceMediaStream(); ~NrIceMediaStream();
@ -210,6 +212,7 @@ class NrIceMediaStream {
const size_t components_; const size_t components_;
nr_ice_media_stream *stream_; nr_ice_media_stream *stream_;
uint16_t level_; uint16_t level_;
bool has_parsed_attrs_;
}; };

View File

@ -265,7 +265,7 @@ class IceTestPeer : public sigslot::has_slots<> {
PR_Sleep(1000); PR_Sleep(1000);
} }
void AddStream(int components) { void AddStream_s(int components) {
char name[100]; char name[100];
snprintf(name, sizeof(name), "%s:stream%d", name_.c_str(), snprintf(name, sizeof(name), "%s:stream%d", name_.c_str(),
(int)streams_.size()); (int)streams_.size());
@ -281,6 +281,14 @@ class IceTestPeer : public sigslot::has_slots<> {
stream->SignalPacketReceived.connect(this, &IceTestPeer::PacketReceived); stream->SignalPacketReceived.connect(this, &IceTestPeer::PacketReceived);
} }
void AddStream(int components)
{
test_utils->sts_target()->Dispatch(WrapRunnable(this,
&IceTestPeer::AddStream_s,
components),
NS_DISPATCH_SYNC);
}
void SetStunServer(const std::string addr, uint16_t port) { void SetStunServer(const std::string addr, uint16_t port) {
if (addr.empty()) { if (addr.empty()) {
// Happens when MOZ_DISABLE_NONLOCAL_CONNECTIONS is set // Happens when MOZ_DISABLE_NONLOCAL_CONNECTIONS is set
@ -410,9 +418,17 @@ class IceTestPeer : public sigslot::has_slots<> {
bool gathering_complete() { return gathering_complete_; } bool gathering_complete() { return gathering_complete_; }
int ready_ct() { return ready_ct_; } int ready_ct() { return ready_ct_; }
bool is_ready(size_t stream) { bool is_ready_s(size_t stream) {
return streams_[stream]->state() == NrIceMediaStream::ICE_OPEN; return streams_[stream]->state() == NrIceMediaStream::ICE_OPEN;
} }
bool is_ready(size_t stream)
{
bool result;
test_utils->sts_target()->Dispatch(
WrapRunnableRet(this, &IceTestPeer::is_ready_s, stream, &result),
NS_DISPATCH_SYNC);
return result;
}
bool ice_complete() { return ice_complete_; } bool ice_complete() { return ice_complete_; }
bool ice_reached_checking() { return ice_reached_checking_; } bool ice_reached_checking() { return ice_reached_checking_; }
size_t received() { return received_; } size_t received() { return received_; }
@ -426,12 +442,16 @@ class IceTestPeer : public sigslot::has_slots<> {
remote_ = remote; remote_ = remote;
trickle_mode_ = trickle_mode; trickle_mode_ = trickle_mode;
ice_complete_ = false;
res = ice_ctx_->ParseGlobalAttributes(remote->GetGlobalAttributes()); res = ice_ctx_->ParseGlobalAttributes(remote->GetGlobalAttributes());
ASSERT_TRUE(NS_SUCCEEDED(res)); ASSERT_TRUE(NS_SUCCEEDED(res));
if (trickle_mode == TRICKLE_NONE || if (trickle_mode == TRICKLE_NONE ||
trickle_mode == TRICKLE_REAL) { trickle_mode == TRICKLE_REAL) {
for (size_t i=0; i<streams_.size(); ++i) { for (size_t i=0; i<streams_.size(); ++i) {
if (streams_[i]->HasParsedAttributes()) {
continue;
}
std::vector<std::string> candidates = std::vector<std::string> candidates =
remote->GetCandidates(i); remote->GetCandidates(i);
@ -444,7 +464,11 @@ class IceTestPeer : public sigslot::has_slots<> {
} else { } else {
// Parse empty attributes and then trickle them out later // Parse empty attributes and then trickle them out later
for (size_t i=0; i<streams_.size(); ++i) { for (size_t i=0; i<streams_.size(); ++i) {
if (streams_[i]->HasParsedAttributes()) {
continue;
}
std::vector<std::string> empty_attrs; std::vector<std::string> empty_attrs;
std::cout << "Calling ParseAttributes on stream " << i << std::endl;
res = streams_[i]->ParseAttributes(empty_attrs); res = streams_[i]->ParseAttributes(empty_attrs);
ASSERT_TRUE(NS_SUCCEEDED(res)); ASSERT_TRUE(NS_SUCCEEDED(res));
} }
@ -470,6 +494,8 @@ class IceTestPeer : public sigslot::has_slots<> {
// If we are in trickle deferred mode, now trickle in the candidates // If we are in trickle deferred mode, now trickle in the candidates
// for |stream| // for |stream|
// The size of streams_ is not going to change out from under us, so should
// be safe here.
ASSERT_GT(remote_->streams_.size(), stream); ASSERT_GT(remote_->streams_.size(), stream);
std::vector<SchedulableTrickleCandidate*>& candidates = std::vector<SchedulableTrickleCandidate*>& candidates =
@ -602,6 +628,16 @@ class IceTestPeer : public sigslot::has_slots<> {
} }
ice_ctx_ = nullptr; ice_ctx_ = nullptr;
if (remote_) {
remote_->UnsetRemote();
remote_ = nullptr;
}
}
void UnsetRemote()
{
remote_ = nullptr;
} }
void StartChecks() { void StartChecks() {
@ -649,7 +685,7 @@ class IceTestPeer : public sigslot::has_slots<> {
// If we are connected, then try to trickle to the // If we are connected, then try to trickle to the
// other side. // other side.
if (remote_ && remote_->remote_) { if (remote_ && remote_->remote_ && (trickle_mode_ != TRICKLE_SIMULATE)) {
std::vector<mozilla::RefPtr<NrIceMediaStream> >::iterator it = std::vector<mozilla::RefPtr<NrIceMediaStream> >::iterator it =
std::find(streams_.begin(), streams_.end(), stream); std::find(streams_.begin(), streams_.end(), stream);
ASSERT_NE(streams_.end(), it); ASSERT_NE(streams_.end(), it);
@ -663,8 +699,9 @@ class IceTestPeer : public sigslot::has_slots<> {
} }
} }
nsresult GetCandidatePairs(size_t stream_index, nsresult GetCandidatePairs_s(size_t stream_index,
std::vector<NrIceCandidatePair>* pairs) { std::vector<NrIceCandidatePair>* pairs)
{
MOZ_ASSERT(pairs); MOZ_ASSERT(pairs);
if (stream_index >= streams_.size()) { if (stream_index >= streams_.size()) {
// Is there a better error for "no such index"? // Is there a better error for "no such index"?
@ -672,14 +709,20 @@ class IceTestPeer : public sigslot::has_slots<> {
return NS_ERROR_INVALID_ARG; return NS_ERROR_INVALID_ARG;
} }
nsresult res; return streams_[stream_index]->GetCandidatePairs(pairs);
}
nsresult GetCandidatePairs(size_t stream_index,
std::vector<NrIceCandidatePair>* pairs) {
nsresult v;
test_utils->sts_target()->Dispatch( test_utils->sts_target()->Dispatch(
WrapRunnableRet(streams_[stream_index], WrapRunnableRet(this,
&NrIceMediaStream::GetCandidatePairs, &IceTestPeer::GetCandidatePairs_s,
stream_index,
pairs, pairs,
&res), &v),
NS_DISPATCH_SYNC); NS_DISPATCH_SYNC);
return res; return v;
} }
void DumpCandidatePair(const NrIceCandidatePair& pair) { void DumpCandidatePair(const NrIceCandidatePair& pair) {
@ -693,7 +736,7 @@ class IceTestPeer : public sigslot::has_slots<> {
<< " codeword = " << pair.codeword << std::endl; << " codeword = " << pair.codeword << std::endl;
} }
void DumpCandidatePairs(NrIceMediaStream *stream) { void DumpCandidatePairs_s(NrIceMediaStream *stream) {
std::vector<NrIceCandidatePair> pairs; std::vector<NrIceCandidatePair> pairs;
nsresult res = stream->GetCandidatePairs(&pairs); nsresult res = stream->GetCandidatePairs(&pairs);
ASSERT_TRUE(NS_SUCCEEDED(res)); ASSERT_TRUE(NS_SUCCEEDED(res));
@ -707,10 +750,10 @@ class IceTestPeer : public sigslot::has_slots<> {
std::cerr << "]" << std::endl; std::cerr << "]" << std::endl;
} }
void DumpCandidatePairs() { void DumpCandidatePairs_s() {
std::cerr << "Dumping candidate pairs for all streams [" << std::endl; std::cerr << "Dumping candidate pairs for all streams [" << std::endl;
for (size_t s = 0; s < streams_.size(); ++s) { for (size_t s = 0; s < streams_.size(); ++s) {
DumpCandidatePairs(streams_[s]); DumpCandidatePairs_s(streams_[s]);
} }
std::cerr << "]" << std::endl; std::cerr << "]" << std::endl;
} }
@ -790,11 +833,11 @@ class IceTestPeer : public sigslot::has_slots<> {
void StreamReady(NrIceMediaStream *stream) { void StreamReady(NrIceMediaStream *stream) {
++ready_ct_; ++ready_ct_;
std::cerr << "Stream ready " << stream->name() << " ct=" << ready_ct_ << std::endl; std::cerr << "Stream ready " << stream->name() << " ct=" << ready_ct_ << std::endl;
DumpCandidatePairs(stream); DumpCandidatePairs_s(stream);
} }
void StreamFailed(NrIceMediaStream *stream) { void StreamFailed(NrIceMediaStream *stream) {
std::cerr << "Stream failed " << stream->name() << " ct=" << ready_ct_ << std::endl; std::cerr << "Stream failed " << stream->name() << " ct=" << ready_ct_ << std::endl;
DumpCandidatePairs(stream); DumpCandidatePairs_s(stream);
} }
void ConnectionStateChange(NrIceCtx* ctx, void ConnectionStateChange(NrIceCtx* ctx,
@ -834,20 +877,39 @@ class IceTestPeer : public sigslot::has_slots<> {
candidate_filter_ = filter; candidate_filter_ = filter;
} }
// Allow us to parse candidates directly on the current thread. void ParseCandidate_s(size_t i, const std::string& candidate) {
void ParseCandidate(size_t i, const std::string& candidate) {
std::vector<std::string> attributes; std::vector<std::string> attributes;
attributes.push_back(candidate); attributes.push_back(candidate);
streams_[i]->ParseAttributes(attributes); streams_[i]->ParseAttributes(attributes);
} }
void DisableComponent(size_t stream, int component_id) { void ParseCandidate(size_t i, const std::string& candidate)
{
test_utils->sts_target()->Dispatch(
WrapRunnable(this,
&IceTestPeer::ParseCandidate_s,
i,
candidate),
NS_DISPATCH_SYNC);
}
void DisableComponent_s(size_t stream, int component_id) {
ASSERT_LT(stream, streams_.size()); ASSERT_LT(stream, streams_.size());
nsresult res = streams_[stream]->DisableComponent(component_id); nsresult res = streams_[stream]->DisableComponent(component_id);
ASSERT_TRUE(NS_SUCCEEDED(res)); ASSERT_TRUE(NS_SUCCEEDED(res));
} }
void DisableComponent(size_t stream, int component_id)
{
test_utils->sts_target()->Dispatch(
WrapRunnable(this,
&IceTestPeer::DisableComponent_s,
stream,
component_id),
NS_DISPATCH_SYNC);
}
int trickled() { return trickled_; } int trickled() { return trickled_; }
void SetControlling(NrIceCtx::Controlling controlling) { void SetControlling(NrIceCtx::Controlling controlling) {
@ -1612,6 +1674,64 @@ TEST_F(IceConnectTest, TestConnectTrickleTwoStreamsOneComponent) {
AssertCheckingReached(); AssertCheckingReached();
} }
void RealisticTrickleDelay(
std::vector<SchedulableTrickleCandidate*>& candidates) {
for (size_t i = 0; i < candidates.size(); ++i) {
SchedulableTrickleCandidate* cand = candidates[i];
if (cand->IsHost()) {
cand->Schedule(i*10);
} else if (cand->IsReflexive()) {
cand->Schedule(i*10 + 100);
} else if (cand->IsRelay()) {
cand->Schedule(i*10 + 200);
}
}
}
void DelayRelayCandidates(
std::vector<SchedulableTrickleCandidate*>& candidates,
unsigned int ms) {
for (auto i = candidates.begin(); i != candidates.end(); ++i) {
if ((*i)->IsRelay()) {
(*i)->Schedule(ms);
} else {
(*i)->Schedule(0);
}
}
}
TEST_F(IceConnectTest, TestConnectTrickleAddStreamDuringICE) {
AddStream("first", 1);
ASSERT_TRUE(Gather());
ConnectTrickle();
RealisticTrickleDelay(p1_->ControlTrickle(0));
RealisticTrickleDelay(p2_->ControlTrickle(0));
AddStream("second", 1);
RealisticTrickleDelay(p1_->ControlTrickle(1));
RealisticTrickleDelay(p2_->ControlTrickle(1));
ASSERT_TRUE_WAIT(p1_->ice_complete(), 1000);
ASSERT_TRUE_WAIT(p2_->ice_complete(), 1000);
AssertCheckingReached();
}
TEST_F(IceConnectTest, TestConnectTrickleAddStreamAfterICE) {
AddStream("first", 1);
ASSERT_TRUE(Gather());
ConnectTrickle();
RealisticTrickleDelay(p1_->ControlTrickle(0));
RealisticTrickleDelay(p2_->ControlTrickle(0));
ASSERT_TRUE_WAIT(p1_->ice_complete(), 1000);
ASSERT_TRUE_WAIT(p2_->ice_complete(), 1000);
AddStream("second", 1);
ASSERT_TRUE(Gather());
ConnectTrickle();
RealisticTrickleDelay(p1_->ControlTrickle(1));
RealisticTrickleDelay(p2_->ControlTrickle(1));
ASSERT_TRUE_WAIT(p1_->ice_complete(), 1000);
ASSERT_TRUE_WAIT(p2_->ice_complete(), 1000);
AssertCheckingReached();
}
TEST_F(IceConnectTest, TestConnectRealTrickleOneStreamOneComponent) { TEST_F(IceConnectTest, TestConnectRealTrickleOneStreamOneComponent) {
AddStream("first", 1); AddStream("first", 1);
AddStream("second", 1); AddStream("second", 1);
@ -1657,32 +1777,6 @@ TEST_F(IceConnectTest, TestConnectTurnWithDelay) {
WaitForComplete(); WaitForComplete();
} }
void RealisticTrickleDelay(
std::vector<SchedulableTrickleCandidate*>& candidates) {
for (size_t i = 0; i < candidates.size(); ++i) {
SchedulableTrickleCandidate* cand = candidates[i];
if (cand->IsHost()) {
cand->Schedule(i*10);
} else if (cand->IsReflexive()) {
cand->Schedule(i*10 + 100);
} else if (cand->IsRelay()) {
cand->Schedule(i*10 + 200);
}
}
}
void DelayRelayCandidates(
std::vector<SchedulableTrickleCandidate*>& candidates,
unsigned int ms) {
for (auto i = candidates.begin(); i != candidates.end(); ++i) {
if ((*i)->IsRelay()) {
(*i)->Schedule(ms);
} else {
(*i)->Schedule(0);
}
}
}
TEST_F(IceConnectTest, TestConnectTurnWithNormalTrickleDelay) { TEST_F(IceConnectTest, TestConnectTurnWithNormalTrickleDelay) {
if (g_turn_server.empty()) if (g_turn_server.empty())
return; return;

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