mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge mozilla-central to fx-team
This commit is contained in:
commit
7696f0bc8e
@ -15,9 +15,9 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="e67d319d0854e32e23210784eb9c4e1b8a025adb"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="2d370fa35c1a0ee2a637e3772c0843586a5f96c9"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="2782648aabf0af464dd9c4202b367b408898546d"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
|
@ -15,9 +15,9 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="e67d319d0854e32e23210784eb9c4e1b8a025adb"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="2d370fa35c1a0ee2a637e3772c0843586a5f96c9"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="2782648aabf0af464dd9c4202b367b408898546d"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
|
@ -19,8 +19,8 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="e67d319d0854e32e23210784eb9c4e1b8a025adb"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="2d370fa35c1a0ee2a637e3772c0843586a5f96c9"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="27eb2f04e149fc2c9976d881b1b5984bbe7ee089"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="067c08fb3e5744b42b68d1f861245f7d507109bc"/>
|
||||
|
@ -17,8 +17,8 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="e67d319d0854e32e23210784eb9c4e1b8a025adb"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="2d370fa35c1a0ee2a637e3772c0843586a5f96c9"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="2782648aabf0af464dd9c4202b367b408898546d"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a1f9532e4157df2dc8d3e9c8100120f80117dcd4"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
|
@ -15,9 +15,9 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="e67d319d0854e32e23210784eb9c4e1b8a025adb"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="2d370fa35c1a0ee2a637e3772c0843586a5f96c9"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="2782648aabf0af464dd9c4202b367b408898546d"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
|
@ -15,9 +15,9 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="c9d4fe680662ee44a4bdea42ae00366f5df399cf">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="e67d319d0854e32e23210784eb9c4e1b8a025adb"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="2d370fa35c1a0ee2a637e3772c0843586a5f96c9"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="2782648aabf0af464dd9c4202b367b408898546d"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
|
@ -19,8 +19,8 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="e67d319d0854e32e23210784eb9c4e1b8a025adb"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="2d370fa35c1a0ee2a637e3772c0843586a5f96c9"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="27eb2f04e149fc2c9976d881b1b5984bbe7ee089"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="067c08fb3e5744b42b68d1f861245f7d507109bc"/>
|
||||
|
@ -15,9 +15,9 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="e67d319d0854e32e23210784eb9c4e1b8a025adb"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="2d370fa35c1a0ee2a637e3772c0843586a5f96c9"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="2782648aabf0af464dd9c4202b367b408898546d"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
|
@ -1,9 +1,9 @@
|
||||
{
|
||||
"git": {
|
||||
"git_revision": "e67d319d0854e32e23210784eb9c4e1b8a025adb",
|
||||
"git_revision": "2d370fa35c1a0ee2a637e3772c0843586a5f96c9",
|
||||
"remote": "https://git.mozilla.org/releases/gaia.git",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "62487ac0eb5432a716c1a8405bd9c5ebc9db2b1d",
|
||||
"revision": "71b3fccca59c5a80c05e5ed99e1f5108c1b1ffad",
|
||||
"repo_path": "integration/gaia-central"
|
||||
}
|
||||
|
@ -17,8 +17,8 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="e67d319d0854e32e23210784eb9c4e1b8a025adb"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="2d370fa35c1a0ee2a637e3772c0843586a5f96c9"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="2782648aabf0af464dd9c4202b367b408898546d"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a1f9532e4157df2dc8d3e9c8100120f80117dcd4"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
|
@ -15,9 +15,9 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="c9d4fe680662ee44a4bdea42ae00366f5df399cf">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="e67d319d0854e32e23210784eb9c4e1b8a025adb"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="2d370fa35c1a0ee2a637e3772c0843586a5f96c9"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="2782648aabf0af464dd9c4202b367b408898546d"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
|
@ -205,11 +205,7 @@ function beginTest() {
|
||||
}
|
||||
}
|
||||
|
||||
var prefs = [
|
||||
[ "canvas.capturestream.enabled", true ],
|
||||
];
|
||||
SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
|
||||
|
||||
beginTest();
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
@ -211,13 +211,8 @@ function beginTest() {
|
||||
document.manager.runTests(corsTests, startTest);
|
||||
}
|
||||
|
||||
var prefs = [
|
||||
[ "canvas.capturestream.enabled", true ],
|
||||
];
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
|
||||
|
||||
beginTest();
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
@ -20,7 +20,7 @@ pref(webgl.force-layers-readback,true) == webgl-clear-test.html?readback wrappe
|
||||
== webgl-resize-test.html wrapper.html?green.png
|
||||
|
||||
# Check that captureStream() displays in a local video element
|
||||
pref(canvas.capturestream.enabled,true) skip-if(winWidget&&layersGPUAccelerated&&d2d) == webgl-capturestream-test.html?preserve wrapper.html?green.png
|
||||
skip-if(winWidget&&layersGPUAccelerated&&d2d) == webgl-capturestream-test.html?preserve wrapper.html?green.png
|
||||
|
||||
# Some of the failure conditions are a little crazy. I'm (jgilbert) setting these based on
|
||||
# failures encountered when running on Try, and then targetting the Try config by
|
||||
@ -157,6 +157,6 @@ pref(canvas.focusring.enabled,true) skip-if(B2G) skip-if(Android&&AndroidVersion
|
||||
pref(canvas.customfocusring.enabled,true) skip-if(B2G) skip-if(Android&&AndroidVersion<15,8,500) skip-if(winWidget) needs-focus == drawCustomFocusRing.html drawCustomFocusRing-ref.html
|
||||
|
||||
# Check that captureStream() displays in a local video element
|
||||
pref(canvas.capturestream.enabled,true) skip-if(winWidget&&layersGPUAccelerated&&d2d) == capturestream.html wrapper.html?green.png
|
||||
skip-if(winWidget&&layersGPUAccelerated&&d2d) == capturestream.html wrapper.html?green.png
|
||||
|
||||
fuzzy-if(Android,3,40) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu),1,1) == 1177726-text-stroke-bounds.html 1177726-text-stroke-bounds-ref.html
|
||||
|
@ -107,9 +107,6 @@ function beginTest() {
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var prefs = [
|
||||
[ "canvas.capturestream.enabled", true ],
|
||||
];
|
||||
SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
|
||||
beginTest();
|
||||
</script>
|
||||
|
||||
|
@ -171,9 +171,6 @@ function beginTest() {
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var prefs = [
|
||||
[ "canvas.capturestream.enabled", true ],
|
||||
];
|
||||
SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
|
||||
beginTest();
|
||||
</script>
|
||||
|
||||
|
@ -358,6 +358,14 @@ DOMMediaStream::StopTrack(TrackID aTrackID)
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
DOMMediaStream::ApplyConstraintsToTrack(TrackID aTrackID,
|
||||
const MediaTrackConstraints& aConstraints,
|
||||
ErrorResult &aRv)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
DOMMediaStream::CombineWithPrincipal(nsIPrincipal* aPrincipal)
|
||||
{
|
||||
|
@ -48,6 +48,7 @@ class VideoTrack;
|
||||
class AudioTrackList;
|
||||
class VideoTrackList;
|
||||
class MediaTrackListListener;
|
||||
struct MediaTrackConstraints;
|
||||
} // namespace dom
|
||||
|
||||
namespace layers {
|
||||
@ -77,6 +78,7 @@ class DOMMediaStream : public DOMEventTargetHelper
|
||||
typedef dom::MediaTrackListListener MediaTrackListListener;
|
||||
|
||||
public:
|
||||
typedef dom::MediaTrackConstraints MediaTrackConstraints;
|
||||
typedef uint8_t TrackTypeHints;
|
||||
|
||||
DOMMediaStream();
|
||||
@ -121,6 +123,11 @@ public:
|
||||
|
||||
virtual void StopTrack(TrackID aTrackID);
|
||||
|
||||
virtual already_AddRefed<dom::Promise>
|
||||
ApplyConstraintsToTrack(TrackID aTrackID,
|
||||
const MediaTrackConstraints& aConstraints,
|
||||
ErrorResult &aRv);
|
||||
|
||||
virtual DOMLocalMediaStream* AsDOMLocalMediaStream() { return nullptr; }
|
||||
virtual DOMHwMediaStream* AsDOMHwMediaStream() { return nullptr; }
|
||||
|
||||
|
@ -79,8 +79,6 @@
|
||||
#include "mozilla/WindowsVersion.h"
|
||||
#endif
|
||||
|
||||
#include <map>
|
||||
|
||||
// GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
|
||||
// GetTickCount() and conflicts with MediaStream::GetCurrentTime.
|
||||
#ifdef GetCurrentTime
|
||||
@ -119,6 +117,7 @@ using dom::File;
|
||||
using dom::MediaStreamConstraints;
|
||||
using dom::MediaTrackConstraintSet;
|
||||
using dom::MediaTrackConstraints;
|
||||
using dom::MediaStreamTrack;
|
||||
using dom::MediaStreamError;
|
||||
using dom::GetUserMediaRequest;
|
||||
using dom::Sequence;
|
||||
@ -214,6 +213,154 @@ HostHasPermission(nsIURI &docURI)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Generic class for running long media operations like Start off the main
|
||||
// thread, and then (because nsDOMMediaStreams aren't threadsafe),
|
||||
// ProxyReleases mStream since it's cycle collected.
|
||||
class MediaOperationTask : public Task
|
||||
{
|
||||
public:
|
||||
// so we can send Stop without AddRef()ing from the MSG thread
|
||||
MediaOperationTask(MediaOperation aType,
|
||||
GetUserMediaCallbackMediaStreamListener* aListener,
|
||||
DOMMediaStream* aStream,
|
||||
DOMMediaStream::OnTracksAvailableCallback* aOnTracksAvailableCallback,
|
||||
AudioDevice* aAudioDevice,
|
||||
VideoDevice* aVideoDevice,
|
||||
bool aBool,
|
||||
uint64_t aWindowID,
|
||||
already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
|
||||
const dom::MediaTrackConstraints& aConstraints = dom::MediaTrackConstraints())
|
||||
: mType(aType)
|
||||
, mStream(aStream)
|
||||
, mOnTracksAvailableCallback(aOnTracksAvailableCallback)
|
||||
, mAudioDevice(aAudioDevice)
|
||||
, mVideoDevice(aVideoDevice)
|
||||
, mListener(aListener)
|
||||
, mBool(aBool)
|
||||
, mWindowID(aWindowID)
|
||||
, mOnFailure(aError)
|
||||
, mConstraints(aConstraints)
|
||||
{}
|
||||
|
||||
~MediaOperationTask()
|
||||
{
|
||||
// MediaStreams can be released on any thread.
|
||||
}
|
||||
|
||||
void
|
||||
ReturnCallbackError(nsresult rv, const char* errorLog);
|
||||
|
||||
void
|
||||
Run()
|
||||
{
|
||||
SourceMediaStream *source = mListener->GetSourceStream();
|
||||
// No locking between these is required as all the callbacks for the
|
||||
// same MediaStream will occur on the same thread.
|
||||
if (!source) // means the stream was never Activated()
|
||||
return;
|
||||
|
||||
switch (mType) {
|
||||
case MEDIA_START:
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Never call on main thread");
|
||||
nsresult rv;
|
||||
|
||||
if (mAudioDevice) {
|
||||
rv = mAudioDevice->GetSource()->Start(source, kAudioTrack);
|
||||
if (NS_FAILED(rv)) {
|
||||
ReturnCallbackError(rv, "Starting audio failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (mVideoDevice) {
|
||||
rv = mVideoDevice->GetSource()->Start(source, kVideoTrack);
|
||||
if (NS_FAILED(rv)) {
|
||||
ReturnCallbackError(rv, "Starting video failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Start() queued the tracks to be added synchronously to avoid races
|
||||
source->FinishAddTracks();
|
||||
|
||||
source->SetPullEnabled(true);
|
||||
source->AdvanceKnownTracksTime(STREAM_TIME_MAX);
|
||||
|
||||
MM_LOG(("started all sources"));
|
||||
// Forward mOnTracksAvailableCallback to GetUserMediaNotificationEvent,
|
||||
// because mOnTracksAvailableCallback needs to be added to mStream
|
||||
// on the main thread.
|
||||
nsIRunnable *event =
|
||||
new GetUserMediaNotificationEvent(GetUserMediaNotificationEvent::STARTING,
|
||||
mStream.forget(),
|
||||
mOnTracksAvailableCallback.forget(),
|
||||
mAudioDevice != nullptr,
|
||||
mVideoDevice != nullptr,
|
||||
mWindowID, mOnFailure.forget());
|
||||
// event must always be released on mainthread due to the JS callbacks
|
||||
// in the TracksAvailableCallback
|
||||
NS_DispatchToMainThread(event);
|
||||
}
|
||||
break;
|
||||
|
||||
case MEDIA_STOP:
|
||||
case MEDIA_STOP_TRACK:
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Never call on main thread");
|
||||
if (mAudioDevice) {
|
||||
mAudioDevice->GetSource()->Stop(source, kAudioTrack);
|
||||
mAudioDevice->GetSource()->Deallocate();
|
||||
}
|
||||
if (mVideoDevice) {
|
||||
mVideoDevice->GetSource()->Stop(source, kVideoTrack);
|
||||
mVideoDevice->GetSource()->Deallocate();
|
||||
}
|
||||
// Do this after stopping all tracks with EndTrack()
|
||||
if (mBool) {
|
||||
source->Finish();
|
||||
}
|
||||
|
||||
nsIRunnable *event =
|
||||
new GetUserMediaNotificationEvent(mListener,
|
||||
mType == MEDIA_STOP ?
|
||||
GetUserMediaNotificationEvent::STOPPING :
|
||||
GetUserMediaNotificationEvent::STOPPED_TRACK,
|
||||
mAudioDevice != nullptr,
|
||||
mVideoDevice != nullptr,
|
||||
mWindowID);
|
||||
// event must always be released on mainthread due to the JS callbacks
|
||||
// in the TracksAvailableCallback
|
||||
NS_DispatchToMainThread(event);
|
||||
}
|
||||
break;
|
||||
|
||||
case MEDIA_DIRECT_LISTENERS:
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Never call on main thread");
|
||||
if (mVideoDevice) {
|
||||
mVideoDevice->GetSource()->SetDirectListeners(mBool);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_ASSERT(false,"invalid MediaManager operation");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
MediaOperation mType;
|
||||
nsRefPtr<DOMMediaStream> mStream;
|
||||
nsAutoPtr<DOMMediaStream::OnTracksAvailableCallback> mOnTracksAvailableCallback;
|
||||
nsRefPtr<AudioDevice> mAudioDevice; // threadsafe
|
||||
nsRefPtr<VideoDevice> mVideoDevice; // threadsafe
|
||||
nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener; // threadsafe
|
||||
bool mBool;
|
||||
uint64_t mWindowID;
|
||||
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure;
|
||||
dom::MediaTrackConstraints mConstraints;
|
||||
};
|
||||
|
||||
/**
|
||||
* Send an error back to content.
|
||||
* Do this only on the main thread. The onSuccess callback is also passed here
|
||||
@ -470,6 +617,16 @@ nsresult AudioDevice::Allocate(const dom::MediaTrackConstraints &aConstraints,
|
||||
return GetSource()->Allocate(aConstraints, aPrefs, mID);
|
||||
}
|
||||
|
||||
nsresult VideoDevice::Restart(const dom::MediaTrackConstraints &aConstraints,
|
||||
const MediaEnginePrefs &aPrefs) {
|
||||
return GetSource()->Restart(aConstraints, aPrefs, mID);
|
||||
}
|
||||
|
||||
nsresult AudioDevice::Restart(const dom::MediaTrackConstraints &aConstraints,
|
||||
const MediaEnginePrefs &aPrefs) {
|
||||
return GetSource()->Restart(aConstraints, aPrefs, mID);
|
||||
}
|
||||
|
||||
/**
|
||||
* A subclass that we only use to stash internal pointers to MediaStreamGraph objects
|
||||
* that need to be cleaned up.
|
||||
@ -480,23 +637,23 @@ public:
|
||||
static already_AddRefed<nsDOMUserMediaStream>
|
||||
CreateTrackUnionStream(nsIDOMWindow* aWindow,
|
||||
GetUserMediaCallbackMediaStreamListener* aListener,
|
||||
MediaEngineSource* aAudioSource,
|
||||
MediaEngineSource* aVideoSource,
|
||||
AudioDevice* aAudioDevice,
|
||||
VideoDevice* aVideoDevice,
|
||||
MediaStreamGraph* aMSG)
|
||||
{
|
||||
nsRefPtr<nsDOMUserMediaStream> stream = new nsDOMUserMediaStream(aListener,
|
||||
aAudioSource,
|
||||
aVideoSource);
|
||||
aAudioDevice,
|
||||
aVideoDevice);
|
||||
stream->InitTrackUnionStream(aWindow, aMSG);
|
||||
return stream.forget();
|
||||
}
|
||||
|
||||
nsDOMUserMediaStream(GetUserMediaCallbackMediaStreamListener* aListener,
|
||||
MediaEngineSource *aAudioSource,
|
||||
MediaEngineSource *aVideoSource) :
|
||||
AudioDevice *aAudioDevice,
|
||||
VideoDevice *aVideoDevice) :
|
||||
mListener(aListener),
|
||||
mAudioSource(aAudioSource),
|
||||
mVideoSource(aVideoSource),
|
||||
mAudioDevice(aAudioDevice),
|
||||
mVideoDevice(aVideoDevice),
|
||||
mEchoOn(true),
|
||||
mAgcOn(false),
|
||||
mNoiseOn(true),
|
||||
@ -543,13 +700,60 @@ public:
|
||||
// risky to do late in a release since that will affect all track ends, and not
|
||||
// just StopTrack()s.
|
||||
if (GetDOMTrackFor(aTrackID)) {
|
||||
mListener->StopTrack(aTrackID, !!GetDOMTrackFor(aTrackID)->AsAudioStreamTrack());
|
||||
mListener->StopTrack(aTrackID,
|
||||
!!GetDOMTrackFor(aTrackID)->AsAudioStreamTrack());
|
||||
} else {
|
||||
LOG(("StopTrack(%d) on non-existant track", aTrackID));
|
||||
LOG(("StopTrack(%d) on non-existent track", aTrackID));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual already_AddRefed<Promise>
|
||||
ApplyConstraintsToTrack(TrackID aTrackID,
|
||||
const MediaTrackConstraints& aConstraints,
|
||||
ErrorResult &aRv) override
|
||||
{
|
||||
nsPIDOMWindow* window = static_cast<nsPIDOMWindow*>(mWindow.get());
|
||||
nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(window);
|
||||
nsRefPtr<Promise> promise = Promise::Create(go, aRv);
|
||||
|
||||
if (sInShutdown) {
|
||||
nsRefPtr<MediaStreamError> error = new MediaStreamError(window,
|
||||
NS_LITERAL_STRING("AbortError"),
|
||||
NS_LITERAL_STRING("In shutdown"));
|
||||
promise->MaybeReject(error);
|
||||
return promise.forget();
|
||||
}
|
||||
if (!mSourceStream) {
|
||||
nsRefPtr<MediaStreamError> error = new MediaStreamError(window,
|
||||
NS_LITERAL_STRING("InternalError"),
|
||||
NS_LITERAL_STRING("No stream."));
|
||||
promise->MaybeReject(error);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
nsRefPtr<dom::MediaStreamTrack> track = GetDOMTrackFor(aTrackID);
|
||||
if (!track) {
|
||||
LOG(("ApplyConstraintsToTrack(%d) on non-existent track", aTrackID));
|
||||
nsRefPtr<MediaStreamError> error = new MediaStreamError(window,
|
||||
NS_LITERAL_STRING("InternalError"),
|
||||
NS_LITERAL_STRING("No track."));
|
||||
promise->MaybeReject(error);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
typedef media::Pledge<bool, MediaStreamError*> PledgeVoid;
|
||||
|
||||
nsRefPtr<PledgeVoid> p = mListener->ApplyConstraintsToTrack(window,
|
||||
aTrackID, !!track->AsAudioStreamTrack(), aConstraints);
|
||||
p->Then([promise](bool& aDummy) mutable {
|
||||
promise->MaybeResolve(false);
|
||||
}, [promise](MediaStreamError*& reason) mutable {
|
||||
promise->MaybeReject(reason);
|
||||
});
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
#if 0
|
||||
virtual void NotifyMediaStreamTrackEnded(dom::MediaStreamTrack* aTrack)
|
||||
{
|
||||
@ -598,14 +802,14 @@ public:
|
||||
}
|
||||
|
||||
// let us intervene for direct listeners when someone does track.enabled = false
|
||||
virtual void SetTrackEnabled(TrackID aID, bool aEnabled) override
|
||||
virtual void SetTrackEnabled(TrackID aTrackID, bool aEnabled) override
|
||||
{
|
||||
// We encapsulate the SourceMediaStream and TrackUnion into one entity, so
|
||||
// we can handle the disabling at the SourceMediaStream
|
||||
|
||||
// We need to find the input track ID for output ID aID, so we let the TrackUnion
|
||||
// We need to find the input track ID for output ID aTrackID, so we let the TrackUnion
|
||||
// forward the request to the source and translate the ID
|
||||
GetStream()->AsProcessedStream()->ForwardTrackEnabled(aID, aEnabled);
|
||||
GetStream()->AsProcessedStream()->ForwardTrackEnabled(aTrackID, aEnabled);
|
||||
}
|
||||
|
||||
virtual DOMLocalMediaStream* AsDOMLocalMediaStream() override
|
||||
@ -618,10 +822,10 @@ public:
|
||||
// MediaEngine supports only one video and on video track now and TrackID is
|
||||
// fixed in MediaEngine.
|
||||
if (aTrackID == kVideoTrack) {
|
||||
return mVideoSource;
|
||||
return mVideoDevice ? mVideoDevice->GetSource() : nullptr;
|
||||
}
|
||||
else if (aTrackID == kAudioTrack) {
|
||||
return mAudioSource;
|
||||
return mAudioDevice ? mAudioDevice->GetSource() : nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
@ -632,8 +836,8 @@ public:
|
||||
nsRefPtr<SourceMediaStream> mSourceStream;
|
||||
nsRefPtr<MediaInputPort> mPort;
|
||||
nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener;
|
||||
nsRefPtr<MediaEngineSource> mAudioSource; // so we can turn on AEC
|
||||
nsRefPtr<MediaEngineSource> mVideoSource;
|
||||
nsRefPtr<AudioDevice> mAudioDevice; // so we can turn on AEC
|
||||
nsRefPtr<VideoDevice> mVideoDevice;
|
||||
bool mEchoOn;
|
||||
bool mAgcOn;
|
||||
bool mNoiseOn;
|
||||
@ -686,11 +890,11 @@ public:
|
||||
uint64_t aWindowID,
|
||||
GetUserMediaCallbackMediaStreamListener* aListener,
|
||||
const nsCString& aOrigin,
|
||||
MediaEngineSource* aAudioSource,
|
||||
MediaEngineSource* aVideoSource,
|
||||
AudioDevice* aAudioDevice,
|
||||
VideoDevice* aVideoDevice,
|
||||
PeerIdentity* aPeerIdentity)
|
||||
: mAudioSource(aAudioSource)
|
||||
, mVideoSource(aVideoSource)
|
||||
: mAudioDevice(aAudioDevice)
|
||||
, mVideoDevice(aVideoDevice)
|
||||
, mWindowID(aWindowID)
|
||||
, mListener(aListener)
|
||||
, mOrigin(aOrigin)
|
||||
@ -787,7 +991,7 @@ public:
|
||||
#endif
|
||||
|
||||
MediaStreamGraph::GraphDriverType graphDriverType =
|
||||
mAudioSource ? MediaStreamGraph::AUDIO_THREAD_DRIVER
|
||||
mAudioDevice ? MediaStreamGraph::AUDIO_THREAD_DRIVER
|
||||
: MediaStreamGraph::SYSTEM_THREAD_DRIVER;
|
||||
MediaStreamGraph* msg =
|
||||
MediaStreamGraph::GetInstance(graphDriverType,
|
||||
@ -800,8 +1004,8 @@ public:
|
||||
// using the audio source and the SourceMediaStream, which acts as
|
||||
// placeholders. We re-route a number of stream internaly in the MSG and mix
|
||||
// them down instead.
|
||||
if (mAudioSource &&
|
||||
mAudioSource->GetMediaSource() == dom::MediaSourceEnum::AudioCapture) {
|
||||
if (mAudioDevice &&
|
||||
mAudioDevice->GetMediaSource() == dom::MediaSourceEnum::AudioCapture) {
|
||||
domStream = DOMLocalMediaStream::CreateAudioCaptureStream(window, msg);
|
||||
// It should be possible to pipe the capture stream to anything. CORS is
|
||||
// not a problem here, we got explicit user content.
|
||||
@ -814,7 +1018,7 @@ public:
|
||||
// avoid us blocking
|
||||
nsRefPtr<nsDOMUserMediaStream> trackunion =
|
||||
nsDOMUserMediaStream::CreateTrackUnionStream(window, mListener,
|
||||
mAudioSource, mVideoSource,
|
||||
mAudioDevice, mVideoDevice,
|
||||
msg);
|
||||
trackunion->GetStream()->AsProcessedStream()->SetAutofinish(true);
|
||||
nsRefPtr<MediaInputPort> port = trackunion->GetStream()->AsProcessedStream()->
|
||||
@ -859,7 +1063,7 @@ public:
|
||||
// Activate our listener. We'll call Start() on the source when get a callback
|
||||
// that the MediaStream has started consuming. The listener is freed
|
||||
// when the page is invalidated (on navigation or close).
|
||||
mListener->Activate(stream.forget(), mAudioSource, mVideoSource);
|
||||
mListener->Activate(stream.forget(), mAudioDevice, mVideoDevice);
|
||||
|
||||
// Note: includes JS callbacks; must be released on MainThread
|
||||
TracksAvailableCallback* tracksAvailableCallback =
|
||||
@ -874,11 +1078,11 @@ public:
|
||||
// because that can take a while.
|
||||
// Pass ownership of trackunion to the MediaOperationTask
|
||||
// to ensure it's kept alive until the MediaOperationTask runs (at least).
|
||||
MediaManager::PostTask(
|
||||
FROM_HERE, new MediaOperationTask(MEDIA_START, mListener, domStream,
|
||||
tracksAvailableCallback, mAudioSource,
|
||||
mVideoSource, false, mWindowID,
|
||||
mOnFailure.forget()));
|
||||
MediaManager::PostTask(FROM_HERE,
|
||||
new MediaOperationTask(MEDIA_START, mListener, domStream,
|
||||
tracksAvailableCallback,
|
||||
mAudioDevice, mVideoDevice,
|
||||
false, mWindowID, mOnFailure.forget()));
|
||||
// We won't need mOnFailure now.
|
||||
mOnFailure = nullptr;
|
||||
|
||||
@ -893,8 +1097,8 @@ public:
|
||||
private:
|
||||
nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> mOnSuccess;
|
||||
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure;
|
||||
nsRefPtr<MediaEngineSource> mAudioSource;
|
||||
nsRefPtr<MediaEngineSource> mVideoSource;
|
||||
nsRefPtr<AudioDevice> mAudioDevice;
|
||||
nsRefPtr<VideoDevice> mVideoDevice;
|
||||
uint64_t mWindowID;
|
||||
nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener;
|
||||
nsCString mOrigin;
|
||||
@ -949,127 +1153,6 @@ GetSources(MediaEngine *engine, dom::MediaSourceEnum aSrcType,
|
||||
}
|
||||
}
|
||||
|
||||
template<class DeviceType>
|
||||
static bool
|
||||
AreUnfitSettings(const MediaTrackConstraints &aConstraints,
|
||||
nsTArray<nsRefPtr<DeviceType>>& aSources)
|
||||
{
|
||||
nsTArray<const MediaTrackConstraintSet*> aggregateConstraints;
|
||||
aggregateConstraints.AppendElement(&aConstraints);
|
||||
|
||||
for (auto& source : aSources) {
|
||||
if (source->GetBestFitnessDistance(aggregateConstraints) != UINT32_MAX) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Apply constrains to a supplied list of sources (removes items from the list)
|
||||
|
||||
template<class DeviceType>
|
||||
static const char*
|
||||
SelectSettings(const MediaTrackConstraints &aConstraints,
|
||||
nsTArray<nsRefPtr<DeviceType>>& aSources)
|
||||
{
|
||||
auto& c = aConstraints;
|
||||
|
||||
// First apply top-level constraints.
|
||||
|
||||
// Stack constraintSets that pass, starting with the required one, because the
|
||||
// whole stack must be re-satisfied each time a capability-set is ruled out
|
||||
// (this avoids storing state or pushing algorithm into the lower-level code).
|
||||
nsTArray<nsRefPtr<DeviceType>> unsatisfactory;
|
||||
nsTArray<const MediaTrackConstraintSet*> aggregateConstraints;
|
||||
aggregateConstraints.AppendElement(&c);
|
||||
|
||||
std::multimap<uint32_t, nsRefPtr<DeviceType>> ordered;
|
||||
|
||||
for (uint32_t i = 0; i < aSources.Length();) {
|
||||
uint32_t distance = aSources[i]->GetBestFitnessDistance(aggregateConstraints);
|
||||
if (distance == UINT32_MAX) {
|
||||
unsatisfactory.AppendElement(aSources[i]);
|
||||
aSources.RemoveElementAt(i);
|
||||
} else {
|
||||
ordered.insert(std::pair<uint32_t, nsRefPtr<DeviceType>>(distance,
|
||||
aSources[i]));
|
||||
++i;
|
||||
}
|
||||
}
|
||||
if (!aSources.Length()) {
|
||||
// None selected. The spec says to report a constraint that satisfies NONE
|
||||
// of the sources. Unfortunately, this is a bit laborious to find out, and
|
||||
// requires updating as new constraints are added!
|
||||
|
||||
if (c.mDeviceId.IsConstrainDOMStringParameters()) {
|
||||
MediaTrackConstraints fresh;
|
||||
fresh.mDeviceId = c.mDeviceId;
|
||||
if (AreUnfitSettings(fresh, unsatisfactory)) {
|
||||
return "deviceId";
|
||||
}
|
||||
}
|
||||
if (c.mWidth.IsConstrainLongRange()) {
|
||||
MediaTrackConstraints fresh;
|
||||
fresh.mWidth = c.mWidth;
|
||||
if (AreUnfitSettings(fresh, unsatisfactory)) {
|
||||
return "width";
|
||||
}
|
||||
}
|
||||
if (c.mHeight.IsConstrainLongRange()) {
|
||||
MediaTrackConstraints fresh;
|
||||
fresh.mHeight = c.mHeight;
|
||||
if (AreUnfitSettings(fresh, unsatisfactory)) {
|
||||
return "height";
|
||||
}
|
||||
}
|
||||
if (c.mFrameRate.IsConstrainDoubleRange()) {
|
||||
MediaTrackConstraints fresh;
|
||||
fresh.mFrameRate = c.mFrameRate;
|
||||
if (AreUnfitSettings(fresh, unsatisfactory)) {
|
||||
return "frameRate";
|
||||
}
|
||||
}
|
||||
if (c.mFacingMode.IsConstrainDOMStringParameters()) {
|
||||
MediaTrackConstraints fresh;
|
||||
fresh.mFacingMode = c.mFacingMode;
|
||||
if (AreUnfitSettings(fresh, unsatisfactory)) {
|
||||
return "facingMode";
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// Order devices by shortest distance
|
||||
for (auto& ordinal : ordered) {
|
||||
aSources.RemoveElement(ordinal.second);
|
||||
aSources.AppendElement(ordinal.second);
|
||||
}
|
||||
|
||||
// Then apply advanced constraints.
|
||||
|
||||
if (c.mAdvanced.WasPassed()) {
|
||||
auto &array = c.mAdvanced.Value();
|
||||
|
||||
for (int i = 0; i < int(array.Length()); i++) {
|
||||
aggregateConstraints.AppendElement(&array[i]);
|
||||
nsTArray<nsRefPtr<DeviceType>> rejects;
|
||||
for (uint32_t j = 0; j < aSources.Length();) {
|
||||
if (aSources[j]->GetBestFitnessDistance(aggregateConstraints) == UINT32_MAX) {
|
||||
rejects.AppendElement(aSources[j]);
|
||||
aSources.RemoveElementAt(j);
|
||||
} else {
|
||||
++j;
|
||||
}
|
||||
}
|
||||
if (!aSources.Length()) {
|
||||
aSources.AppendElements(Move(rejects));
|
||||
aggregateConstraints.RemoveElementAt(aggregateConstraints.Length() - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static const char*
|
||||
SelectSettings(MediaStreamConstraints &aConstraints,
|
||||
nsTArray<nsRefPtr<MediaDevice>>& aSources)
|
||||
@ -1096,13 +1179,15 @@ SelectSettings(MediaStreamConstraints &aConstraints,
|
||||
const char* badConstraint = nullptr;
|
||||
|
||||
if (IsOn(aConstraints.mVideo)) {
|
||||
badConstraint = SelectSettings(GetInvariant(aConstraints.mVideo), videos);
|
||||
badConstraint = MediaConstraintsHelper::SelectSettings(
|
||||
GetInvariant(aConstraints.mVideo), videos);
|
||||
for (auto& video : videos) {
|
||||
aSources.AppendElement(video);
|
||||
}
|
||||
}
|
||||
if (audios.Length() && IsOn(aConstraints.mAudio)) {
|
||||
badConstraint = SelectSettings(GetInvariant(aConstraints.mAudio), audios);
|
||||
badConstraint = MediaConstraintsHelper::SelectSettings(
|
||||
GetInvariant(aConstraints.mAudio), audios);
|
||||
for (auto& audio : audios) {
|
||||
aSources.AppendElement(audio);
|
||||
}
|
||||
@ -1200,13 +1285,10 @@ public:
|
||||
peerIdentity = new PeerIdentity(mConstraints.mPeerIdentity);
|
||||
}
|
||||
|
||||
NS_DispatchToMainThread(do_AddRef(new GetUserMediaStreamRunnable(
|
||||
mOnSuccess, mOnFailure, mWindowID, mListener, mOrigin,
|
||||
(mAudioDevice? mAudioDevice->GetSource() : nullptr),
|
||||
(mVideoDevice? mVideoDevice->GetSource() : nullptr),
|
||||
peerIdentity
|
||||
)));
|
||||
|
||||
NS_DispatchToMainThread(do_AddRef(
|
||||
new GetUserMediaStreamRunnable(mOnSuccess, mOnFailure, mWindowID,
|
||||
mListener, mOrigin, mAudioDevice,
|
||||
mVideoDevice, peerIdentity)));
|
||||
MOZ_ASSERT(!mOnSuccess);
|
||||
MOZ_ASSERT(!mOnFailure);
|
||||
}
|
||||
@ -2031,8 +2113,8 @@ MediaManager::GetUserMedia(nsPIDOMWindow* aWindow,
|
||||
#ifdef MOZ_WEBRTC
|
||||
EnableWebRtcLog();
|
||||
#endif
|
||||
}, [onFailure](MediaStreamError& reason) mutable {
|
||||
onFailure->OnError(&reason);
|
||||
}, [onFailure](MediaStreamError*& reason) mutable {
|
||||
onFailure->OnError(reason);
|
||||
});
|
||||
return NS_OK;
|
||||
}
|
||||
@ -2212,10 +2294,10 @@ MediaManager::EnumerateDevices(nsPIDOMWindow* aWindow,
|
||||
mgr->RemoveFromWindowList(windowId, listener);
|
||||
nsCOMPtr<nsIWritableVariant> array = MediaManager_ToJSArray(*devices);
|
||||
onSuccess->OnSuccess(array);
|
||||
}, [onFailure, windowId, listener](MediaStreamError& reason) mutable {
|
||||
}, [onFailure, windowId, listener](MediaStreamError*& reason) mutable {
|
||||
nsRefPtr<MediaManager> mgr = MediaManager_GetInstance();
|
||||
mgr->RemoveFromWindowList(windowId, listener);
|
||||
onFailure->OnError(&reason);
|
||||
onFailure->OnError(reason);
|
||||
});
|
||||
return NS_OK;
|
||||
}
|
||||
@ -2927,10 +3009,10 @@ GetUserMediaCallbackMediaStreamListener::AudioConfig(bool aEchoOn,
|
||||
bool aNoiseOn, uint32_t aNoise,
|
||||
int32_t aPlayoutDelay)
|
||||
{
|
||||
if (mAudioSource) {
|
||||
if (mAudioDevice) {
|
||||
#ifdef MOZ_WEBRTC
|
||||
MediaManager::PostTask(FROM_HERE,
|
||||
NewRunnableMethod(mAudioSource.get(), &MediaEngineSource::Config,
|
||||
NewRunnableMethod(mAudioDevice->GetSource(), &MediaEngineSource::Config,
|
||||
aEchoOn, aEcho, aAgcOn, aAGC, aNoiseOn,
|
||||
aNoise, aPlayoutDelay));
|
||||
#endif
|
||||
@ -2948,7 +3030,7 @@ GetUserMediaCallbackMediaStreamListener::Invalidate()
|
||||
MediaManager::PostTask(FROM_HERE,
|
||||
new MediaOperationTask(MEDIA_STOP,
|
||||
this, nullptr, nullptr,
|
||||
mAudioSource, mVideoSource,
|
||||
mAudioDevice, mVideoDevice,
|
||||
mFinished, mWindowID, nullptr));
|
||||
}
|
||||
|
||||
@ -2958,18 +3040,18 @@ void
|
||||
GetUserMediaCallbackMediaStreamListener::StopSharing()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
if (mVideoSource && !mStopped &&
|
||||
(mVideoSource->GetMediaSource() == dom::MediaSourceEnum::Screen ||
|
||||
mVideoSource->GetMediaSource() == dom::MediaSourceEnum::Application ||
|
||||
mVideoSource->GetMediaSource() == dom::MediaSourceEnum::Window)) {
|
||||
if (mVideoDevice && !mStopped &&
|
||||
(mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Screen ||
|
||||
mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Application ||
|
||||
mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Window)) {
|
||||
// Stop the whole stream if there's no audio; just the video track if we have both
|
||||
MediaManager::PostTask(FROM_HERE,
|
||||
new MediaOperationTask(mAudioSource ? MEDIA_STOP_TRACK : MEDIA_STOP,
|
||||
new MediaOperationTask(mAudioDevice ? MEDIA_STOP_TRACK : MEDIA_STOP,
|
||||
this, nullptr, nullptr,
|
||||
nullptr, mVideoSource,
|
||||
nullptr, mVideoDevice,
|
||||
mFinished, mWindowID, nullptr));
|
||||
} else if (mAudioSource &&
|
||||
mAudioSource->GetMediaSource() == dom::MediaSourceEnum::AudioCapture) {
|
||||
} else if (mAudioDevice &&
|
||||
mAudioDevice->GetMediaSource() == dom::MediaSourceEnum::AudioCapture) {
|
||||
nsCOMPtr<nsPIDOMWindow> window = nsGlobalWindow::GetInnerWindowWithId(mWindowID);
|
||||
MOZ_ASSERT(window);
|
||||
window->SetAudioCapture(false);
|
||||
@ -2981,25 +3063,119 @@ GetUserMediaCallbackMediaStreamListener::StopSharing()
|
||||
}
|
||||
}
|
||||
|
||||
// ApplyConstraints for track
|
||||
|
||||
already_AddRefed<GetUserMediaCallbackMediaStreamListener::PledgeVoid>
|
||||
GetUserMediaCallbackMediaStreamListener::ApplyConstraintsToTrack(
|
||||
nsPIDOMWindow* aWindow,
|
||||
TrackID aTrackID,
|
||||
bool aIsAudio,
|
||||
const MediaTrackConstraints& aConstraints)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsRefPtr<PledgeVoid> p = new PledgeVoid();
|
||||
|
||||
if (!(((aIsAudio && mAudioDevice) ||
|
||||
(!aIsAudio && mVideoDevice)) && !mStopped))
|
||||
{
|
||||
LOG(("gUM track %d applyConstraints, but we don't have type %s",
|
||||
aTrackID, aIsAudio ? "audio" : "video"));
|
||||
p->Resolve(false);
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
// XXX to support multiple tracks of a type in a stream, this should key off
|
||||
// the TrackID and not just the type
|
||||
nsRefPtr<AudioDevice> audioDevice = aIsAudio ? mAudioDevice.get() : nullptr;
|
||||
nsRefPtr<VideoDevice> videoDevice = !aIsAudio ? mVideoDevice.get() : nullptr;
|
||||
|
||||
nsRefPtr<MediaManager> mgr = MediaManager::GetInstance();
|
||||
uint32_t id = mgr->mOutstandingVoidPledges.Append(*p);
|
||||
uint64_t windowId = aWindow->WindowID();
|
||||
|
||||
MediaManager::PostTask(FROM_HERE, NewTaskFrom([id, windowId,
|
||||
audioDevice, videoDevice,
|
||||
aConstraints]() mutable {
|
||||
MOZ_ASSERT(MediaManager::IsInMediaThread());
|
||||
nsRefPtr<MediaManager> mgr = MediaManager::GetInstance();
|
||||
const char* badConstraint = nullptr;
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
if (audioDevice) {
|
||||
rv = audioDevice->Restart(aConstraints, mgr->mPrefs);
|
||||
if (rv == NS_ERROR_NOT_AVAILABLE) {
|
||||
nsTArray<nsRefPtr<AudioDevice>> audios;
|
||||
audios.AppendElement(audioDevice);
|
||||
badConstraint = MediaConstraintsHelper::SelectSettings(aConstraints,
|
||||
audios);
|
||||
}
|
||||
} else {
|
||||
rv = videoDevice->Restart(aConstraints, mgr->mPrefs);
|
||||
if (rv == NS_ERROR_NOT_AVAILABLE) {
|
||||
nsTArray<nsRefPtr<VideoDevice>> videos;
|
||||
videos.AppendElement(videoDevice);
|
||||
badConstraint = MediaConstraintsHelper::SelectSettings(aConstraints,
|
||||
videos);
|
||||
}
|
||||
}
|
||||
NS_DispatchToMainThread(do_AddRef(NewRunnableFrom([id, windowId, rv,
|
||||
badConstraint]() mutable {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsRefPtr<MediaManager> mgr = MediaManager_GetInstance();
|
||||
if (!mgr) {
|
||||
return NS_OK;
|
||||
}
|
||||
nsRefPtr<PledgeVoid> p = mgr->mOutstandingVoidPledges.Remove(id);
|
||||
if (p) {
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
p->Resolve(false);
|
||||
} else {
|
||||
nsPIDOMWindow *window = static_cast<nsPIDOMWindow*>
|
||||
(nsGlobalWindow::GetInnerWindowWithId(windowId));
|
||||
if (window) {
|
||||
if (rv == NS_ERROR_NOT_AVAILABLE) {
|
||||
nsString constraint;
|
||||
constraint.AssignASCII(badConstraint);
|
||||
nsRefPtr<MediaStreamError> error =
|
||||
new MediaStreamError(window,
|
||||
NS_LITERAL_STRING("OverconstrainedError"),
|
||||
NS_LITERAL_STRING(""),
|
||||
constraint);
|
||||
p->Reject(error);
|
||||
} else {
|
||||
nsRefPtr<MediaStreamError> error =
|
||||
new MediaStreamError(window,
|
||||
NS_LITERAL_STRING("InternalError"));
|
||||
p->Reject(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
})));
|
||||
}));
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
// Stop backend for track
|
||||
|
||||
void
|
||||
GetUserMediaCallbackMediaStreamListener::StopTrack(TrackID aID, bool aIsAudio)
|
||||
GetUserMediaCallbackMediaStreamListener::StopTrack(TrackID aTrackID, bool aIsAudio)
|
||||
{
|
||||
if (((aIsAudio && mAudioSource) ||
|
||||
(!aIsAudio && mVideoSource)) && !mStopped)
|
||||
if (((aIsAudio && mAudioDevice) ||
|
||||
(!aIsAudio && mVideoDevice)) && !mStopped)
|
||||
{
|
||||
// XXX to support multiple tracks of a type in a stream, this should key off
|
||||
// the TrackID and not just the type
|
||||
MediaManager::PostTask(FROM_HERE,
|
||||
new MediaOperationTask(MEDIA_STOP_TRACK,
|
||||
this, nullptr, nullptr,
|
||||
aIsAudio ? mAudioSource.get() : nullptr,
|
||||
!aIsAudio ? mVideoSource.get() : nullptr,
|
||||
aIsAudio ? mAudioDevice.get() : nullptr,
|
||||
!aIsAudio ? mVideoDevice.get() : nullptr,
|
||||
mFinished, mWindowID, nullptr));
|
||||
} else {
|
||||
LOG(("gUM track %d ended, but we don't have type %s",
|
||||
aID, aIsAudio ? "audio" : "video"));
|
||||
aTrackID, aIsAudio ? "audio" : "video"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -3020,7 +3196,7 @@ GetUserMediaCallbackMediaStreamListener::NotifyDirectListeners(MediaStreamGraph*
|
||||
MediaManager::PostTask(FROM_HERE,
|
||||
new MediaOperationTask(MEDIA_DIRECT_LISTENERS,
|
||||
this, nullptr, nullptr,
|
||||
mAudioSource, mVideoSource,
|
||||
mAudioDevice, mVideoDevice,
|
||||
aHasListeners, mWindowID, nullptr));
|
||||
}
|
||||
|
||||
|
@ -47,12 +47,72 @@
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
struct MediaStreamConstraints;
|
||||
struct MediaTrackConstraints;
|
||||
struct MediaTrackConstraintSet;
|
||||
} // namespace dom
|
||||
|
||||
extern PRLogModuleInfo* GetMediaManagerLog();
|
||||
#define MM_LOG(msg) MOZ_LOG(GetMediaManagerLog(), mozilla::LogLevel::Debug, msg)
|
||||
|
||||
class MediaDevice : public nsIMediaDevice
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIMEDIADEVICE
|
||||
|
||||
void SetId(const nsAString& aID);
|
||||
virtual uint32_t GetBestFitnessDistance(
|
||||
const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets);
|
||||
protected:
|
||||
virtual ~MediaDevice() {}
|
||||
explicit MediaDevice(MediaEngineSource* aSource, bool aIsVideo);
|
||||
static uint32_t FitnessDistance(nsString aN,
|
||||
const dom::OwningStringOrStringSequenceOrConstrainDOMStringParameters& aConstraint);
|
||||
private:
|
||||
static bool StringsContain(const dom::OwningStringOrStringSequence& aStrings,
|
||||
nsString aN);
|
||||
static uint32_t FitnessDistance(nsString aN,
|
||||
const dom::ConstrainDOMStringParameters& aParams);
|
||||
protected:
|
||||
nsString mName;
|
||||
nsString mID;
|
||||
dom::MediaSourceEnum mMediaSource;
|
||||
nsRefPtr<MediaEngineSource> mSource;
|
||||
public:
|
||||
dom::MediaSourceEnum GetMediaSource() {
|
||||
return mMediaSource;
|
||||
}
|
||||
bool mIsVideo;
|
||||
};
|
||||
|
||||
class VideoDevice : public MediaDevice
|
||||
{
|
||||
public:
|
||||
typedef MediaEngineVideoSource Source;
|
||||
|
||||
explicit VideoDevice(Source* aSource);
|
||||
NS_IMETHOD GetType(nsAString& aType);
|
||||
Source* GetSource();
|
||||
nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
|
||||
const MediaEnginePrefs &aPrefs);
|
||||
nsresult Restart(const dom::MediaTrackConstraints &aConstraints,
|
||||
const MediaEnginePrefs &aPrefs);
|
||||
};
|
||||
|
||||
class AudioDevice : public MediaDevice
|
||||
{
|
||||
public:
|
||||
typedef MediaEngineAudioSource Source;
|
||||
|
||||
explicit AudioDevice(Source* aSource);
|
||||
NS_IMETHOD GetType(nsAString& aType);
|
||||
Source* GetSource();
|
||||
nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
|
||||
const MediaEnginePrefs &aPrefs);
|
||||
nsresult Restart(const dom::MediaTrackConstraints &aConstraints,
|
||||
const MediaEnginePrefs &aPrefs);
|
||||
};
|
||||
|
||||
/**
|
||||
* This class is an implementation of MediaStreamListener. This is used
|
||||
* to Start() and Stop() the underlying MediaEngineSource when MediaStreams
|
||||
@ -79,13 +139,13 @@ public:
|
||||
}
|
||||
|
||||
void Activate(already_AddRefed<SourceMediaStream> aStream,
|
||||
MediaEngineSource* aAudioSource,
|
||||
MediaEngineSource* aVideoSource)
|
||||
AudioDevice* aAudioDevice,
|
||||
VideoDevice* aVideoDevice)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
mStream = aStream;
|
||||
mAudioSource = aAudioSource;
|
||||
mVideoSource = aVideoSource;
|
||||
mAudioDevice = aAudioDevice;
|
||||
mVideoDevice = aVideoDevice;
|
||||
|
||||
mStream->AddListener(this);
|
||||
}
|
||||
@ -107,46 +167,57 @@ public:
|
||||
|
||||
void StopTrack(TrackID aID, bool aIsAudio);
|
||||
|
||||
// mVideo/AudioSource are set by Activate(), so we assume they're capturing
|
||||
typedef media::Pledge<bool, dom::MediaStreamError*> PledgeVoid;
|
||||
|
||||
already_AddRefed<PledgeVoid>
|
||||
ApplyConstraintsToTrack(nsPIDOMWindow* aWindow,
|
||||
TrackID aID, bool aIsAudio,
|
||||
const dom::MediaTrackConstraints& aConstraints);
|
||||
|
||||
// mVideo/AudioDevice are set by Activate(), so we assume they're capturing
|
||||
// if set and represent a real capture device.
|
||||
bool CapturingVideo()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
return mVideoSource && !mStopped &&
|
||||
mVideoSource->GetMediaSource() == dom::MediaSourceEnum::Camera &&
|
||||
(!mVideoSource->IsFake() ||
|
||||
return mVideoDevice && !mStopped &&
|
||||
mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Camera &&
|
||||
(!mVideoDevice->GetSource()->IsFake() ||
|
||||
Preferences::GetBool("media.navigator.permission.fake"));
|
||||
}
|
||||
bool CapturingAudio()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
return mAudioSource && !mStopped &&
|
||||
(!mAudioSource->IsFake() ||
|
||||
return mAudioDevice && !mStopped &&
|
||||
(!mAudioDevice->GetSource()->IsFake() ||
|
||||
Preferences::GetBool("media.navigator.permission.fake"));
|
||||
}
|
||||
bool CapturingScreen()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
return mVideoSource && !mStopped && !mVideoSource->IsAvailable() &&
|
||||
mVideoSource->GetMediaSource() == dom::MediaSourceEnum::Screen;
|
||||
return mVideoDevice && !mStopped &&
|
||||
!mVideoDevice->GetSource()->IsAvailable() &&
|
||||
mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Screen;
|
||||
}
|
||||
bool CapturingWindow()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
return mVideoSource && !mStopped && !mVideoSource->IsAvailable() &&
|
||||
mVideoSource->GetMediaSource() == dom::MediaSourceEnum::Window;
|
||||
return mVideoDevice && !mStopped &&
|
||||
!mVideoDevice->GetSource()->IsAvailable() &&
|
||||
mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Window;
|
||||
}
|
||||
bool CapturingApplication()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
return mVideoSource && !mStopped && !mVideoSource->IsAvailable() &&
|
||||
mVideoSource->GetMediaSource() == dom::MediaSourceEnum::Application;
|
||||
return mVideoDevice && !mStopped &&
|
||||
!mVideoDevice->GetSource()->IsAvailable() &&
|
||||
mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Application;
|
||||
}
|
||||
bool CapturingBrowser()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
return mVideoSource && !mStopped && mVideoSource->IsAvailable() &&
|
||||
mVideoSource->GetMediaSource() == dom::MediaSourceEnum::Browser;
|
||||
return mVideoDevice && !mStopped &&
|
||||
mVideoDevice->GetSource()->IsAvailable() &&
|
||||
mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Browser;
|
||||
}
|
||||
|
||||
void SetStopped()
|
||||
@ -187,11 +258,13 @@ public:
|
||||
{
|
||||
// Currently audio sources ignore NotifyPull, but they could
|
||||
// watch it especially for fake audio.
|
||||
if (mAudioSource) {
|
||||
mAudioSource->NotifyPull(aGraph, mStream, kAudioTrack, aDesiredTime);
|
||||
if (mAudioDevice) {
|
||||
mAudioDevice->GetSource()->NotifyPull(aGraph, mStream, kAudioTrack,
|
||||
aDesiredTime);
|
||||
}
|
||||
if (mVideoSource) {
|
||||
mVideoSource->NotifyPull(aGraph, mStream, kVideoTrack, aDesiredTime);
|
||||
if (mVideoDevice) {
|
||||
mVideoDevice->GetSource()->NotifyPull(aGraph, mStream, kVideoTrack,
|
||||
aDesiredTime);
|
||||
}
|
||||
}
|
||||
|
||||
@ -237,8 +310,8 @@ private:
|
||||
|
||||
// Accessed from MediaStreamGraph thread, MediaManager thread, and MainThread
|
||||
// No locking needed as they're only addrefed except on the MediaManager thread
|
||||
nsRefPtr<MediaEngineSource> mAudioSource; // threadsafe refcnt
|
||||
nsRefPtr<MediaEngineSource> mVideoSource; // threadsafe refcnt
|
||||
nsRefPtr<AudioDevice> mAudioDevice; // threadsafe refcnt
|
||||
nsRefPtr<VideoDevice> mVideoDevice; // threadsafe refcnt
|
||||
nsRefPtr<SourceMediaStream> mStream; // threadsafe refcnt
|
||||
bool mFinished;
|
||||
|
||||
@ -253,7 +326,7 @@ class GetUserMediaNotificationEvent: public nsRunnable
|
||||
enum GetUserMediaStatus {
|
||||
STARTING,
|
||||
STOPPING,
|
||||
STOPPED_TRACK
|
||||
STOPPED_TRACK,
|
||||
};
|
||||
GetUserMediaNotificationEvent(GetUserMediaCallbackMediaStreamListener* aListener,
|
||||
GetUserMediaStatus aStatus,
|
||||
@ -291,7 +364,7 @@ typedef enum {
|
||||
MEDIA_START,
|
||||
MEDIA_STOP,
|
||||
MEDIA_STOP_TRACK,
|
||||
MEDIA_DIRECT_LISTENERS
|
||||
MEDIA_DIRECT_LISTENERS,
|
||||
} MediaOperation;
|
||||
|
||||
class MediaManager;
|
||||
@ -310,206 +383,9 @@ private:
|
||||
nsAutoPtr<DOMMediaStream::OnTracksAvailableCallback> mOnTracksAvailableCallback;
|
||||
};
|
||||
|
||||
// Generic class for running long media operations like Start off the main
|
||||
// thread, and then (because nsDOMMediaStreams aren't threadsafe),
|
||||
// ProxyReleases mStream since it's cycle collected.
|
||||
class MediaOperationTask : public Task
|
||||
{
|
||||
public:
|
||||
// so we can send Stop without AddRef()ing from the MSG thread
|
||||
MediaOperationTask(MediaOperation aType,
|
||||
GetUserMediaCallbackMediaStreamListener* aListener,
|
||||
DOMMediaStream* aStream,
|
||||
DOMMediaStream::OnTracksAvailableCallback* aOnTracksAvailableCallback,
|
||||
MediaEngineSource* aAudioSource,
|
||||
MediaEngineSource* aVideoSource,
|
||||
bool aBool,
|
||||
uint64_t aWindowID,
|
||||
already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError)
|
||||
: mType(aType)
|
||||
, mStream(aStream)
|
||||
, mOnTracksAvailableCallback(aOnTracksAvailableCallback)
|
||||
, mAudioSource(aAudioSource)
|
||||
, mVideoSource(aVideoSource)
|
||||
, mListener(aListener)
|
||||
, mBool(aBool)
|
||||
, mWindowID(aWindowID)
|
||||
, mOnFailure(aError)
|
||||
{}
|
||||
|
||||
~MediaOperationTask()
|
||||
{
|
||||
// MediaStreams can be released on any thread.
|
||||
}
|
||||
|
||||
void
|
||||
ReturnCallbackError(nsresult rv, const char* errorLog);
|
||||
|
||||
void
|
||||
Run()
|
||||
{
|
||||
SourceMediaStream *source = mListener->GetSourceStream();
|
||||
// No locking between these is required as all the callbacks for the
|
||||
// same MediaStream will occur on the same thread.
|
||||
if (!source) // means the stream was never Activated()
|
||||
return;
|
||||
|
||||
switch (mType) {
|
||||
case MEDIA_START:
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Never call on main thread");
|
||||
nsresult rv;
|
||||
|
||||
if (mAudioSource) {
|
||||
rv = mAudioSource->Start(source, kAudioTrack);
|
||||
if (NS_FAILED(rv)) {
|
||||
ReturnCallbackError(rv, "Starting audio failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (mVideoSource) {
|
||||
rv = mVideoSource->Start(source, kVideoTrack);
|
||||
if (NS_FAILED(rv)) {
|
||||
ReturnCallbackError(rv, "Starting video failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Start() queued the tracks to be added synchronously to avoid races
|
||||
source->FinishAddTracks();
|
||||
|
||||
source->SetPullEnabled(true);
|
||||
source->AdvanceKnownTracksTime(STREAM_TIME_MAX);
|
||||
|
||||
MM_LOG(("started all sources"));
|
||||
// Forward mOnTracksAvailableCallback to GetUserMediaNotificationEvent,
|
||||
// because mOnTracksAvailableCallback needs to be added to mStream
|
||||
// on the main thread.
|
||||
nsIRunnable *event =
|
||||
new GetUserMediaNotificationEvent(GetUserMediaNotificationEvent::STARTING,
|
||||
mStream.forget(),
|
||||
mOnTracksAvailableCallback.forget(),
|
||||
mAudioSource != nullptr,
|
||||
mVideoSource != nullptr,
|
||||
mWindowID, mOnFailure.forget());
|
||||
// event must always be released on mainthread due to the JS callbacks
|
||||
// in the TracksAvailableCallback
|
||||
NS_DispatchToMainThread(event);
|
||||
}
|
||||
break;
|
||||
|
||||
case MEDIA_STOP:
|
||||
case MEDIA_STOP_TRACK:
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Never call on main thread");
|
||||
if (mAudioSource) {
|
||||
mAudioSource->Stop(source, kAudioTrack);
|
||||
mAudioSource->Deallocate();
|
||||
}
|
||||
if (mVideoSource) {
|
||||
mVideoSource->Stop(source, kVideoTrack);
|
||||
mVideoSource->Deallocate();
|
||||
}
|
||||
// Do this after stopping all tracks with EndTrack()
|
||||
if (mBool) {
|
||||
source->Finish();
|
||||
}
|
||||
|
||||
nsIRunnable *event =
|
||||
new GetUserMediaNotificationEvent(mListener,
|
||||
mType == MEDIA_STOP ?
|
||||
GetUserMediaNotificationEvent::STOPPING :
|
||||
GetUserMediaNotificationEvent::STOPPED_TRACK,
|
||||
mAudioSource != nullptr,
|
||||
mVideoSource != nullptr,
|
||||
mWindowID);
|
||||
// event must always be released on mainthread due to the JS callbacks
|
||||
// in the TracksAvailableCallback
|
||||
NS_DispatchToMainThread(event);
|
||||
}
|
||||
break;
|
||||
|
||||
case MEDIA_DIRECT_LISTENERS:
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Never call on main thread");
|
||||
if (mVideoSource) {
|
||||
mVideoSource->SetDirectListeners(mBool);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_ASSERT(false,"invalid MediaManager operation");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
MediaOperation mType;
|
||||
nsRefPtr<DOMMediaStream> mStream;
|
||||
nsAutoPtr<DOMMediaStream::OnTracksAvailableCallback> mOnTracksAvailableCallback;
|
||||
nsRefPtr<MediaEngineSource> mAudioSource; // threadsafe
|
||||
nsRefPtr<MediaEngineSource> mVideoSource; // threadsafe
|
||||
nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener; // threadsafe
|
||||
bool mBool;
|
||||
uint64_t mWindowID;
|
||||
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure;
|
||||
};
|
||||
|
||||
typedef nsTArray<nsRefPtr<GetUserMediaCallbackMediaStreamListener> > StreamListeners;
|
||||
typedef nsClassHashtable<nsUint64HashKey, StreamListeners> WindowTable;
|
||||
|
||||
class MediaDevice : public nsIMediaDevice
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIMEDIADEVICE
|
||||
|
||||
void SetId(const nsAString& aID);
|
||||
virtual uint32_t GetBestFitnessDistance(
|
||||
const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets);
|
||||
protected:
|
||||
virtual ~MediaDevice() {}
|
||||
explicit MediaDevice(MediaEngineSource* aSource, bool aIsVideo);
|
||||
static uint32_t FitnessDistance(nsString aN,
|
||||
const dom::OwningStringOrStringSequenceOrConstrainDOMStringParameters& aConstraint);
|
||||
private:
|
||||
static bool StringsContain(const dom::OwningStringOrStringSequence& aStrings,
|
||||
nsString aN);
|
||||
static uint32_t FitnessDistance(nsString aN,
|
||||
const dom::ConstrainDOMStringParameters& aParams);
|
||||
protected:
|
||||
nsString mName;
|
||||
nsString mID;
|
||||
dom::MediaSourceEnum mMediaSource;
|
||||
nsRefPtr<MediaEngineSource> mSource;
|
||||
public:
|
||||
bool mIsVideo;
|
||||
};
|
||||
|
||||
class VideoDevice : public MediaDevice
|
||||
{
|
||||
public:
|
||||
typedef MediaEngineVideoSource Source;
|
||||
|
||||
explicit VideoDevice(Source* aSource);
|
||||
NS_IMETHOD GetType(nsAString& aType);
|
||||
Source* GetSource();
|
||||
nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
|
||||
const MediaEnginePrefs &aPrefs);
|
||||
};
|
||||
|
||||
class AudioDevice : public MediaDevice
|
||||
{
|
||||
public:
|
||||
typedef MediaEngineAudioSource Source;
|
||||
|
||||
explicit AudioDevice(Source* aSource);
|
||||
NS_IMETHOD GetType(nsAString& aType);
|
||||
Source* GetSource();
|
||||
nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
|
||||
const MediaEnginePrefs &aPrefs);
|
||||
};
|
||||
|
||||
// we could add MediaManager if needed
|
||||
typedef void (*WindowListenerCallback)(MediaManager *aThis,
|
||||
uint64_t aWindowID,
|
||||
@ -519,6 +395,7 @@ typedef void (*WindowListenerCallback)(MediaManager *aThis,
|
||||
class MediaManager final : public nsIMediaManagerService,
|
||||
public nsIObserver
|
||||
{
|
||||
friend GetUserMediaCallbackMediaStreamListener;
|
||||
public:
|
||||
static already_AddRefed<MediaManager> GetInstance();
|
||||
|
||||
@ -586,7 +463,7 @@ public:
|
||||
typedef nsTArray<nsRefPtr<MediaDevice>> SourceSet;
|
||||
static bool IsPrivateBrowsing(nsPIDOMWindow *window);
|
||||
private:
|
||||
typedef media::Pledge<SourceSet*, dom::MediaStreamError> PledgeSourceSet;
|
||||
typedef media::Pledge<SourceSet*, dom::MediaStreamError*> PledgeSourceSet;
|
||||
|
||||
static bool IsPrivileged();
|
||||
static bool IsLoop(nsIURI* aDocURI);
|
||||
@ -646,6 +523,7 @@ private:
|
||||
static StaticRefPtr<MediaManager> sSingleton;
|
||||
|
||||
media::CoatCheck<PledgeSourceSet> mOutstandingPledges;
|
||||
media::CoatCheck<GetUserMediaCallbackMediaStreamListener::PledgeVoid> mOutstandingVoidPledges;
|
||||
#if defined(MOZ_B2G_CAMERA) && defined(MOZ_WIDGET_GONK)
|
||||
nsRefPtr<nsDOMCameraManager> mCameraManager;
|
||||
#endif
|
||||
|
@ -62,5 +62,12 @@ MediaStreamTrack::Stop()
|
||||
mStream->StopTrack(mTrackID);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
MediaStreamTrack::ApplyConstraints(const MediaTrackConstraints& aConstraints,
|
||||
ErrorResult &aRv)
|
||||
{
|
||||
return mStream->ApplyConstraintsToTrack(mTrackID, aConstraints, aRv);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "nsID.h"
|
||||
#include "StreamBuffer.h"
|
||||
#include "MediaTrackConstraints.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -49,6 +50,8 @@ public:
|
||||
bool Enabled() { return mEnabled; }
|
||||
void SetEnabled(bool aEnabled);
|
||||
void Stop();
|
||||
already_AddRefed<Promise>
|
||||
ApplyConstraints(const dom::MediaTrackConstraints& aConstraints, ErrorResult &aRv);
|
||||
|
||||
// Notifications from the MediaStreamGraph
|
||||
void NotifyEnded() { mEnded = true; }
|
||||
|
@ -63,7 +63,7 @@ class Pledge : public PledgeBase
|
||||
};
|
||||
|
||||
public:
|
||||
explicit Pledge() : mDone(false), mError(nullptr) {}
|
||||
explicit Pledge() : mDone(false), mRejected(false) {}
|
||||
Pledge(const Pledge& aOther) = delete;
|
||||
Pledge& operator = (const Pledge&) = delete;
|
||||
|
||||
@ -97,10 +97,10 @@ public:
|
||||
mFunctors = new Functors(aOnSuccess, aOnFailure);
|
||||
|
||||
if (mDone) {
|
||||
if (!mError) {
|
||||
if (!mRejected) {
|
||||
mFunctors->Succeed(mValue);
|
||||
} else {
|
||||
mFunctors->Fail(*mError);
|
||||
mFunctors->Fail(mError);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -110,22 +110,11 @@ public:
|
||||
mValue = aValue;
|
||||
Resolve();
|
||||
}
|
||||
protected:
|
||||
void Resolve()
|
||||
{
|
||||
if (!mDone) {
|
||||
mDone = true;
|
||||
MOZ_ASSERT(!mError);
|
||||
if (mFunctors) {
|
||||
mFunctors->Succeed(mValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Reject(ErrorType rv)
|
||||
{
|
||||
if (!mDone) {
|
||||
mDone = true;
|
||||
mDone = mRejected = true;
|
||||
mError = rv;
|
||||
if (mFunctors) {
|
||||
mFunctors->Fail(mError);
|
||||
@ -133,104 +122,24 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
ValueType mValue;
|
||||
private:
|
||||
~Pledge() {};
|
||||
bool mDone;
|
||||
nsRefPtr<ErrorType> mError;
|
||||
ScopedDeletePtr<FunctorsBase> mFunctors;
|
||||
};
|
||||
|
||||
template<typename ValueType>
|
||||
class Pledge<ValueType, nsresult> : public PledgeBase
|
||||
{
|
||||
// TODO: Remove workaround once mozilla allows std::function from <functional>
|
||||
// wo/std::function support, do template + virtual trick to accept lambdas
|
||||
class FunctorsBase
|
||||
{
|
||||
public:
|
||||
FunctorsBase() {}
|
||||
virtual void Succeed(ValueType& result) = 0;
|
||||
virtual void Fail(nsresult error) = 0;
|
||||
virtual ~FunctorsBase() {};
|
||||
};
|
||||
|
||||
public:
|
||||
explicit Pledge() : mDone(false), mError(NS_OK) {}
|
||||
Pledge(const Pledge& aOther) = delete;
|
||||
Pledge& operator = (const Pledge&) = delete;
|
||||
|
||||
template<typename OnSuccessType>
|
||||
void Then(OnSuccessType aOnSuccess)
|
||||
{
|
||||
Then(aOnSuccess, [](nsresult){});
|
||||
}
|
||||
|
||||
template<typename OnSuccessType, typename OnFailureType>
|
||||
void Then(OnSuccessType aOnSuccess, OnFailureType aOnFailure)
|
||||
{
|
||||
class Functors : public FunctorsBase
|
||||
{
|
||||
public:
|
||||
Functors(OnSuccessType& aOnSuccess, OnFailureType& aOnFailure)
|
||||
: mOnSuccess(aOnSuccess), mOnFailure(aOnFailure) {}
|
||||
|
||||
void Succeed(ValueType& result)
|
||||
{
|
||||
mOnSuccess(result);
|
||||
}
|
||||
void Fail(nsresult rv)
|
||||
{
|
||||
mOnFailure(rv);
|
||||
};
|
||||
|
||||
OnSuccessType mOnSuccess;
|
||||
OnFailureType mOnFailure;
|
||||
};
|
||||
mFunctors = new Functors(aOnSuccess, aOnFailure);
|
||||
|
||||
if (mDone) {
|
||||
if (mError == NS_OK) {
|
||||
mFunctors->Succeed(mValue);
|
||||
} else {
|
||||
mFunctors->Fail(mError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Resolve(const ValueType& aValue)
|
||||
{
|
||||
mValue = aValue;
|
||||
Resolve();
|
||||
}
|
||||
protected:
|
||||
void Resolve()
|
||||
{
|
||||
if (!mDone) {
|
||||
mDone = true;
|
||||
MOZ_ASSERT(mError == NS_OK);
|
||||
MOZ_ASSERT(!mRejected);
|
||||
if (mFunctors) {
|
||||
mFunctors->Succeed(mValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Reject(nsresult error)
|
||||
{
|
||||
if (!mDone) {
|
||||
mDone = true;
|
||||
mError = error;
|
||||
if (mFunctors) {
|
||||
mFunctors->Fail(mError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ValueType mValue;
|
||||
private:
|
||||
~Pledge() {};
|
||||
bool mDone;
|
||||
nsresult mError;
|
||||
bool mRejected;
|
||||
ErrorType mError;
|
||||
ScopedDeletePtr<FunctorsBase> mFunctors;
|
||||
};
|
||||
|
||||
|
@ -234,7 +234,6 @@ function setupEnvironment() {
|
||||
window.finish = () => SimpleTest.finish();
|
||||
SpecialPowers.pushPrefEnv({
|
||||
'set': [
|
||||
['canvas.capturestream.enabled', true],
|
||||
['media.peerconnection.enabled', true],
|
||||
['media.peerconnection.identity.enabled', true],
|
||||
['media.peerconnection.identity.timeout', 120000],
|
||||
|
@ -117,7 +117,12 @@ runTest(function() {
|
||||
is(e.constraint, test.constraint,
|
||||
test.message + " w/correct constraint.");
|
||||
}
|
||||
}), p);
|
||||
}), p)
|
||||
.then(() => navigator.mediaDevices.getUserMedia({video: true, audio: true}))
|
||||
.then(stream => stream.getVideoTracks()[0].applyConstraints({ width: 320 })
|
||||
.then(() => stream.getAudioTracks()[0].applyConstraints({ })))
|
||||
.then(() => ok(true, "applyConstraints code exercised"))
|
||||
// TODO: Test outcome once fake devices support constraints (Bug 1088621)
|
||||
});
|
||||
|
||||
</script>
|
||||
|
@ -111,6 +111,11 @@ public:
|
||||
/* Stop the device and release the corresponding MediaStream */
|
||||
virtual nsresult Stop(SourceMediaStream *aSource, TrackID aID) = 0;
|
||||
|
||||
/* Restart with new capability */
|
||||
virtual nsresult Restart(const dom::MediaTrackConstraints& aConstraints,
|
||||
const MediaEnginePrefs &aPrefs,
|
||||
const nsString& aDeviceId) = 0;
|
||||
|
||||
/* Change device configuration. */
|
||||
virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
|
||||
bool aAgcOn, uint32_t aAGC,
|
||||
|
@ -201,6 +201,14 @@ MediaEngineDefaultVideoSource::Stop(SourceMediaStream *aSource, TrackID aID)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaEngineDefaultVideoSource::Restart(const dom::MediaTrackConstraints& aConstraints,
|
||||
const MediaEnginePrefs &aPrefs,
|
||||
const nsString& aDeviceId)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MediaEngineDefaultVideoSource::Notify(nsITimer* aTimer)
|
||||
{
|
||||
@ -470,6 +478,14 @@ MediaEngineDefaultAudioSource::Stop(SourceMediaStream *aSource, TrackID aID)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaEngineDefaultAudioSource::Restart(const dom::MediaTrackConstraints& aConstraints,
|
||||
const MediaEnginePrefs &aPrefs,
|
||||
const nsString& aDeviceId)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MediaEngineDefaultAudioSource::Notify(nsITimer* aTimer)
|
||||
{
|
||||
|
@ -49,6 +49,9 @@ public:
|
||||
virtual nsresult Deallocate() override;
|
||||
virtual nsresult Start(SourceMediaStream*, TrackID) override;
|
||||
virtual nsresult Stop(SourceMediaStream*, TrackID) override;
|
||||
virtual nsresult Restart(const dom::MediaTrackConstraints& aConstraints,
|
||||
const MediaEnginePrefs &aPrefs,
|
||||
const nsString& aDeviceId) override;
|
||||
virtual void SetDirectListeners(bool aHasDirectListeners) override {};
|
||||
virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
|
||||
bool aAgcOn, uint32_t aAGC,
|
||||
@ -119,6 +122,9 @@ public:
|
||||
virtual nsresult Deallocate() override;
|
||||
virtual nsresult Start(SourceMediaStream*, TrackID) override;
|
||||
virtual nsresult Stop(SourceMediaStream*, TrackID) override;
|
||||
virtual nsresult Restart(const dom::MediaTrackConstraints& aConstraints,
|
||||
const MediaEnginePrefs &aPrefs,
|
||||
const nsString& aDeviceId) override;
|
||||
virtual void SetDirectListeners(bool aHasDirectListeners) override {};
|
||||
virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
|
||||
bool aAgcOn, uint32_t aAGC,
|
||||
|
@ -328,6 +328,14 @@ MediaEngineGonkVideoSource::Stop(SourceMediaStream* aSource, TrackID aID)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaEngineGonkVideoSource::Restart(const dom::MediaTrackConstraints& aConstraints,
|
||||
const MediaEnginePrefs& aPrefs,
|
||||
const nsString& aDeviceId)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization and Shutdown functions for the video source, called by the
|
||||
* constructor and destructor respectively.
|
||||
|
@ -66,6 +66,9 @@ public:
|
||||
virtual nsresult Deallocate() override;
|
||||
virtual nsresult Start(SourceMediaStream* aStream, TrackID aID) override;
|
||||
virtual nsresult Stop(SourceMediaStream* aSource, TrackID aID) override;
|
||||
virtual nsresult Restart(const dom::MediaTrackConstraints& aConstraints,
|
||||
const MediaEnginePrefs &aPrefs,
|
||||
const nsString& aDeviceId) override;
|
||||
virtual void NotifyPull(MediaStreamGraph* aGraph,
|
||||
SourceMediaStream* aSource,
|
||||
TrackID aId,
|
||||
|
@ -212,6 +212,31 @@ MediaEngineRemoteVideoSource::Stop(mozilla::SourceMediaStream* aSource,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaEngineRemoteVideoSource::Restart(const dom::MediaTrackConstraints& aConstraints,
|
||||
const MediaEnginePrefs& aPrefs,
|
||||
const nsString& aDeviceId)
|
||||
{
|
||||
if (!mInitDone) {
|
||||
LOG(("Init not done"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (!ChooseCapability(aConstraints, aPrefs, aDeviceId)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
if (mState != kStarted) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mozilla::camera::StopCapture(mCapEngine, mCaptureIndex);
|
||||
if (mozilla::camera::StartCapture(mCapEngine,
|
||||
mCaptureIndex, mCapability, this)) {
|
||||
LOG(("StartCapture failed"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
MediaEngineRemoteVideoSource::NotifyPull(MediaStreamGraph* aGraph,
|
||||
SourceMediaStream* aSource,
|
||||
|
@ -71,6 +71,9 @@ public:
|
||||
virtual nsresult Deallocate() override;;
|
||||
virtual nsresult Start(SourceMediaStream*, TrackID) override;
|
||||
virtual nsresult Stop(SourceMediaStream*, TrackID) override;
|
||||
virtual nsresult Restart(const dom::MediaTrackConstraints& aConstraints,
|
||||
const MediaEnginePrefs &aPrefs,
|
||||
const nsString& aDeviceId) override;
|
||||
virtual void NotifyPull(MediaStreamGraph* aGraph,
|
||||
SourceMediaStream* aSource,
|
||||
TrackID aId,
|
||||
|
@ -291,6 +291,15 @@ MediaEngineTabVideoSource::Stop(mozilla::SourceMediaStream*, mozilla::TrackID)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaEngineTabVideoSource::Restart(const dom::MediaTrackConstraints& aConstraints,
|
||||
const mozilla::MediaEnginePrefs& aPrefs,
|
||||
const nsString& aDeviceId)
|
||||
{
|
||||
// TODO
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaEngineTabVideoSource::Config(bool, uint32_t, bool, uint32_t, bool, uint32_t, int32_t)
|
||||
{
|
||||
|
@ -29,6 +29,9 @@ class MediaEngineTabVideoSource : public MediaEngineVideoSource, nsIDOMEventList
|
||||
virtual void SetDirectListeners(bool aHasDirectListeners) override {};
|
||||
virtual void NotifyPull(mozilla::MediaStreamGraph*, mozilla::SourceMediaStream*, mozilla::TrackID, mozilla::StreamTime) override;
|
||||
virtual nsresult Stop(mozilla::SourceMediaStream*, mozilla::TrackID) override;
|
||||
virtual nsresult Restart(const dom::MediaTrackConstraints& aConstraints,
|
||||
const mozilla::MediaEnginePrefs& aPrefs,
|
||||
const nsString& aDeviceId) override;
|
||||
virtual nsresult Config(bool, uint32_t, bool, uint32_t, bool, uint32_t, int32_t) override;
|
||||
virtual bool IsFake() override;
|
||||
virtual const dom::MediaSourceEnum GetMediaSource() override {
|
||||
|
@ -84,6 +84,9 @@ public:
|
||||
}
|
||||
nsresult Start(SourceMediaStream* aMediaStream, TrackID aId) override;
|
||||
nsresult Stop(SourceMediaStream* aMediaStream, TrackID aId) override;
|
||||
nsresult Restart(const dom::MediaTrackConstraints& aConstraints,
|
||||
const MediaEnginePrefs &aPrefs,
|
||||
const nsString& aDeviceId) override;
|
||||
void SetDirectListeners(bool aDirect) override
|
||||
{}
|
||||
nsresult Config(bool aEchoOn, uint32_t aEcho, bool aAgcOn,
|
||||
@ -155,6 +158,9 @@ public:
|
||||
virtual nsresult Deallocate() override;
|
||||
virtual nsresult Start(SourceMediaStream* aStream, TrackID aID) override;
|
||||
virtual nsresult Stop(SourceMediaStream* aSource, TrackID aID) override;
|
||||
virtual nsresult Restart(const dom::MediaTrackConstraints& aConstraints,
|
||||
const MediaEnginePrefs &aPrefs,
|
||||
const nsString& aDeviceId) override;
|
||||
virtual void SetDirectListeners(bool aHasDirectListeners) override {};
|
||||
virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
|
||||
bool aAgcOn, uint32_t aAGC,
|
||||
|
@ -422,6 +422,14 @@ MediaEngineWebRTCMicrophoneSource::Stop(SourceMediaStream *aSource, TrackID aID)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaEngineWebRTCMicrophoneSource::Restart(const dom::MediaTrackConstraints& aConstraints,
|
||||
const MediaEnginePrefs &aPrefs,
|
||||
const nsString& aDeviceId)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
MediaEngineWebRTCMicrophoneSource::NotifyPull(MediaStreamGraph *aGraph,
|
||||
SourceMediaStream *aSource,
|
||||
@ -664,6 +672,15 @@ MediaEngineWebRTCAudioCaptureSource::Stop(SourceMediaStream *aMediaStream,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaEngineWebRTCAudioCaptureSource::Restart(
|
||||
const dom::MediaTrackConstraints& aConstraints,
|
||||
const MediaEnginePrefs &aPrefs,
|
||||
const nsString& aDeviceId)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
MediaEngineWebRTCAudioCaptureSource::GetBestFitnessDistance(
|
||||
const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets,
|
||||
|
@ -12,6 +12,8 @@
|
||||
#include "mozilla/dom/MediaTrackConstraintSetBinding.h"
|
||||
#include "mozilla/dom/MediaTrackSupportedConstraintsBinding.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
template<class EnumValuesStrings, class Enum>
|
||||
@ -104,6 +106,128 @@ protected:
|
||||
GetMinimumFitnessDistance(const dom::MediaTrackConstraintSet &aConstraints,
|
||||
bool aAdvanced,
|
||||
const nsString& aDeviceId);
|
||||
|
||||
template<class DeviceType>
|
||||
static bool
|
||||
AreUnfitSettings(const dom::MediaTrackConstraints &aConstraints,
|
||||
nsTArray<nsRefPtr<DeviceType>>& aSources)
|
||||
{
|
||||
nsTArray<const dom::MediaTrackConstraintSet*> aggregateConstraints;
|
||||
aggregateConstraints.AppendElement(&aConstraints);
|
||||
|
||||
for (auto& source : aSources) {
|
||||
if (source->GetBestFitnessDistance(aggregateConstraints) != UINT32_MAX) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
// Apply constrains to a supplied list of sources (removes items from the list)
|
||||
|
||||
template<class DeviceType>
|
||||
static const char*
|
||||
SelectSettings(const dom::MediaTrackConstraints &aConstraints,
|
||||
nsTArray<nsRefPtr<DeviceType>>& aSources)
|
||||
{
|
||||
auto& c = aConstraints;
|
||||
|
||||
// First apply top-level constraints.
|
||||
|
||||
// Stack constraintSets that pass, starting with the required one, because the
|
||||
// whole stack must be re-satisfied each time a capability-set is ruled out
|
||||
// (this avoids storing state or pushing algorithm into the lower-level code).
|
||||
nsTArray<nsRefPtr<DeviceType>> unsatisfactory;
|
||||
nsTArray<const dom::MediaTrackConstraintSet*> aggregateConstraints;
|
||||
aggregateConstraints.AppendElement(&c);
|
||||
|
||||
std::multimap<uint32_t, nsRefPtr<DeviceType>> ordered;
|
||||
|
||||
for (uint32_t i = 0; i < aSources.Length();) {
|
||||
uint32_t distance = aSources[i]->GetBestFitnessDistance(aggregateConstraints);
|
||||
if (distance == UINT32_MAX) {
|
||||
unsatisfactory.AppendElement(aSources[i]);
|
||||
aSources.RemoveElementAt(i);
|
||||
} else {
|
||||
ordered.insert(std::pair<uint32_t, nsRefPtr<DeviceType>>(distance,
|
||||
aSources[i]));
|
||||
++i;
|
||||
}
|
||||
}
|
||||
if (!aSources.Length()) {
|
||||
// None selected. The spec says to report a constraint that satisfies NONE
|
||||
// of the sources. Unfortunately, this is a bit laborious to find out, and
|
||||
// requires updating as new constraints are added!
|
||||
|
||||
if (c.mDeviceId.IsConstrainDOMStringParameters()) {
|
||||
dom::MediaTrackConstraints fresh;
|
||||
fresh.mDeviceId = c.mDeviceId;
|
||||
if (AreUnfitSettings(fresh, unsatisfactory)) {
|
||||
return "deviceId";
|
||||
}
|
||||
}
|
||||
if (c.mWidth.IsConstrainLongRange()) {
|
||||
dom::MediaTrackConstraints fresh;
|
||||
fresh.mWidth = c.mWidth;
|
||||
if (AreUnfitSettings(fresh, unsatisfactory)) {
|
||||
return "width";
|
||||
}
|
||||
}
|
||||
if (c.mHeight.IsConstrainLongRange()) {
|
||||
dom::MediaTrackConstraints fresh;
|
||||
fresh.mHeight = c.mHeight;
|
||||
if (AreUnfitSettings(fresh, unsatisfactory)) {
|
||||
return "height";
|
||||
}
|
||||
}
|
||||
if (c.mFrameRate.IsConstrainDoubleRange()) {
|
||||
dom::MediaTrackConstraints fresh;
|
||||
fresh.mFrameRate = c.mFrameRate;
|
||||
if (AreUnfitSettings(fresh, unsatisfactory)) {
|
||||
return "frameRate";
|
||||
}
|
||||
}
|
||||
if (c.mFacingMode.IsConstrainDOMStringParameters()) {
|
||||
dom::MediaTrackConstraints fresh;
|
||||
fresh.mFacingMode = c.mFacingMode;
|
||||
if (AreUnfitSettings(fresh, unsatisfactory)) {
|
||||
return "facingMode";
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// Order devices by shortest distance
|
||||
for (auto& ordinal : ordered) {
|
||||
aSources.RemoveElement(ordinal.second);
|
||||
aSources.AppendElement(ordinal.second);
|
||||
}
|
||||
|
||||
// Then apply advanced constraints.
|
||||
|
||||
if (c.mAdvanced.WasPassed()) {
|
||||
auto &array = c.mAdvanced.Value();
|
||||
|
||||
for (int i = 0; i < int(array.Length()); i++) {
|
||||
aggregateConstraints.AppendElement(&array[i]);
|
||||
nsTArray<nsRefPtr<DeviceType>> rejects;
|
||||
for (uint32_t j = 0; j < aSources.Length();) {
|
||||
if (aSources[j]->GetBestFitnessDistance(aggregateConstraints) == UINT32_MAX) {
|
||||
rejects.AppendElement(aSources[j]);
|
||||
aSources.RemoveElementAt(j);
|
||||
} else {
|
||||
++j;
|
||||
}
|
||||
}
|
||||
if (!aSources.Length()) {
|
||||
aSources.AppendElements(Move(rejects));
|
||||
aggregateConstraints.RemoveElementAt(aggregateConstraints.Length() - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -41,6 +41,12 @@ parent:
|
||||
// |binaryType| (from TCPOption.binaryType).
|
||||
Open(nsString host, uint16_t port, bool useSSL, bool useArrayBuffers);
|
||||
|
||||
// Ask parent to open a socket and bind the newly-opened socket to a local
|
||||
// address specified in |localAddr| and |localPort|.
|
||||
OpenBind(nsCString host, uint16_t port,
|
||||
nsCString localAddr, uint16_t localPort,
|
||||
bool useSSL, bool aUseArrayBuffers);
|
||||
|
||||
// When child's send() is called, this message requrests parent to send
|
||||
// data and update it's trackingNumber.
|
||||
Data(SendableData data, uint32_t trackingNumber);
|
||||
|
@ -143,6 +143,7 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TCPSocket)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIObserver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
||||
NS_INTERFACE_MAP_ENTRY(nsITCPSocketCallback)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
TCPSocket::TCPSocket(nsIGlobalObject* aGlobal, const nsAString& aHost, uint16_t aPort,
|
||||
@ -167,12 +168,14 @@ TCPSocket::TCPSocket(nsIGlobalObject* aGlobal, const nsAString& aHost, uint16_t
|
||||
, mInBrowser(false)
|
||||
#endif
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal);
|
||||
if (window && window->IsOuterWindow()) {
|
||||
window = window->GetCurrentInnerWindow();
|
||||
}
|
||||
if (window) {
|
||||
mInnerWindowID = window->WindowID();
|
||||
if (aGlobal) {
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal);
|
||||
if (window && window->IsOuterWindow()) {
|
||||
window = window->GetCurrentInnerWindow();
|
||||
}
|
||||
if (window) {
|
||||
mInnerWindowID = window->WindowID();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -234,6 +237,24 @@ TCPSocket::CreateStream()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
TCPSocket::InitWithUnconnectedTransport(nsISocketTransport* aTransport)
|
||||
{
|
||||
mReadyState = TCPReadyState::Connecting;
|
||||
mTransport = aTransport;
|
||||
|
||||
MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Content);
|
||||
|
||||
nsCOMPtr<nsIThread> mainThread;
|
||||
NS_GetMainThread(getter_AddRefs(mainThread));
|
||||
mTransport->SetEventSink(this, mainThread);
|
||||
|
||||
nsresult rv = CreateStream();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
TCPSocket::Init()
|
||||
{
|
||||
@ -242,9 +263,8 @@ TCPSocket::Init()
|
||||
obs->AddObserver(this, "inner-window-destroyed", true);
|
||||
}
|
||||
|
||||
mReadyState = TCPReadyState::Connecting;
|
||||
|
||||
if (XRE_GetProcessType() == GeckoProcessType_Content) {
|
||||
mReadyState = TCPReadyState::Connecting;
|
||||
mSocketBridgeChild = new TCPSocketChild(mHost, mPort);
|
||||
mSocketBridgeChild->SendOpen(this, mSsl, mUseArrayBuffers);
|
||||
return NS_OK;
|
||||
@ -259,19 +279,12 @@ TCPSocket::Init()
|
||||
} else {
|
||||
socketTypes[0] = "starttls";
|
||||
}
|
||||
nsCOMPtr<nsISocketTransport> transport;
|
||||
nsresult rv = sts->CreateTransport(socketTypes, 1, NS_ConvertUTF16toUTF8(mHost), mPort,
|
||||
nullptr, getter_AddRefs(mTransport));
|
||||
nullptr, getter_AddRefs(transport));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIThread> mainThread;
|
||||
NS_GetMainThread(getter_AddRefs(mainThread));
|
||||
|
||||
mTransport->SetEventSink(this, mainThread);
|
||||
|
||||
rv = CreateStream();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
return InitWithUnconnectedTransport(transport);
|
||||
}
|
||||
|
||||
void
|
||||
@ -448,12 +461,12 @@ TCPSocket::ActivateTLS()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NS_IMETHODIMP
|
||||
TCPSocket::FireErrorEvent(const nsAString& aName, const nsAString& aType)
|
||||
{
|
||||
if (mSocketBridgeParent) {
|
||||
mSocketBridgeParent->FireErrorEvent(aName, aType, mReadyState);
|
||||
return;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
TCPSocketErrorEventInit init;
|
||||
@ -464,28 +477,66 @@ TCPSocket::FireErrorEvent(const nsAString& aName, const nsAString& aType)
|
||||
|
||||
nsRefPtr<TCPSocketErrorEvent> event =
|
||||
TCPSocketErrorEvent::Constructor(this, NS_LITERAL_STRING("error"), init);
|
||||
MOZ_ASSERT(event);
|
||||
event->SetTrusted(true);
|
||||
bool dummy;
|
||||
DispatchEvent(event, &dummy);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
NS_IMETHODIMP
|
||||
TCPSocket::FireEvent(const nsAString& aType)
|
||||
{
|
||||
if (mSocketBridgeParent) {
|
||||
mSocketBridgeParent->FireEvent(aType, mReadyState);
|
||||
return;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
AutoJSAPI api;
|
||||
if (NS_WARN_IF(!api.Init(GetOwner()))) {
|
||||
return;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
JS::Rooted<JS::Value> val(api.cx());
|
||||
FireDataEvent(api.cx(), aType, val);
|
||||
return FireDataEvent(api.cx(), aType, val);
|
||||
}
|
||||
|
||||
void
|
||||
NS_IMETHODIMP
|
||||
TCPSocket::FireDataArrayEvent(const nsAString& aType,
|
||||
const InfallibleTArray<uint8_t>& buffer)
|
||||
{
|
||||
AutoJSAPI api;
|
||||
if (NS_WARN_IF(!api.Init(GetOwner()))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
JSContext* cx = api.cx();
|
||||
JS::Rooted<JS::Value> val(cx);
|
||||
|
||||
bool ok = IPC::DeserializeArrayBuffer(cx, buffer, &val);
|
||||
if (ok) {
|
||||
return FireDataEvent(cx, aType, val);
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TCPSocket::FireDataStringEvent(const nsAString& aType,
|
||||
const nsACString& aString)
|
||||
{
|
||||
AutoJSAPI api;
|
||||
if (NS_WARN_IF(!api.Init(GetOwner()))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
JSContext* cx = api.cx();
|
||||
JS::Rooted<JS::Value> val(cx);
|
||||
|
||||
bool ok = ToJSValue(cx, NS_ConvertASCIItoUTF16(aString), &val);
|
||||
if (ok) {
|
||||
return FireDataEvent(cx, aType, val);
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TCPSocket::FireDataEvent(JSContext* aCx, const nsAString& aType, JS::Handle<JS::Value> aData)
|
||||
{
|
||||
MOZ_ASSERT(!mSocketBridgeParent);
|
||||
@ -500,6 +551,7 @@ TCPSocket::FireDataEvent(JSContext* aCx, const nsAString& aType, JS::Handle<JS::
|
||||
event->SetTrusted(true);
|
||||
bool dummy;
|
||||
DispatchEvent(event, &dummy);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
@ -687,11 +739,10 @@ TCPSocket::MaybeReportErrorAndCloseIfOpen(nsresult status) {
|
||||
}
|
||||
}
|
||||
|
||||
FireErrorEvent(errName, errorType);
|
||||
NS_WARN_IF(NS_FAILED(FireErrorEvent(errName, errorType)));
|
||||
}
|
||||
|
||||
FireEvent(NS_LITERAL_STRING("close"));
|
||||
return NS_OK;
|
||||
return FireEvent(NS_LITERAL_STRING("close"));
|
||||
}
|
||||
|
||||
void
|
||||
@ -1059,26 +1110,28 @@ TCPSocket::SetAppIdAndBrowser(uint32_t aAppId, bool aInBrowser)
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
NS_IMETHODIMP
|
||||
TCPSocket::UpdateReadyState(uint32_t aReadyState)
|
||||
{
|
||||
MOZ_ASSERT(mSocketBridgeChild);
|
||||
mReadyState = static_cast<TCPReadyState>(aReadyState);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
NS_IMETHODIMP
|
||||
TCPSocket::UpdateBufferedAmount(uint32_t aBufferedAmount, uint32_t aTrackingNumber)
|
||||
{
|
||||
if (aTrackingNumber != mTrackingNumber) {
|
||||
return;
|
||||
return NS_OK;
|
||||
}
|
||||
mBufferedAmount = aBufferedAmount;
|
||||
if (!mBufferedAmount) {
|
||||
if (mWaitingForDrain) {
|
||||
mWaitingForDrain = false;
|
||||
FireEvent(NS_LITERAL_STRING("drain"));
|
||||
return FireEvent(NS_LITERAL_STRING("drain"));
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "nsITCPSocketCallback.h"
|
||||
#include "js/RootingAPI.h"
|
||||
|
||||
class nsISocketTransport;
|
||||
@ -72,6 +73,7 @@ class TCPSocket final : public DOMEventTargetHelper
|
||||
, public nsIInputStreamCallback
|
||||
, public nsIObserver
|
||||
, public nsSupportsWeakReference
|
||||
, public nsITCPSocketCallback
|
||||
{
|
||||
public:
|
||||
TCPSocket(nsIGlobalObject* aGlobal, const nsAString& aHost, uint16_t aPort,
|
||||
@ -84,6 +86,7 @@ public:
|
||||
NS_DECL_NSITRANSPORTEVENTSINK
|
||||
NS_DECL_NSIINPUTSTREAMCALLBACK
|
||||
NS_DECL_NSIOBSERVER
|
||||
NS_DECL_NSITCPSOCKETCALLBACK
|
||||
|
||||
nsPIDOMWindow* GetParentObject() const
|
||||
{
|
||||
@ -154,17 +157,9 @@ public:
|
||||
// Inform this socket that a buffered send() has completed sending.
|
||||
void NotifyCopyComplete(nsresult aStatus);
|
||||
|
||||
// Set this child socket's number of buffered bytes, based on the count from the parent
|
||||
// process associated with the given sequence id.
|
||||
void UpdateBufferedAmount(uint32_t aAmount, uint32_t aTrackingNumber);
|
||||
// Set this child socket's ready state, based on the state in the parent process.
|
||||
void UpdateReadyState(uint32_t aReadyState);
|
||||
// Dispatch an "error" event at this object.
|
||||
void FireErrorEvent(const nsAString& aName, const nsAString& aMessage);
|
||||
// Dispatch an event of the given type at this object.
|
||||
void FireEvent(const nsAString& aType);
|
||||
// Dispatch a "data" event at this object.
|
||||
void FireDataEvent(JSContext* aCx, const nsAString& aType, JS::Handle<JS::Value> aData);
|
||||
// Initialize this socket from a low-level connection that hasn't connected yet
|
||||
// (called from RecvOpenBind() in TCPSocketParent).
|
||||
nsresult InitWithUnconnectedTransport(nsISocketTransport* aTransport);
|
||||
|
||||
private:
|
||||
~TCPSocket();
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "mozilla/net/NeckoChild.h"
|
||||
#include "mozilla/dom/PBrowserChild.h"
|
||||
#include "mozilla/dom/TabChild.h"
|
||||
#include "nsITCPSocketCallback.h"
|
||||
#include "TCPSocket.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "jsapi.h"
|
||||
@ -93,7 +94,7 @@ TCPSocketChild::TCPSocketChild(const nsAString& aHost, const uint16_t& aPort)
|
||||
}
|
||||
|
||||
void
|
||||
TCPSocketChild::SendOpen(TCPSocket* aSocket, bool aUseSSL, bool aUseArrayBuffers)
|
||||
TCPSocketChild::SendOpen(nsITCPSocketCallback* aSocket, bool aUseSSL, bool aUseArrayBuffers)
|
||||
{
|
||||
mSocket = aSocket;
|
||||
|
||||
@ -102,6 +103,22 @@ TCPSocketChild::SendOpen(TCPSocket* aSocket, bool aUseSSL, bool aUseArrayBuffers
|
||||
PTCPSocketChild::SendOpen(mHost, mPort, aUseSSL, aUseArrayBuffers);
|
||||
}
|
||||
|
||||
void
|
||||
TCPSocketChild::SendWindowlessOpenBind(nsITCPSocketCallback* aSocket,
|
||||
const nsACString& aRemoteHost, uint16_t aRemotePort,
|
||||
const nsACString& aLocalHost, uint16_t aLocalPort,
|
||||
bool aUseSSL)
|
||||
{
|
||||
mSocket = aSocket;
|
||||
AddIPDLReference();
|
||||
gNeckoChild->SendPTCPSocketConstructor(this,
|
||||
NS_ConvertUTF8toUTF16(aRemoteHost),
|
||||
aRemotePort);
|
||||
PTCPSocketChild::SendOpenBind(nsCString(aRemoteHost), aRemotePort,
|
||||
nsCString(aLocalHost), aLocalPort,
|
||||
aUseSSL, true);
|
||||
}
|
||||
|
||||
void
|
||||
TCPSocketChildBase::ReleaseIPDLReference()
|
||||
{
|
||||
@ -147,26 +164,13 @@ TCPSocketChild::RecvCallback(const nsString& aType,
|
||||
} else if (aData.type() == CallbackData::TSendableData) {
|
||||
const SendableData& data = aData.get_SendableData();
|
||||
|
||||
AutoJSAPI api;
|
||||
if (NS_WARN_IF(!api.Init(mSocket->GetOwner()))) {
|
||||
return true;
|
||||
}
|
||||
JSContext* cx = api.cx();
|
||||
JS::Rooted<JS::Value> val(cx);
|
||||
|
||||
if (data.type() == SendableData::TArrayOfuint8_t) {
|
||||
bool ok = IPC::DeserializeArrayBuffer(cx, data.get_ArrayOfuint8_t(), &val);
|
||||
NS_ENSURE_TRUE(ok, true);
|
||||
|
||||
mSocket->FireDataArrayEvent(aType, data.get_ArrayOfuint8_t());
|
||||
} else if (data.type() == SendableData::TnsCString) {
|
||||
bool ok = ToJSValue(cx, NS_ConvertASCIItoUTF16(data.get_nsCString()), &val);
|
||||
NS_ENSURE_TRUE(ok, true);
|
||||
|
||||
mSocket->FireDataStringEvent(aType, data.get_nsCString());
|
||||
} else {
|
||||
MOZ_CRASH("Invalid callback data type!");
|
||||
}
|
||||
mSocket->FireDataEvent(cx, aType, val);
|
||||
|
||||
} else {
|
||||
MOZ_CRASH("Invalid callback type!");
|
||||
}
|
||||
@ -199,6 +203,13 @@ TCPSocketChild::SendSend(const ArrayBuffer& aData,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TCPSocketChild::SendSendArray(nsTArray<uint8_t>& aArray, uint32_t aTrackingNumber)
|
||||
{
|
||||
SendData(aArray, aTrackingNumber);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
TCPSocketChild::SetSocket(TCPSocket* aSocket)
|
||||
{
|
||||
|
@ -12,6 +12,15 @@
|
||||
#include "nsCOMPtr.h"
|
||||
#include "js/TypeDecls.h"
|
||||
|
||||
class nsITCPSocketCallback;
|
||||
|
||||
namespace IPC {
|
||||
bool
|
||||
DeserializeArrayBuffer(JSContext* cx,
|
||||
const InfallibleTArray<uint8_t>& aBuffer,
|
||||
JS::MutableHandle<JS::Value> aVal);
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
@ -29,7 +38,7 @@ protected:
|
||||
TCPSocketChildBase();
|
||||
virtual ~TCPSocketChildBase();
|
||||
|
||||
nsRefPtr<TCPSocket> mSocket;
|
||||
nsCOMPtr<nsITCPSocketCallback> mSocket;
|
||||
bool mIPCOpen;
|
||||
};
|
||||
|
||||
@ -42,12 +51,20 @@ public:
|
||||
TCPSocketChild(const nsAString& aHost, const uint16_t& aPort);
|
||||
~TCPSocketChild();
|
||||
|
||||
void SendOpen(TCPSocket* aSocket, bool aUseSSL, bool aUseArrayBuffers);
|
||||
void SendOpen(nsITCPSocketCallback* aSocket, bool aUseSSL, bool aUseArrayBuffers);
|
||||
void SendWindowlessOpenBind(nsITCPSocketCallback* aSocket,
|
||||
const nsACString& aRemoteHost, uint16_t aRemotePort,
|
||||
const nsACString& aLocalHost, uint16_t aLocalPort,
|
||||
bool aUseSSL);
|
||||
NS_IMETHOD SendSendArray(nsTArray<uint8_t>& aArray,
|
||||
uint32_t aTrackingNumber);
|
||||
void SendSend(const nsACString& aData, uint32_t aTrackingNumber);
|
||||
nsresult SendSend(const ArrayBuffer& aData,
|
||||
uint32_t aByteOffset,
|
||||
uint32_t aByteLength,
|
||||
uint32_t aTrackingNumber);
|
||||
void SendSendArray(nsTArray<uint8_t>* arr,
|
||||
uint32_t trackingNumber);
|
||||
void SetSocket(TCPSocket* aSocket);
|
||||
|
||||
void GetHost(nsAString& aHost);
|
||||
|
@ -16,6 +16,8 @@
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "mozilla/dom/TabParent.h"
|
||||
#include "mozilla/HoldDropJSObjects.h"
|
||||
#include "nsISocketTransportService.h"
|
||||
#include "nsISocketTransport.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
||||
@ -170,6 +172,74 @@ TCPSocketParent::RecvOpen(const nsString& aHost, const uint16_t& aPort, const bo
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TCPSocketParent::RecvOpenBind(const nsCString& aRemoteHost,
|
||||
const uint16_t& aRemotePort,
|
||||
const nsCString& aLocalAddr,
|
||||
const uint16_t& aLocalPort,
|
||||
const bool& aUseSSL,
|
||||
const bool& aUseArrayBuffers)
|
||||
{
|
||||
if (net::UsingNeckoIPCSecurity() &&
|
||||
!AssertAppProcessPermission(Manager()->Manager(), "tcp-socket")) {
|
||||
FireInteralError(this, __LINE__);
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsISocketTransportService> sts =
|
||||
do_GetService("@mozilla.org/network/socket-transport-service;1", &rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
FireInteralError(this, __LINE__);
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISocketTransport> socketTransport;
|
||||
rv = sts->CreateTransport(nullptr, 0,
|
||||
aRemoteHost, aRemotePort,
|
||||
nullptr, getter_AddRefs(socketTransport));
|
||||
if (NS_FAILED(rv)) {
|
||||
FireInteralError(this, __LINE__);
|
||||
return true;
|
||||
}
|
||||
|
||||
PRNetAddr prAddr;
|
||||
if (PR_SUCCESS != PR_InitializeNetAddr(PR_IpAddrAny, aLocalPort, &prAddr)) {
|
||||
FireInteralError(this, __LINE__);
|
||||
return true;
|
||||
}
|
||||
if (PR_SUCCESS != PR_StringToNetAddr(aLocalAddr.BeginReading(), &prAddr)) {
|
||||
FireInteralError(this, __LINE__);
|
||||
return true;
|
||||
}
|
||||
|
||||
mozilla::net::NetAddr addr;
|
||||
PRNetAddrToNetAddr(&prAddr, &addr);
|
||||
rv = socketTransport->Bind(&addr);
|
||||
if (NS_FAILED(rv)) {
|
||||
FireInteralError(this, __LINE__);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Obtain App ID
|
||||
uint32_t appId = nsIScriptSecurityManager::NO_APP_ID;
|
||||
bool inBrowser = false;
|
||||
const PContentParent *content = Manager()->Manager();
|
||||
const InfallibleTArray<PBrowserParent*>& browsers = content->ManagedPBrowserParent();
|
||||
if (browsers.Length() > 0) {
|
||||
TabParent *tab = static_cast<TabParent*>(browsers[0]);
|
||||
appId = tab->OwnAppId();
|
||||
inBrowser = tab->IsBrowserElement();
|
||||
}
|
||||
|
||||
mSocket = new TCPSocket(nullptr, NS_ConvertUTF8toUTF16(aRemoteHost), aRemotePort, aUseSSL, aUseArrayBuffers);
|
||||
mSocket->SetAppIdAndBrowser(appId, inBrowser);
|
||||
mSocket->SetSocketBridgeParent(this);
|
||||
rv = mSocket->InitWithUnconnectedTransport(socketTransport);
|
||||
NS_ENSURE_SUCCESS(rv, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TCPSocketParent::RecvStartTLS()
|
||||
{
|
||||
|
@ -52,6 +52,13 @@ public:
|
||||
virtual bool RecvOpen(const nsString& aHost, const uint16_t& aPort,
|
||||
const bool& useSSL, const bool& aUseArrayBuffers) override;
|
||||
|
||||
virtual bool RecvOpenBind(const nsCString& aRemoteHost,
|
||||
const uint16_t& aRemotePort,
|
||||
const nsCString& aLocalAddr,
|
||||
const uint16_t& aLocalPort,
|
||||
const bool& aUseSSL,
|
||||
const bool& aUseArrayBuffers) override;
|
||||
|
||||
virtual bool RecvStartTLS() override;
|
||||
virtual bool RecvSuspend() override;
|
||||
virtual bool RecvResume() override;
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIMozNavigatorNetwork.idl',
|
||||
'nsITCPSocketCallback.idl',
|
||||
'nsIUDPSocketChild.idl',
|
||||
]
|
||||
|
||||
|
62
dom/network/interfaces/nsITCPSocketCallback.idl
Normal file
62
dom/network/interfaces/nsITCPSocketCallback.idl
Normal file
@ -0,0 +1,62 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* MozTCPSocket exposes a TCP client and server sockets
|
||||
* to highly privileged apps. It provides a buffered, non-blocking
|
||||
* interface for sending. For receiving, it uses an asynchronous,
|
||||
* event handler based interface.
|
||||
*/
|
||||
|
||||
#include "domstubs.idl"
|
||||
|
||||
%{C++
|
||||
template<class T> class InfallibleTArray;
|
||||
%}
|
||||
[ref] native nsUint8TArrayRef(InfallibleTArray<uint8_t>);
|
||||
[ptr] native JSContextPtr(JSContext);
|
||||
|
||||
|
||||
/*
|
||||
* This interface is implemented in TCPSocket.cpp as an internal interface
|
||||
* for use in cross-process socket implementation.
|
||||
* Needed to account for multiple possible types that can be provided to
|
||||
* the socket callbacks as arguments.
|
||||
*/
|
||||
[scriptable, uuid(ac2c4b69-cb79-4767-b1ce-bcf62945cd39)]
|
||||
interface nsITCPSocketCallback : nsISupports {
|
||||
// Limitation of TCPSocket's buffer size.
|
||||
const unsigned long BUFFER_SIZE = 65536;
|
||||
|
||||
// Dispatch an "error" event at this object with the given name and type.
|
||||
void fireErrorEvent(in AString name, in AString type);
|
||||
|
||||
// Dispatch a "data" event at this object with a string
|
||||
void fireDataStringEvent(in DOMString type, in ACString data);
|
||||
|
||||
// Dispatch a "data" event at this object with an Array
|
||||
void fireDataArrayEvent(in DOMString type, [const] in nsUint8TArrayRef data);
|
||||
|
||||
// Dispatch a "data" event at this object with an ArrayBuffer argument
|
||||
void fireDataEvent(in JSContextPtr cx, in DOMString type, in jsval data);
|
||||
|
||||
// Dispatch an event of the given type at this object.
|
||||
void fireEvent(in DOMString type);
|
||||
|
||||
// Update the DOM object's readyState.
|
||||
// @param readyState
|
||||
// new ready state
|
||||
void updateReadyState(in unsigned long readystate);
|
||||
|
||||
// Update the DOM object's bufferedAmount value with a tracking number to
|
||||
// to allow tracking of which writes are "in-flight"
|
||||
// @param bufferedAmount
|
||||
// TCPSocket parent's bufferedAmount.
|
||||
// @param trackingNumber
|
||||
// A number to ensure the bufferedAmount is updated after data
|
||||
// from child are sent to parent.
|
||||
void updateBufferedAmount(in uint32_t bufferedAmount,
|
||||
in uint32_t trackingNumber);
|
||||
};
|
||||
|
@ -270,7 +270,7 @@ var interfaceNamesInGlobalScope =
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "CameraStateChangeEvent", b2g: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "CanvasCaptureMediaStream", disabled: true},
|
||||
{name: "CanvasCaptureMediaStream"},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"CanvasGradient",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
|
@ -55,24 +55,26 @@ dictionary MediaTrackConstraints : MediaTrackConstraintSet {
|
||||
sequence<MediaTrackConstraintSet> advanced;
|
||||
};
|
||||
|
||||
interface MediaStreamTrack {
|
||||
[Exposed=Window]
|
||||
interface MediaStreamTrack : EventTarget {
|
||||
readonly attribute DOMString kind;
|
||||
readonly attribute DOMString id;
|
||||
readonly attribute DOMString label;
|
||||
attribute boolean enabled;
|
||||
// readonly attribute MediaStreamTrackState readyState;
|
||||
// readonly attribute SourceTypeEnum sourceType;
|
||||
// readonly attribute DOMString sourceId;
|
||||
// attribute EventHandler onstarted;
|
||||
// attribute EventHandler onmute;
|
||||
// attribute EventHandler onunmute;
|
||||
// readonly attribute boolean muted;
|
||||
// attribute EventHandler onmute;
|
||||
// attribute EventHandler onunmute;
|
||||
// readonly attribute boolean _readonly;
|
||||
// readonly attribute boolean remote;
|
||||
// readonly attribute MediaStreamTrackState readyState;
|
||||
// attribute EventHandler onended;
|
||||
// any getConstraint (DOMString constraintName, optional boolean mandatory = false);
|
||||
// void setConstraint (DOMString constraintName, any constraintValue, optional boolean mandatory = false);
|
||||
// MediaTrackConstraints? constraints ();
|
||||
// void applyConstraints (MediaTrackConstraints constraints);
|
||||
// void prependConstraint (DOMString constraintName, any constraintValue);
|
||||
// void appendConstraint (DOMString constraintName, any constraintValue);
|
||||
// attribute EventHandler onoverconstrained;
|
||||
// MediaStreamTrack clone ();
|
||||
void stop ();
|
||||
// MediaTrackCapabilities getCapabilities ();
|
||||
// MediaTrackConstraints getConstraints ();
|
||||
// MediaTrackSettings getSettings ();
|
||||
|
||||
[Throws]
|
||||
Promise<void> applyConstraints (optional MediaTrackConstraints constraints);
|
||||
// attribute EventHandler onoverconstrained;
|
||||
};
|
||||
|
@ -635,6 +635,34 @@ nsEditorSpellCheck::SetCurrentDictionary(const nsAString& aDictionary)
|
||||
return mSpellChecker->SetCurrentDictionary(aDictionary);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsEditorSpellCheck::CheckCurrentDictionary()
|
||||
{
|
||||
mSpellChecker->CheckCurrentDictionary();
|
||||
|
||||
// Check if our current dictionary is still available.
|
||||
nsAutoString currentDictionary;
|
||||
nsresult rv = GetCurrentDictionary(currentDictionary);
|
||||
if (NS_SUCCEEDED(rv) && !currentDictionary.IsEmpty()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If our preferred current dictionary has gone, pick another one.
|
||||
nsTArray<nsString> dictList;
|
||||
rv = mSpellChecker->GetDictionaryList(&dictList);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (dictList.Length() > 0) {
|
||||
// Use RAII object to prevent content preferences being written during
|
||||
// this call.
|
||||
UpdateDictionaryHolder holder(this);
|
||||
rv = SetCurrentDictionary(dictList[0]);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsEditorSpellCheck::UninitSpellChecker()
|
||||
{
|
||||
@ -807,14 +835,6 @@ nsEditorSpellCheck::DictionaryFetched(DictionaryFetcher* aFetcher)
|
||||
#endif
|
||||
}
|
||||
|
||||
// Auxiliary status.
|
||||
nsresult rv2;
|
||||
|
||||
// We obtain a list of available dictionaries.
|
||||
nsTArray<nsString> dictList;
|
||||
rv2 = mSpellChecker->GetDictionaryList(&dictList);
|
||||
NS_ENSURE_SUCCESS(rv2, rv2);
|
||||
|
||||
// Priority 1:
|
||||
// If we successfully fetched a dictionary from content prefs, do not go
|
||||
// further. Use this exact dictionary.
|
||||
@ -825,7 +845,7 @@ nsEditorSpellCheck::DictionaryFetched(DictionaryFetcher* aFetcher)
|
||||
if (!(flags & nsIPlaintextEditor::eEditorMailMask)) {
|
||||
dictName.Assign(aFetcher->mDictionary);
|
||||
if (!dictName.IsEmpty()) {
|
||||
if (NS_SUCCEEDED(TryDictionary(dictName, dictList, DICT_NORMAL_COMPARE))) {
|
||||
if (NS_SUCCEEDED(SetCurrentDictionary(dictName))) {
|
||||
#ifdef DEBUG_DICT
|
||||
printf("***** Assigned from content preferences |%s|\n",
|
||||
NS_ConvertUTF16toUTF8(dictName).get());
|
||||
@ -851,6 +871,14 @@ nsEditorSpellCheck::DictionaryFetched(DictionaryFetcher* aFetcher)
|
||||
NS_ConvertUTF16toUTF8(dictName).get());
|
||||
#endif
|
||||
|
||||
// Auxiliary status.
|
||||
nsresult rv2;
|
||||
|
||||
// We obtain a list of available dictionaries.
|
||||
nsTArray<nsString> dictList;
|
||||
rv2 = mSpellChecker->GetDictionaryList(&dictList);
|
||||
NS_ENSURE_SUCCESS(rv2, rv2);
|
||||
|
||||
// Get the preference value.
|
||||
nsAutoString preferredDict;
|
||||
preferredDict = Preferences::GetLocalizedString("spellchecker.dictionary");
|
||||
|
@ -1328,6 +1328,15 @@ NS_IMETHODIMP nsEditor::Observe(nsISupports* aSubj, const char *aTopic,
|
||||
|
||||
// When nsIEditorSpellCheck::GetCurrentDictionary changes
|
||||
if (mInlineSpellChecker) {
|
||||
// if the current dictionary is no longer available, find another one
|
||||
nsCOMPtr<nsIEditorSpellCheck> editorSpellCheck;
|
||||
mInlineSpellChecker->GetSpellChecker(getter_AddRefs(editorSpellCheck));
|
||||
if (editorSpellCheck) {
|
||||
// Note: This might change the current dictionary, which may call
|
||||
// this observer recursively.
|
||||
editorSpellCheck->CheckCurrentDictionary();
|
||||
}
|
||||
|
||||
// update the inline spell checker to reflect the new current dictionary
|
||||
mInlineSpellChecker->SpellCheckRange(nullptr); // causes recheck
|
||||
}
|
||||
|
@ -5614,27 +5614,6 @@ nsHTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode* aNode,
|
||||
if (mHTMLEditor->IsVisBreak(nextNode->AsDOMNode())) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Check for newlines in pre-formatted text nodes.
|
||||
bool isPRE;
|
||||
mHTMLEditor->IsPreformatted(nextNode->AsDOMNode(), &isPRE);
|
||||
if (isPRE) {
|
||||
nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(nextNode);
|
||||
if (textNode) {
|
||||
nsAutoString tempString;
|
||||
textNode->GetData(tempString);
|
||||
int32_t newlinePos = tempString.FindChar(nsCRT::LF);
|
||||
if (newlinePos >= 0) {
|
||||
if ((uint32_t)newlinePos + 1 == tempString.Length()) {
|
||||
// No need for special processing if the newline is at the end.
|
||||
break;
|
||||
}
|
||||
*outNode = nextNode->AsDOMNode();
|
||||
*outOffset = newlinePos + 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
NS_ENSURE_TRUE(mHTMLEditor, /* void */);
|
||||
nextNode = mHTMLEditor->GetNextHTMLNode(node, offset, true);
|
||||
}
|
||||
@ -5802,29 +5781,6 @@ nsHTMLEditRules::GetNodesForOperation(nsTArray<nsRefPtr<nsRange>>& aArrayOfRange
|
||||
int32_t rangeCount = aArrayOfRanges.Length();
|
||||
nsresult res = NS_OK;
|
||||
|
||||
// Split text nodes. This is necessary, since GetPromotedPoint() may return a
|
||||
// range ending in a text node in case where part of a pre-formatted
|
||||
// elements needs to be moved.
|
||||
for (int32_t i = 0; i < rangeCount; i++) {
|
||||
nsRefPtr<nsRange> r = aArrayOfRanges[i];
|
||||
nsCOMPtr<nsIContent> endParent = do_QueryInterface(r->GetEndParent());
|
||||
|
||||
if (mHTMLEditor->IsTextNode(endParent)) {
|
||||
int32_t offset = r->EndOffset();
|
||||
|
||||
// Split the text node.
|
||||
nsCOMPtr<nsIDOMNode> tempNode;
|
||||
res = mHTMLEditor->SplitNode(endParent->AsDOMNode(), offset,
|
||||
getter_AddRefs(tempNode));
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
|
||||
// Correct the range.
|
||||
// The new end parent becomes the parent node of the text.
|
||||
nsCOMPtr<nsIContent> newParent = endParent->GetParent();
|
||||
r->SetEnd(newParent, newParent->IndexOf(endParent));
|
||||
}
|
||||
}
|
||||
|
||||
// Bust up any inlines that cross our range endpoints, but only if we are
|
||||
// allowed to touch content.
|
||||
|
||||
|
@ -130,7 +130,6 @@ skip-if = toolkit == 'android' || e10s
|
||||
[test_bug757371.html]
|
||||
[test_bug757771.html]
|
||||
[test_bug767684.html]
|
||||
[test_bug772796.html]
|
||||
[test_bug773262.html]
|
||||
[test_bug780035.html]
|
||||
[test_bug787432.html]
|
||||
|
@ -1,218 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=772796
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 772796</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<style> .pre { white-space: pre } </style>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=772796">Mozilla Bug 772796</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
|
||||
<div id="editable" contenteditable></div>
|
||||
|
||||
<pre id="test">
|
||||
|
||||
<script type="application/javascript">
|
||||
var tests = [
|
||||
/*00*/[ "<div>test</div><pre>foobar\nbaz</pre>", "<div>testfoobar\n</div><pre>baz</pre>" ],
|
||||
/*01*/[ "<div>test</div><pre><b>foobar\nbaz</b></pre>", "<div>testfoobar\n</div><pre><b>baz</b></pre>" ],
|
||||
/*02*/[ "<div>test</div><pre><b>foo</b>bar\nbaz</pre>", "<div>test<b>foo</b>bar\n</div><pre>baz</pre>" ],
|
||||
/*03*/[ "<div>test</div><pre><b>foo</b>\nbar</pre>", "<div>test<b>foo</b>\n</div><pre>bar</pre>" ],
|
||||
/*04*/[ "<div>test</div><pre><b>foo\n</b>bar\nbaz</pre>", "<div>testfoo\n</div><pre>bar\nbaz</pre>" ],
|
||||
/* The <br> after the foobar is unfortunate but is behaviour that hasn't changed in bug 772796. */
|
||||
/*05*/[ "<div>test</div><pre>foobar<br>baz</pre>", "<div>testfoobar<br></div><pre>baz</pre>" ],
|
||||
/*06*/[ "<div>test</div><pre><b>foobar<br>baz</b></pre>", "<div>testfoobar<br></div><pre><b>baz</b></pre>" ],
|
||||
|
||||
/*
|
||||
* Some tests with block elements.
|
||||
* Tests 07, 09 and 11 don't use "MoveBlock", they use "JoinNodesSmart".
|
||||
* Test 11 is a pain: <div>foo\bar</div> is be joined to "test", losing the visible line break.
|
||||
*/
|
||||
/*07*/[ "<div>test</div><pre><div>foobar</div>baz</pre>", "<div>testfoobar</div><pre>baz</pre>" ],
|
||||
/*08*/[ "<div>test</div><pre>foobar<div>baz</div></pre>", "<div>testfoobar</div><pre><div>baz</div></pre>" ],
|
||||
/*09*/[ "<div>test</div><pre><div>foobar</div>baz\nfred</pre>", "<div>testfoobar</div><pre>baz\nfred</pre>" ],
|
||||
/*10*/[ "<div>test</div><pre>foobar<div>baz</div>\nfred</pre>", "<div>testfoobar</div><pre><div>baz</div>\nfred</pre>" ],
|
||||
/*11*/[ "<div>test</div><pre><div>foo\nbar</div>baz\nfred</pre>", "<div>testfoo\nbar</div><pre>baz\nfred</pre>" ], // BAD
|
||||
/*12*/[ "<div>test</div><pre>foo<div>bar</div>baz\nfred</pre>", "<div>testfoo</div><pre><div>bar</div>baz\nfred</pre>" ],
|
||||
|
||||
/*
|
||||
* Repeating all tests above with the <pre> on a new line.
|
||||
* We know that backspace doesn't work (bug 1190161). Third argument shows the current outcome.
|
||||
*/
|
||||
/*13*/[ "<div>test</div>\n<pre>foobar\nbaz</pre>", "<div>testfoobar\n</div><pre>baz</pre>",
|
||||
"<div>test</div>foobar\n<pre>baz</pre>" ],
|
||||
/*14*/[ "<div>test</div>\n<pre><b>foobar\nbaz</b></pre>", "<div>testfoobar\n</div><pre><b>baz</b></pre>",
|
||||
"<div>test</div>foobar\n<pre><b>baz</b></pre>" ],
|
||||
/*15*/[ "<div>test</div>\n<pre><b>foo</b>bar\nbaz</pre>", "<div>test<b>foo</b>bar\n</div><pre>baz</pre>",
|
||||
"<div>test</div><b>foo</b>bar\n<pre>baz</pre>" ],
|
||||
/*16*/[ "<div>test</div>\n<pre><b>foo</b>\nbar</pre>", "<div>test<b>foo</b>\n</div><pre>bar</pre>",
|
||||
"<div>test</div><b>foo</b>\n<pre>bar</pre>" ],
|
||||
/*17*/[ "<div>test</div>\n<pre><b>foo\n</b>bar\nbaz</pre>", "<div>testfoo\n</div><pre>bar\nbaz</pre>",
|
||||
"<div>test</div>foo\n<pre>bar\nbaz</pre>" ],
|
||||
/*18*/[ "<div>test</div>\n<pre>foobar<br>baz</pre>", "<div>testfoobar<br></div><pre>baz</pre>",
|
||||
"<div>test</div>foobar<br><pre>baz</pre>" ],
|
||||
/*19*/[ "<div>test</div>\n<pre><b>foobar<br>baz</b></pre>", "<div>testfoobar<br></div><pre><b>baz</b></pre>",
|
||||
"<div>test</div>foobar<br><pre><b>baz</b></pre>" ],
|
||||
/*20*/[ "<div>test</div>\n<pre><div>foobar</div>baz</pre>", "<div>testfoobar</div><pre>baz</pre>",
|
||||
"<div>test</div>foobar<pre>baz</pre>" ],
|
||||
/*21*/[ "<div>test</div>\n<pre>foobar<div>baz</div></pre>", "<div>testfoobar</div><pre><div>baz</div></pre>",
|
||||
"<div>test</div>foobar<pre><div>baz</div></pre>" ],
|
||||
/*22*/[ "<div>test</div>\n<pre><div>foobar</div>baz\nfred</pre>", "<div>testfoobar</div><pre>baz\nfred</pre>",
|
||||
"<div>test</div>foobar<pre>baz\nfred</pre>" ],
|
||||
/*23*/[ "<div>test</div>\n<pre>foobar<div>baz</div>\nfred</pre>", "<div>testfoobar</div><pre><div>baz</div>\nfred</pre>",
|
||||
"<div>test</div>foobar<pre><div>baz</div>\nfred</pre>" ],
|
||||
/*24*/[ "<div>test</div>\n<pre><div>foo\nbar</div>baz\nfred</pre>", "<div>testfoo\nbar</div><pre>baz\nfred</pre>", // BAD
|
||||
"<div>test</div>foo\n<pre><div>bar</div>baz\nfred</pre>" ],
|
||||
/*25*/[ "<div>test</div>\n<pre>foo<div>bar</div>baz\nfred</pre>", "<div>testfoo</div><pre><div>bar</div>baz\nfred</pre>",
|
||||
"<div>test</div>foo<pre><div>bar</div>baz\nfred</pre>" ],
|
||||
|
||||
/* Some tests without <div>. These exercise the MoveBlock "right in left" */
|
||||
/*26*/[ "test<pre>foobar\nbaz</pre>", "testfoobar\n<pre>baz</pre>" ],
|
||||
/*27*/[ "test<pre><b>foobar\nbaz</b></pre>", "testfoobar\n<pre><b>baz</b></pre>" ],
|
||||
/*28*/[ "test<pre><b>foo</b>bar\nbaz</pre>", "test<b>foo</b>bar\n<pre>baz</pre>" ],
|
||||
/*29*/[ "test<pre><b>foo</b>\nbar</pre>", "test<b>foo</b>\n<pre>bar</pre>" ],
|
||||
/*30*/[ "test<pre><b>foo\n</b>bar\nbaz</pre>", "testfoo\n<pre>bar\nbaz</pre>" ],
|
||||
/*31*/[ "test<pre>foobar<br>baz</pre>", "testfoobar<br><pre>baz</pre>" ],
|
||||
/*32*/[ "test<pre><b>foobar<br>baz</b></pre>", "testfoobar<br><pre><b>baz</b></pre>" ],
|
||||
/*33*/[ "test<pre><div>foobar</div>baz</pre>", "testfoobar<pre>baz</pre>" ],
|
||||
/*34*/[ "test<pre>foobar<div>baz</div></pre>", "testfoobar<pre><div>baz</div></pre>" ],
|
||||
/*35*/[ "test<pre><div>foobar</div>baz\nfred</pre>", "testfoobar<pre>baz\nfred</pre>" ],
|
||||
/*36*/[ "test<pre>foobar<div>baz</div>\nfred</pre>", "testfoobar<pre><div>baz</div>\nfred</pre>" ],
|
||||
/*37*/[ "test<pre><div>foo\nbar</div>baz\nfred</pre>", "testfoo\n<pre><div>bar</div>baz\nfred</pre>" ],
|
||||
/*38*/[ "test<pre>foo<div>bar</div>baz\nfred</pre>", "testfoo<pre><div>bar</div>baz\nfred</pre>" ],
|
||||
|
||||
/*
|
||||
* Some tests with <span class="pre">. Again 07, 09 and 11 use "JoinNodesSmart".
|
||||
* All these exercise MoveBlock "left in right". The "right" is the surrounding "contenteditable" div.
|
||||
*/
|
||||
/*39-00*/[ "<div>test</div><span class=\"pre\">foobar\nbaz</span>", "<div>testfoobar\n</div><span class=\"pre\">baz</span>" ],
|
||||
/*40-01*/[ "<div>test</div><span class=\"pre\"><b>foobar\nbaz</b></span>", "<div>testfoobar\n</div><span class=\"pre\"><b>baz</b></span>" ],
|
||||
/*41-02*/[ "<div>test</div><span class=\"pre\"><b>foo</b>bar\nbaz</span>", "<div>test<b>foo</b>bar\n</div><span class=\"pre\">baz</span>" ],
|
||||
/*42-03*/[ "<div>test</div><span class=\"pre\"><b>foo</b>\nbar</span>", "<div>test<b>foo</b>\n</div><span class=\"pre\">bar</span>" ],
|
||||
/*43-04*/[ "<div>test</div><span class=\"pre\"><b>foo\n</b>bar\nbaz</span>", "<div>testfoo\n</div><span class=\"pre\">bar\nbaz</span>" ],
|
||||
/*44-05*/[ "<div>test</div><span class=\"pre\">foobar<br>baz</span>", "<div>testfoobar<br></div><span class=\"pre\">baz</span>" ],
|
||||
/*45-06*/[ "<div>test</div><span class=\"pre\"><b>foobar<br>baz</b></span>", "<div>testfoobar<br></div><span class=\"pre\"><b>baz</b></span>" ],
|
||||
/*46-07*/[ "<div>test</div><span class=\"pre\"><div>foobar</div>baz</span>", "<div>testfoobar</div><span class=\"pre\">baz</span>" ],
|
||||
/*47-08*/[ "<div>test</div><span class=\"pre\">foobar<div>baz</div></span>", "<div>testfoobar</div><span class=\"pre\"><div>baz</div></span>" ],
|
||||
/*48-09*/[ "<div>test</div><span class=\"pre\"><div>foobar</div>baz\nfred</span>", "<div>testfoobar</div><span class=\"pre\">baz\nfred</span>" ],
|
||||
/*49-10*/[ "<div>test</div><span class=\"pre\">foobar<div>baz</div>\nfred</span>", "<div>testfoobar</div><span class=\"pre\"><div>baz</div>\nfred</span>" ],
|
||||
/*50-11*/[ "<div>test</div><span class=\"pre\"><div>foo\nbar</div>baz\nfred</span>", "<div>testfoo\nbar</div><span class=\"pre\">baz\nfred</span>" ], // BAD
|
||||
/*51-12*/[ "<div>test</div><span class=\"pre\">foo<div>bar</div>baz\nfred</span>", "<div>testfoo</div><span class=\"pre\"><div>bar</div>baz\nfred</span>" ],
|
||||
|
||||
/* Some tests with <div class="pre">. */
|
||||
/*
|
||||
* The results are pretty ugly, since joining two <divs> sadly carrys the properties of the right to the left,
|
||||
* but not in all cases: 07, 09, 11 are actually right. All cases use "JoinNodesSmart".
|
||||
* Here we merely document the ugly behaviour. See bug 1191875 for more information.
|
||||
*/
|
||||
/*52-00*/[ "<div>test</div><div class=\"pre\">foobar\nbaz</div>", "<div class=\"pre\">testfoobar\nbaz</div>" ],
|
||||
/*53-01*/[ "<div>test</div><div class=\"pre\"><b>foobar\nbaz</b></div>", "<div class=\"pre\">test<b>foobar\nbaz</b></div>" ],
|
||||
/*54-02*/[ "<div>test</div><div class=\"pre\"><b>foo</b>bar\nbaz</div>", "<div class=\"pre\">test<b>foo</b>bar\nbaz</div>" ],
|
||||
/*55-03*/[ "<div>test</div><div class=\"pre\"><b>foo</b>\nbar</div>", "<div class=\"pre\">test<b>foo</b>\nbar</div>" ],
|
||||
/*56-04*/[ "<div>test</div><div class=\"pre\"><b>foo\n</b>bar\nbaz</div>", "<div class=\"pre\">test<b>foo\n</b>bar\nbaz</div>" ],
|
||||
/*57-05*/[ "<div>test</div><div class=\"pre\">foobar<br>baz</div>", "<div class=\"pre\">testfoobar<br>baz</div>" ],
|
||||
/*58-06*/[ "<div>test</div><div class=\"pre\"><b>foobar<br>baz</b></div>", "<div class=\"pre\">test<b>foobar<br>baz</b></div>" ],
|
||||
/*59-07*/[ "<div>test</div><div class=\"pre\"><div>foobar</div>baz</div>", "<div>testfoobar</div><div class=\"pre\">baz</div>" ],
|
||||
/*60-08*/[ "<div>test</div><div class=\"pre\">foobar<div>baz</div></div>", "<div class=\"pre\">testfoobar<div>baz</div></div>" ],
|
||||
/*61-09*/[ "<div>test</div><div class=\"pre\"><div>foobar</div>baz\nfred</div>", "<div>testfoobar</div><div class=\"pre\">baz\nfred</div>" ],
|
||||
/*62-10*/[ "<div>test</div><div class=\"pre\">foobar<div>baz</div>\nfred</div>", "<div class=\"pre\">testfoobar<div>baz</div>\nfred</div>" ],
|
||||
/*63-11*/[ "<div>test</div><div class=\"pre\"><div>foo\nbar</div>baz\nfred</div>", "<div>testfoo\nbar</div><div class=\"pre\">baz\nfred</div>" ], // BAD
|
||||
/*64-12*/[ "<div>test</div><div class=\"pre\">foo<div>bar</div>baz\nfred</div>", "<div class=\"pre\">testfoo<div>bar</div>baz\nfred</div>" ],
|
||||
|
||||
/* Some tests with lists. These exercise the MoveBlock "left in right". */
|
||||
/*65*/[ "<ul><pre><li>test</li>foobar\nbaz</pre></ul>", "<ul><pre><li>testfoobar\n</li>baz</pre></ul>" ],
|
||||
/*66*/[ "<ul><pre><li>test</li><b>foobar\nbaz</b></pre></ul>", "<ul><pre><li>testfoobar\n</li><b>baz</b></pre></ul>" ],
|
||||
/*67*/[ "<ul><pre><li>test</li><b>foo</b>bar\nbaz</pre></ul>", "<ul><pre><li>test<b>foo</b>bar\n</li>baz</pre></ul>" ],
|
||||
/*68*/[ "<ul><pre><li>test</li><b>foo</b>\nbar</pre></ul>", "<ul><pre><li>test<b>foo</b>\n</li>bar</pre></ul>" ],
|
||||
/*69*/[ "<ul><pre><li>test</li><b>foo\n</b>bar\nbaz</pre></ul>", "<ul><pre><li>testfoo\n</li>bar\nbaz</pre></ul>" ],
|
||||
|
||||
// Last not least, a simple edge case test.
|
||||
/*70*/[ "<div>test</div><pre>foobar\n</pre>baz", "<div>testfoobar\n</div>baz" ],
|
||||
];
|
||||
|
||||
/** Test for Bug 772796 **/
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
SimpleTest.waitForFocus(function() {
|
||||
|
||||
var sel = window.getSelection();
|
||||
var theEdit = document.getElementById("editable");
|
||||
var testName;
|
||||
var theDiv;
|
||||
|
||||
for (i = 0; i < tests.length; i++) {
|
||||
// for (i = 1; i < 2; i++) {
|
||||
testName = "test" + i.toString();
|
||||
dump (testName+"\n");
|
||||
dump (tests[i][0]+"\n");
|
||||
|
||||
/* Set up the selection. */
|
||||
theEdit.innerHTML = "<div id=\"" + testName + "\">" + tests[i][0] + "</div>";
|
||||
theDiv = document.getElementById(testName);
|
||||
theDiv.focus();
|
||||
sel.collapse(theDiv, 0);
|
||||
synthesizeMouse(theDiv, 100, 2, {}); /* click behind and down */
|
||||
|
||||
/** First round: Forward delete. **/
|
||||
synthesizeKey("VK_DELETE", {});
|
||||
is(theDiv.innerHTML, tests[i][1], "delete(collapsed): inner HTML for " + testName);
|
||||
|
||||
/* Set up the selection. */
|
||||
theEdit.innerHTML = "<div id=\"" + testName + "\">" + tests[i][0] + "</div>";
|
||||
theDiv = document.getElementById(testName);
|
||||
theDiv.focus();
|
||||
sel.collapse(theDiv, 0);
|
||||
synthesizeMouse(theDiv, 100, 2, {}); /* click behind and down */
|
||||
|
||||
/** Second round: Backspace. **/
|
||||
synthesizeKey("VK_RIGHT", {});
|
||||
synthesizeKey("VK_BACK_SPACE", {});
|
||||
if (tests[i].length == 2) {
|
||||
is(theDiv.innerHTML, tests[i][1], "backspace: inner HTML for " + testName);
|
||||
} else {
|
||||
todo_is(theDiv.innerHTML, tests[i][1], "backspace(should be): inner HTML for " + testName);
|
||||
is(theDiv.innerHTML, tests[i][2], "backspace(currently is): inner HTML for " + testName);
|
||||
}
|
||||
|
||||
/* Set up the selection. */
|
||||
theEdit.innerHTML = "<div id=\"" + testName + "\">" + tests[i][0] + "</div>";
|
||||
theDiv = document.getElementById(testName);
|
||||
theDiv.focus();
|
||||
sel.collapse(theDiv, 0);
|
||||
synthesizeMouse(theDiv, 100, 2, {}); /* click behind and down */
|
||||
|
||||
/** Third round: Delete with non-collapsed selection. **/
|
||||
synthesizeKey("VK_LEFT", {});
|
||||
/* Strangely enough we need to hit "right arrow" three times to select two characters. */
|
||||
synthesizeKey("VK_RIGHT", {shiftKey:true});
|
||||
synthesizeKey("VK_RIGHT", {shiftKey:true});
|
||||
synthesizeKey("VK_RIGHT", {shiftKey:true});
|
||||
synthesizeKey("VK_DELETE", {});
|
||||
|
||||
/* We always expect to the delete the "tf" in "testfoo". */
|
||||
var expected = tests[i][1].replace("testfoo", "tesoo")
|
||||
.replace("test<b>foo", "tes<b>oo")
|
||||
.replace("test<span class=\"pre\">foo", "tes<span class=\"pre\">oo")
|
||||
.replace("test<span class=\"pre\"><b>foo", "tes<span class=\"pre\"><b>oo");
|
||||
is(theDiv.innerHTML, expected, "delete(non-collapsed): inner HTML for " + testName);
|
||||
}
|
||||
|
||||
SimpleTest.finish();
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -9,10 +9,17 @@ interface nsIEditor;
|
||||
interface nsITextServicesFilter;
|
||||
interface nsIEditorSpellCheckCallback;
|
||||
|
||||
[scriptable, uuid(c9e630b8-79fd-4546-b068-be1b2a84c347)]
|
||||
[scriptable, uuid(dd32ef3b-a7d8-43d1-9617-5f2dddbe29eb)]
|
||||
interface nsIEditorSpellCheck : nsISupports
|
||||
{
|
||||
|
||||
/**
|
||||
* Call this on any change in installed dictionaries to ensure that the spell
|
||||
* checker is not using a current dictionary which is no longer available.
|
||||
* If the current dictionary is no longer available, then pick another one.
|
||||
*/
|
||||
void checkCurrentDictionary();
|
||||
|
||||
/**
|
||||
* Returns true if we can enable spellchecking. If there are no available
|
||||
* dictionaries, this will return false.
|
||||
|
@ -2792,6 +2792,23 @@ GetLcovInfo(JSContext* cx, unsigned argc, Value* vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static bool
|
||||
SetRNGState(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (!args.requireAtLeast(cx, "SetRNGState", 1))
|
||||
return false;
|
||||
|
||||
double seed;
|
||||
if (!ToNumber(cx, args[0], &seed))
|
||||
return false;
|
||||
|
||||
cx->compartment()->rngState = static_cast<uint64_t>(seed) & RNG_MASK;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const JSFunctionSpecWithHelp TestingFunctions[] = {
|
||||
JS_FN_HELP("gc", ::GC, 0, 0,
|
||||
"gc([obj] | 'compartment' [, 'shrinking'])",
|
||||
@ -3241,6 +3258,12 @@ gc::ZealModeHelpText),
|
||||
" Generate LCOV tracefile for the given compartment. If no global are provided then\n"
|
||||
" the current global is used as the default one.\n"),
|
||||
|
||||
#ifdef DEBUG
|
||||
JS_FN_HELP("setRNGState", SetRNGState, 1, 0,
|
||||
"setRNGState(seed)",
|
||||
" Set this compartment's RNG state.\n"),
|
||||
#endif
|
||||
|
||||
JS_FS_HELP_END
|
||||
};
|
||||
|
||||
|
44
js/src/jit-test/tests/basic/math-random.js
Normal file
44
js/src/jit-test/tests/basic/math-random.js
Normal file
@ -0,0 +1,44 @@
|
||||
function test() {
|
||||
// With this seed, state won't be reset in 10000 iteration. The result is
|
||||
// deterministic and it can be used to check the correctness of
|
||||
// implementation.
|
||||
setRNGState(0x12341234);
|
||||
|
||||
function f() {
|
||||
let x = [];
|
||||
for (let i = 0; i < 10000; i++) {
|
||||
x.push(Math.random());
|
||||
}
|
||||
return x;
|
||||
}
|
||||
let x = f();
|
||||
assertEq(x[0], 0.3562073961260165);
|
||||
assertEq(x[10], 0.9777930699941514);
|
||||
assertEq(x[100], 0.9146259915430884);
|
||||
assertEq(x[1000], 0.315983055288946);
|
||||
assertEq(x[2000], 0.7132284805929497);
|
||||
assertEq(x[3000], 0.9621073641614717);
|
||||
assertEq(x[4000], 0.3928228025111996);
|
||||
assertEq(x[5000], 0.555710685962832);
|
||||
assertEq(x[6000], 0.5207553912782503);
|
||||
assertEq(x[7000], 0.08268413491723015);
|
||||
assertEq(x[8000], 0.031796243723989925);
|
||||
assertEq(x[9000], 0.900683320457098);
|
||||
assertEq(x[9999], 0.7750389203054577);
|
||||
|
||||
// With this seed, state will be reset before calculating high bits.
|
||||
// The result is nondeterministic, but it should be in [0, 1) range.
|
||||
setRNGState(0);
|
||||
x = f();
|
||||
assertEq(x[0] >= 0 && x[0] < 1, true);
|
||||
|
||||
// With this seed, high bits will be 0 and state will be reset before
|
||||
// calculating low bits. The result is also nondeterministic, but it should
|
||||
// be in [0, 1 / (1 << 26)) range.
|
||||
setRNGState(0x615c0e462aa9);
|
||||
x = f();
|
||||
assertEq(x[0] >= 0 && x[0] < 1 / (1 << 26), true);
|
||||
}
|
||||
|
||||
if (typeof setRNGState == "function")
|
||||
test();
|
@ -10304,5 +10304,128 @@ CodeGenerator::visitNewTarget(LNewTarget *ins)
|
||||
masm.bind(&done);
|
||||
}
|
||||
|
||||
// Out-of-line math_random_no_outparam call for LRandom.
|
||||
class OutOfLineRandom : public OutOfLineCodeBase<CodeGenerator>
|
||||
{
|
||||
LRandom* lir_;
|
||||
|
||||
public:
|
||||
explicit OutOfLineRandom(LRandom* lir)
|
||||
: lir_(lir)
|
||||
{ }
|
||||
|
||||
void accept(CodeGenerator* codegen) {
|
||||
codegen->visitOutOfLineRandom(this);
|
||||
}
|
||||
|
||||
LRandom* lir() const {
|
||||
return lir_;
|
||||
}
|
||||
};
|
||||
|
||||
static const uint64_t RNG_HIGH_MASK = (0xFFFFFFFFFFFFFFFFULL >>
|
||||
(RNG_STATE_WIDTH - RNG_HIGH_BITS)) << RNG_LOW_BITS;
|
||||
static const double RNG_DSCALE_INV = 1 / RNG_DSCALE;
|
||||
|
||||
void
|
||||
CodeGenerator::visitRandom(LRandom* ins)
|
||||
{
|
||||
FloatRegister output = ToFloatRegister(ins->output());
|
||||
Register JSCompartmentReg = ToRegister(ins->temp1());
|
||||
#ifdef JS_PUNBOX64
|
||||
Register64 rngStateReg = Register64(ToRegister(ins->tempMaybeEAX()));
|
||||
Register64 highReg = Register64(ToRegister(ins->tempMaybeEDX()));
|
||||
#else
|
||||
Register64 rngStateReg = Register64(ToRegister(ins->temp2()), ToRegister(ins->temp3()));
|
||||
Register64 highReg = Register64(ToRegister(ins->tempMaybeEAX()), ToRegister(ins->tempMaybeEDX()));
|
||||
#endif
|
||||
// tempReg is used only on x86.
|
||||
Register tempReg = ToRegister(ins->tempMaybeEAX());
|
||||
|
||||
// rngState = cx->compartment()->rngState;
|
||||
masm.loadJSContext(JSCompartmentReg);
|
||||
masm.loadPtr(Address(JSCompartmentReg, JSContext::offsetOfCompartment()), JSCompartmentReg);
|
||||
masm.load64(Address(JSCompartmentReg, JSCompartment::offsetOfRngState()), rngStateReg);
|
||||
|
||||
// if rngState == 0, escape from inlined code and call
|
||||
// math_random_no_outparam.
|
||||
OutOfLineRandom* ool = new(alloc()) OutOfLineRandom(ins);
|
||||
addOutOfLineCode(ool, ins->mir());
|
||||
masm.branchTest64(Assembler::Zero, rngStateReg, rngStateReg, tempReg, ool->entry());
|
||||
|
||||
// rngState = rngState * RNG_MULTIPLIER;
|
||||
masm.mul64(Imm64(RNG_MULTIPLIER), rngStateReg);
|
||||
|
||||
// rngState += RNG_ADDEND;
|
||||
masm.add64(Imm32(RNG_ADDEND), rngStateReg);
|
||||
|
||||
// rngState &= RNG_MASK;
|
||||
masm.and64(Imm64(RNG_MASK), rngStateReg);
|
||||
|
||||
// if rngState == 0, escape from inlined code and call
|
||||
// math_random_no_outparam.
|
||||
masm.branchTest64(Assembler::Zero, rngStateReg, rngStateReg, tempReg, ool->entry());
|
||||
|
||||
// high = (rngState >> (RNG_STATE_WIDTH - RNG_HIGH_BITS)) << RNG_LOW_BITS;
|
||||
masm.move64(rngStateReg, highReg);
|
||||
masm.lshift64(Imm32(RNG_LOW_BITS - (RNG_STATE_WIDTH - RNG_HIGH_BITS)), highReg);
|
||||
masm.and64(Imm64(RNG_HIGH_MASK), highReg);
|
||||
#ifdef JS_CODEGEN_X86
|
||||
// eax and edx are overwritten by mul64 on x86.
|
||||
masm.push64(highReg);
|
||||
#endif
|
||||
|
||||
// rngState = rngState * RNG_MULTIPLIER;
|
||||
masm.mul64(Imm64(RNG_MULTIPLIER), rngStateReg);
|
||||
|
||||
// rngState += RNG_ADDEND;
|
||||
masm.add64(Imm32(RNG_ADDEND), rngStateReg);
|
||||
|
||||
// rngState &= RNG_MASK;
|
||||
masm.and64(Imm64(RNG_MASK), rngStateReg);
|
||||
|
||||
// cx->compartment()->rngState = rngState;
|
||||
masm.store64(rngStateReg, Address(JSCompartmentReg, JSCompartment::offsetOfRngState()));
|
||||
|
||||
// low = rngState >> (RNG_STATE_WIDTH - RNG_LOW_BITS);
|
||||
const Register64& lowReg = rngStateReg;
|
||||
masm.rshift64(Imm32(RNG_STATE_WIDTH - RNG_LOW_BITS), lowReg);
|
||||
|
||||
// output = double(high | low);
|
||||
#ifdef JS_CODEGEN_X86
|
||||
masm.pop64(highReg);
|
||||
#endif
|
||||
masm.or64(highReg, lowReg);
|
||||
masm.convertUInt64ToDouble(lowReg, tempReg, output);
|
||||
|
||||
// output = output * RNG_DSCALE_INV;
|
||||
masm.mulDoublePtr(ImmPtr(&RNG_DSCALE_INV), tempReg, output);
|
||||
|
||||
masm.bind(ool->rejoin());
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitOutOfLineRandom(OutOfLineRandom* ool)
|
||||
{
|
||||
LRandom* ins = ool->lir();
|
||||
Register temp1 = ToRegister(ins->tempMaybeEAX());
|
||||
Register temp2 = ToRegister(ins->tempMaybeEDX());
|
||||
MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
|
||||
|
||||
LiveRegisterSet regs;
|
||||
setReturnDoubleRegs(®s);
|
||||
saveVolatile(regs);
|
||||
|
||||
masm.loadJSContext(temp1);
|
||||
|
||||
masm.setupUnalignedABICall(temp2);
|
||||
masm.passABIArg(temp1);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
|
||||
|
||||
restoreVolatile(regs);
|
||||
|
||||
masm.jump(ool->rejoin());
|
||||
}
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
@ -45,6 +45,7 @@ class OutOfLineIsCallable;
|
||||
class OutOfLineRegExpExec;
|
||||
class OutOfLineRegExpTest;
|
||||
class OutOfLineLambdaArrow;
|
||||
class OutOfLineRandom;
|
||||
|
||||
class CodeGenerator : public CodeGeneratorSpecific
|
||||
{
|
||||
@ -382,6 +383,9 @@ class CodeGenerator : public CodeGeneratorSpecific
|
||||
void visitAsmJSInterruptCheck(LAsmJSInterruptCheck* lir);
|
||||
void visitRecompileCheck(LRecompileCheck* ins);
|
||||
|
||||
void visitRandom(LRandom* ins);
|
||||
void visitOutOfLineRandom(OutOfLineRandom* ool);
|
||||
|
||||
IonScriptCounts* extractScriptCounts() {
|
||||
IonScriptCounts* counts = scriptCounts_;
|
||||
scriptCounts_ = nullptr; // prevent delete in dtor
|
||||
|
@ -680,6 +680,8 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||
inline void andPtr(Register src, Register dest) PER_ARCH;
|
||||
inline void andPtr(Imm32 imm, Register dest) PER_ARCH;
|
||||
|
||||
inline void and64(Imm64 imm, Register64 dest) PER_ARCH;
|
||||
|
||||
inline void or32(Register src, Register dest) PER_SHARED_ARCH;
|
||||
inline void or32(Imm32 imm, Register dest) PER_SHARED_ARCH;
|
||||
inline void or32(Imm32 imm, const Address& dest) PER_SHARED_ARCH;
|
||||
@ -687,12 +689,28 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||
inline void orPtr(Register src, Register dest) PER_ARCH;
|
||||
inline void orPtr(Imm32 imm, Register dest) PER_ARCH;
|
||||
|
||||
inline void or64(Register64 src, Register64 dest) PER_ARCH;
|
||||
|
||||
inline void xor32(Register src, Register dest) DEFINED_ON(x86_shared);
|
||||
inline void xor32(Imm32 imm, Register dest) PER_SHARED_ARCH;
|
||||
|
||||
inline void xorPtr(Register src, Register dest) PER_ARCH;
|
||||
inline void xorPtr(Imm32 imm, Register dest) PER_ARCH;
|
||||
|
||||
// ===============================================================
|
||||
// Shift functions
|
||||
|
||||
inline void lshiftPtr(Imm32 imm, Register dest) PER_ARCH;
|
||||
|
||||
inline void lshift64(Imm32 imm, Register64 dest) PER_ARCH;
|
||||
|
||||
inline void rshiftPtr(Imm32 imm, Register dest) PER_ARCH;
|
||||
inline void rshiftPtr(Imm32 imm, Register src, Register dest) DEFINED_ON(arm64);
|
||||
|
||||
inline void rshiftPtrArithmetic(Imm32 imm, Register dest) PER_ARCH;
|
||||
|
||||
inline void rshift64(Imm32 imm, Register64 dest) PER_ARCH;
|
||||
|
||||
//}}} check_macroassembler_style
|
||||
public:
|
||||
|
||||
|
@ -93,6 +93,26 @@ struct Register {
|
||||
}
|
||||
};
|
||||
|
||||
struct Register64
|
||||
{
|
||||
#ifdef JS_PUNBOX64
|
||||
Register reg;
|
||||
#else
|
||||
Register high;
|
||||
Register low;
|
||||
#endif
|
||||
|
||||
#ifdef JS_PUNBOX64
|
||||
explicit MOZ_CONSTEXPR Register64(Register r)
|
||||
: reg(r)
|
||||
{}
|
||||
#else
|
||||
MOZ_CONSTEXPR Register64(Register h, Register l)
|
||||
: high(h), low(l)
|
||||
{}
|
||||
#endif
|
||||
};
|
||||
|
||||
class RegisterDump
|
||||
{
|
||||
public:
|
||||
|
@ -10,7 +10,6 @@
|
||||
|
||||
#include "jscntxt.h"
|
||||
#include "jscompartment.h"
|
||||
#include "jsmath.h"
|
||||
#include "jsnum.h"
|
||||
|
||||
#include "jit/CodeGenerator.h"
|
||||
@ -2675,16 +2674,12 @@ CodeGeneratorARM::visitMemoryBarrier(LMemoryBarrier* ins)
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorARM::visitRandom(LRandom* ins)
|
||||
CodeGeneratorARM::setReturnDoubleRegs(LiveRegisterSet* regs)
|
||||
{
|
||||
Register temp = ToRegister(ins->temp());
|
||||
Register temp2 = ToRegister(ins->temp2());
|
||||
|
||||
masm.loadJSContext(temp);
|
||||
|
||||
masm.setupUnalignedABICall(temp2);
|
||||
masm.passABIArg(temp);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
|
||||
|
||||
MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
|
||||
MOZ_ASSERT(ReturnFloat32Reg.code_ == FloatRegisters::s0);
|
||||
MOZ_ASSERT(ReturnDoubleReg.code_ == FloatRegisters::s0);
|
||||
FloatRegister s1 = {FloatRegisters::s1, VFPRegister::Single};
|
||||
regs->add(ReturnFloat32Reg);
|
||||
regs->add(s1);
|
||||
regs->add(ReturnDoubleReg);
|
||||
}
|
||||
|
@ -216,7 +216,7 @@ class CodeGeneratorARM : public CodeGeneratorShared
|
||||
|
||||
void generateInvalidateEpilogue();
|
||||
|
||||
void visitRandom(LRandom* ins);
|
||||
void setReturnDoubleRegs(LiveRegisterSet* regs);
|
||||
|
||||
// Generating a result.
|
||||
template<typename S, typename T>
|
||||
|
@ -497,23 +497,6 @@ class LAsmJSAtomicBinopCallout : public LCallInstructionHelper<1, 2, 0>
|
||||
}
|
||||
};
|
||||
|
||||
// Math.random().
|
||||
class LRandom : public LCallInstructionHelper<1, 0, 2>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(Random)
|
||||
LRandom(const LDefinition& temp, const LDefinition& temp2) {
|
||||
setTemp(0, temp);
|
||||
setTemp(1, temp2);
|
||||
}
|
||||
const LDefinition* temp() {
|
||||
return getTemp(0);
|
||||
}
|
||||
const LDefinition* temp2() {
|
||||
return getTemp(1);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
|
@ -741,6 +741,10 @@ LIRGeneratorARM::visitSubstr(MSubstr* ins)
|
||||
void
|
||||
LIRGeneratorARM::visitRandom(MRandom* ins)
|
||||
{
|
||||
LRandom* lir = new(alloc()) LRandom(tempFixed(CallTempReg0), tempFixed(CallTempReg1));
|
||||
defineReturn(lir, ins);
|
||||
LRandom *lir = new(alloc()) LRandom(temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp());
|
||||
defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
|
||||
}
|
||||
|
@ -63,6 +63,13 @@ MacroAssembler::andPtr(Imm32 imm, Register dest)
|
||||
ma_and(imm, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::and64(Imm64 imm, Register64 dest)
|
||||
{
|
||||
and32(Imm32(imm.value & 0xFFFFFFFFL), dest.low);
|
||||
and32(Imm32((imm.value >> 32) & 0xFFFFFFFFL), dest.high);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::or32(Register src, Register dest)
|
||||
{
|
||||
@ -96,6 +103,13 @@ MacroAssembler::orPtr(Imm32 imm, Register dest)
|
||||
ma_orr(imm, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::or64(Register64 src, Register64 dest)
|
||||
{
|
||||
or32(src.low, dest.low);
|
||||
or32(src.high, dest.high);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::xor32(Imm32 imm, Register dest)
|
||||
{
|
||||
@ -114,6 +128,43 @@ MacroAssembler::xorPtr(Imm32 imm, Register dest)
|
||||
ma_eor(imm, dest);
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
// Shift functions
|
||||
|
||||
void
|
||||
MacroAssembler::lshiftPtr(Imm32 imm, Register dest)
|
||||
{
|
||||
ma_lsl(imm, dest, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::lshift64(Imm32 imm, Register64 dest)
|
||||
{
|
||||
as_mov(dest.high, lsl(dest.high, imm.value));
|
||||
as_orr(dest.high, dest.high, lsr(dest.low, 32 - imm.value));
|
||||
as_mov(dest.low, lsl(dest.low, imm.value));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::rshiftPtr(Imm32 imm, Register dest)
|
||||
{
|
||||
ma_lsr(imm, dest, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::rshiftPtrArithmetic(Imm32 imm, Register dest)
|
||||
{
|
||||
ma_asr(imm, dest, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::rshift64(Imm32 imm, Register64 dest)
|
||||
{
|
||||
as_mov(dest.low, lsr(dest.low, imm.value));
|
||||
as_orr(dest.low, dest.low, lsl(dest.high, 32 - imm.value));
|
||||
as_mov(dest.high, lsr(dest.high, imm.value));
|
||||
}
|
||||
|
||||
//}}} check_macroassembler_style
|
||||
// ===============================================================
|
||||
|
||||
|
@ -90,6 +90,19 @@ MacroAssemblerARM::convertUInt32ToDouble(Register src, FloatRegister dest_)
|
||||
as_vcvt(dest, dest.uintOverlay());
|
||||
}
|
||||
|
||||
static const double TO_DOUBLE_HIGH_SCALE = 0x100000000;
|
||||
|
||||
void
|
||||
MacroAssemblerARMCompat::convertUInt64ToDouble(Register64 src, Register temp, FloatRegister dest)
|
||||
{
|
||||
convertUInt32ToDouble(src.high, dest);
|
||||
movePtr(ImmPtr(&TO_DOUBLE_HIGH_SCALE), ScratchRegister);
|
||||
loadDouble(Address(ScratchRegister, 0), ScratchDoubleReg);
|
||||
mulDouble(ScratchDoubleReg, dest);
|
||||
convertUInt32ToDouble(src.low, ScratchDoubleReg);
|
||||
addDouble(ScratchDoubleReg, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerARM::convertUInt32ToFloat32(Register src, FloatRegister dest_)
|
||||
{
|
||||
@ -3373,6 +3386,22 @@ template void
|
||||
MacroAssemblerARMCompat::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const BaseIndex& dest,
|
||||
MIRType slotType);
|
||||
|
||||
|
||||
void
|
||||
MacroAssemblerARMCompat::branchTest64(Condition cond, Register64 lhs, Register64 rhs,
|
||||
Register temp, Label* label)
|
||||
{
|
||||
if (cond == Assembler::Zero) {
|
||||
MOZ_ASSERT(lhs.low == rhs.low);
|
||||
MOZ_ASSERT(lhs.high == rhs.high);
|
||||
mov(lhs.low, ScratchRegister);
|
||||
asMasm().or32(lhs.high, ScratchRegister);
|
||||
branchTestPtr(cond, ScratchRegister, ScratchRegister, label);
|
||||
} else {
|
||||
MOZ_CRASH("Unsupported condition");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerARMCompat::moveValue(const Value& val, Register type, Register data)
|
||||
{
|
||||
|
@ -987,6 +987,7 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
||||
subPtr(imm, lhs);
|
||||
branch32(cond, lhs, Imm32(0), label);
|
||||
}
|
||||
void branchTest64(Condition cond, Register64 lhs, Register64 rhs, Register temp, Label* label);
|
||||
void moveValue(const Value& val, Register type, Register data);
|
||||
|
||||
CodeOffsetJump jumpWithPatch(RepatchLabel* label, Condition cond = Always,
|
||||
@ -1203,6 +1204,11 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
||||
|
||||
void addPtr(Register src, Register dest);
|
||||
void addPtr(const Address& src, Register dest);
|
||||
void add64(Imm32 imm, Register64 dest) {
|
||||
ma_add(imm, dest.low, SetCC);
|
||||
ma_adc(Imm32(0), dest.high, LeaveCC);
|
||||
}
|
||||
void not32(Register reg);
|
||||
|
||||
void move32(Imm32 imm, Register dest);
|
||||
void move32(Register src, Register dest);
|
||||
@ -1212,6 +1218,10 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
||||
void movePtr(ImmPtr imm, Register dest);
|
||||
void movePtr(AsmJSImmPtr imm, Register dest);
|
||||
void movePtr(ImmGCPtr imm, Register dest);
|
||||
void move64(Register64 src, Register64 dest) {
|
||||
move32(src.low, dest.low);
|
||||
move32(src.high, dest.high);
|
||||
}
|
||||
|
||||
void load8SignExtend(const Address& address, Register dest);
|
||||
void load8SignExtend(const BaseIndex& src, Register dest);
|
||||
@ -1228,6 +1238,10 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
||||
void load32(const Address& address, Register dest);
|
||||
void load32(const BaseIndex& address, Register dest);
|
||||
void load32(AbsoluteAddress address, Register dest);
|
||||
void load64(const Address& address, Register64 dest) {
|
||||
load32(address, dest.low);
|
||||
load32(Address(address.base, address.offset + 4), dest.high);
|
||||
}
|
||||
|
||||
void loadPtr(const Address& address, Register dest);
|
||||
void loadPtr(const BaseIndex& src, Register dest);
|
||||
@ -1294,6 +1308,11 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
||||
|
||||
void store32_NoSecondScratch(Imm32 src, const Address& address);
|
||||
|
||||
void store64(Register64 src, Address address) {
|
||||
store32(src.low, address);
|
||||
store32(src.high, Address(address.base, address.offset + 4));
|
||||
}
|
||||
|
||||
template <typename T> void storePtr(ImmWord imm, T address);
|
||||
template <typename T> void storePtr(ImmPtr imm, T address);
|
||||
template <typename T> void storePtr(ImmGCPtr imm, T address);
|
||||
@ -1642,6 +1661,39 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
||||
void mulBy3(const Register& src, const Register& dest) {
|
||||
as_add(dest, src, lsl(src, 1));
|
||||
}
|
||||
void mul64(Imm64 imm, const Register64& dest) {
|
||||
// LOW32 = LOW(LOW(dest) * LOW(imm));
|
||||
// HIGH32 = LOW(HIGH(dest) * LOW(imm)) [multiply imm into upper bits]
|
||||
// + LOW(LOW(dest) * HIGH(imm)) [multiply dest into upper bits]
|
||||
// + HIGH(LOW(dest) * LOW(imm)) [carry]
|
||||
|
||||
// HIGH(dest) = LOW(HIGH(dest) * LOW(imm));
|
||||
ma_mov(Imm32(imm.value & 0xFFFFFFFFL), ScratchRegister);
|
||||
as_mul(dest.high, dest.high, ScratchRegister);
|
||||
|
||||
// high:low = LOW(dest) * LOW(imm);
|
||||
as_umull(secondScratchReg_, ScratchRegister, dest.low, ScratchRegister);
|
||||
|
||||
// HIGH(dest) += high;
|
||||
as_add(dest.high, dest.high, O2Reg(secondScratchReg_));
|
||||
|
||||
// HIGH(dest) += LOW(LOW(dest) * HIGH(imm));
|
||||
if (((imm.value >> 32) & 0xFFFFFFFFL) == 5)
|
||||
as_add(secondScratchReg_, dest.low, lsl(dest.low, 2));
|
||||
else
|
||||
MOZ_CRASH("Not supported imm");
|
||||
as_add(dest.high, dest.high, O2Reg(secondScratchReg_));
|
||||
|
||||
// LOW(dest) = low;
|
||||
ma_mov(ScratchRegister, dest.low);
|
||||
}
|
||||
|
||||
void convertUInt64ToDouble(Register64 src, Register temp, FloatRegister dest);
|
||||
void mulDoublePtr(ImmPtr imm, Register temp, FloatRegister dest) {
|
||||
movePtr(imm, ScratchRegister);
|
||||
loadDouble(Address(ScratchRegister, 0), ScratchDoubleReg);
|
||||
mulDouble(ScratchDoubleReg, dest);
|
||||
}
|
||||
|
||||
void setStackArg(Register reg, uint32_t arg);
|
||||
|
||||
@ -1664,16 +1716,6 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
||||
|
||||
void checkStackAlignment();
|
||||
|
||||
void rshiftPtr(Imm32 imm, Register dest) {
|
||||
ma_lsr(imm, dest, dest);
|
||||
}
|
||||
void rshiftPtrArithmetic(Imm32 imm, Register dest) {
|
||||
ma_asr(imm, dest, dest);
|
||||
}
|
||||
void lshiftPtr(Imm32 imm, Register dest) {
|
||||
ma_lsl(imm, dest, dest);
|
||||
}
|
||||
|
||||
// If source is a double, load it into dest. If source is int32, convert it
|
||||
// to double. Else, branch to failure.
|
||||
void ensureDouble(const ValueOperand& source, FloatRegister dest, Label* failure);
|
||||
|
@ -608,12 +608,6 @@ CodeGeneratorARM64::generateInvalidateEpilogue()
|
||||
MOZ_CRASH("generateInvalidateEpilogue");
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorARM64::visitRandom(LRandom* ins)
|
||||
{
|
||||
MOZ_CRASH("visitRandom");
|
||||
}
|
||||
|
||||
template <class U>
|
||||
Register
|
||||
getBase(U* mir)
|
||||
@ -732,3 +726,14 @@ CodeGeneratorARM64::visitNegF(LNegF* ins)
|
||||
{
|
||||
MOZ_CRASH("visitNegF");
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorARM64::setReturnDoubleRegs(LiveRegisterSet* regs)
|
||||
{
|
||||
MOZ_ASSERT(ReturnFloat32Reg.code_ == FloatRegisters::s0);
|
||||
MOZ_ASSERT(ReturnDoubleReg.code_ == FloatRegisters::d0);
|
||||
FloatRegister s1 = {FloatRegisters::s1, FloatRegisters::Single};
|
||||
regs->add(ReturnFloat32Reg);
|
||||
regs->add(s1);
|
||||
regs->add(ReturnDoubleReg);
|
||||
}
|
||||
|
@ -220,7 +220,7 @@ class CodeGeneratorARM64 : public CodeGeneratorShared
|
||||
|
||||
void generateInvalidateEpilogue();
|
||||
|
||||
void visitRandom(LRandom* ins);
|
||||
void setReturnDoubleRegs(LiveRegisterSet* regs);
|
||||
|
||||
protected:
|
||||
void postAsmJSCall(LAsmJSCall* lir) {
|
||||
|
@ -407,23 +407,6 @@ class LAsmJSLoadFuncPtr : public LInstructionHelper<1, 1, 1>
|
||||
}
|
||||
};
|
||||
|
||||
// Math.random().
|
||||
class LRandom : public LCallInstructionHelper<1, 0, 2>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(Random)
|
||||
LRandom(const LDefinition& temp, const LDefinition& temp2) {
|
||||
setTemp(0, temp);
|
||||
setTemp(1, temp2);
|
||||
}
|
||||
const LDefinition* temp() {
|
||||
return getTemp(0);
|
||||
}
|
||||
const LDefinition* temp2() {
|
||||
return getTemp(1);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
|
@ -304,5 +304,8 @@ LIRGeneratorARM64::visitSubstr(MSubstr* ins)
|
||||
void
|
||||
LIRGeneratorARM64::visitRandom(MRandom* ins)
|
||||
{
|
||||
MOZ_CRASH("visitRandom");
|
||||
LRandom *lir = new(alloc()) LRandom(temp(),
|
||||
temp(),
|
||||
temp());
|
||||
defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
|
||||
}
|
||||
|
@ -73,6 +73,15 @@ MacroAssembler::andPtr(Imm32 imm, Register dest)
|
||||
And(ARMRegister(dest, 64), ARMRegister(dest, 64), Operand(imm.value));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::and64(Imm64 imm, Register64 dest)
|
||||
{
|
||||
vixl::UseScratchRegisterScope temps(this);
|
||||
const Register scratch = temps.AcquireX().asUnsized();
|
||||
mov(ImmWord(imm.value), scratch);
|
||||
andPtr(scratch, dest.reg);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::or32(Imm32 imm, Register dest)
|
||||
{
|
||||
@ -108,6 +117,12 @@ MacroAssembler::orPtr(Imm32 imm, Register dest)
|
||||
Orr(ARMRegister(dest, 64), ARMRegister(dest, 64), Operand(imm.value));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::or64(Register64 src, Register64 dest)
|
||||
{
|
||||
orPtr(src.reg, dest.reg);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::xor32(Imm32 imm, Register dest)
|
||||
{
|
||||
@ -126,6 +141,45 @@ MacroAssembler::xorPtr(Imm32 imm, Register dest)
|
||||
Eor(ARMRegister(dest, 64), ARMRegister(dest, 64), Operand(imm.value));
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
// Shift functions
|
||||
|
||||
void
|
||||
MacroAssembler::lshiftPtr(Imm32 imm, Register dest)
|
||||
{
|
||||
Lsl(ARMRegister(dest, 64), ARMRegister(dest, 64), imm.value);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::lshift64(Imm32 imm, Register64 dest)
|
||||
{
|
||||
lshiftPtr(imm, dest.reg);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::rshiftPtr(Imm32 imm, Register dest)
|
||||
{
|
||||
Lsr(ARMRegister(dest, 64), ARMRegister(dest, 64), imm.value);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::rshiftPtr(Imm32 imm, Register src, Register dest)
|
||||
{
|
||||
Lsr(ARMRegister(dest, 64), ARMRegister(src, 64), imm.value);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::rshiftPtrArithmetic(Imm32 imm, Register dest)
|
||||
{
|
||||
Asr(ARMRegister(dest, 64), ARMRegister(dest, 64), imm.value);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::rshift64(Imm32 imm, Register64 dest)
|
||||
{
|
||||
rshiftPtr(imm, dest.reg);
|
||||
}
|
||||
|
||||
//}}} check_macroassembler_style
|
||||
// ===============================================================
|
||||
|
||||
|
@ -124,6 +124,13 @@ MacroAssemblerCompat::movePatchablePtr(ImmWord ptr, Register dest)
|
||||
(uint8_t*)&instructionScratch, literalAddr);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerCompat::loadPrivate(const Address& src, Register dest)
|
||||
{
|
||||
loadPtr(src, dest);
|
||||
asMasm().lshiftPtr(Imm32(1), dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerCompat::handleFailureWithHandlerTail(void* handler)
|
||||
{
|
||||
|
@ -784,6 +784,9 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
|
||||
BufferOffset load = movePatchablePtr(ImmPtr(imm.value), dest);
|
||||
writeDataRelocation(imm, load);
|
||||
}
|
||||
void move64(Register64 src, Register64 dest) {
|
||||
movePtr(src.reg, dest.reg);
|
||||
}
|
||||
|
||||
void mov(ImmWord imm, Register dest) {
|
||||
movePtr(imm, dest);
|
||||
@ -850,10 +853,7 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
|
||||
|
||||
Ldr(dest64, MemOperand(ARMRegister(base, 64), index64, vixl::LSL, scale));
|
||||
}
|
||||
void loadPrivate(const Address& src, Register dest) {
|
||||
loadPtr(src, dest);
|
||||
lshiftPtr(Imm32(1), dest);
|
||||
}
|
||||
void loadPrivate(const Address& src, Register dest);
|
||||
|
||||
void store8(Register src, const Address& address) {
|
||||
Strb(ARMRegister(src, 32), MemOperand(ARMRegister(address.base, 64), address.offset));
|
||||
@ -989,6 +989,10 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
|
||||
Str(scratch32, MemOperand(ARMRegister(address.base, 64), address.offset));
|
||||
}
|
||||
|
||||
void store64(Register64 src, Address address) {
|
||||
storePtr(src.reg, address);
|
||||
}
|
||||
|
||||
// SIMD.
|
||||
void loadInt32x1(const Address& addr, FloatRegister dest) { MOZ_CRASH("NYI"); }
|
||||
void loadInt32x1(const BaseIndex& addr, FloatRegister dest) { MOZ_CRASH("NYI"); }
|
||||
@ -1062,20 +1066,6 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
|
||||
branchPtr(cond, lhs, getStackPointer(), label);
|
||||
}
|
||||
|
||||
void rshiftPtr(Imm32 imm, Register dest) {
|
||||
Lsr(ARMRegister(dest, 64), ARMRegister(dest, 64), imm.value);
|
||||
}
|
||||
void rshiftPtr(Imm32 imm, Register src, Register dest) {
|
||||
Lsr(ARMRegister(dest, 64), ARMRegister(src, 64), imm.value);
|
||||
}
|
||||
|
||||
void rshiftPtrArithmetic(Imm32 imm, Register dest) {
|
||||
Asr(ARMRegister(dest, 64), ARMRegister(dest, 64), imm.value);
|
||||
}
|
||||
void lshiftPtr(Imm32 imm, Register dest) {
|
||||
Lsl(ARMRegister(dest, 64), ARMRegister(dest, 64), imm.value);
|
||||
}
|
||||
|
||||
void testPtr(Register lhs, Register rhs) {
|
||||
Tst(ARMRegister(lhs, 64), Operand(ARMRegister(rhs, 64)));
|
||||
}
|
||||
@ -1162,7 +1152,7 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
|
||||
}
|
||||
|
||||
void loadDouble(const Address& src, FloatRegister dest) {
|
||||
Ldr(ARMFPRegister(dest, 64), MemOperand(ARMRegister(src.base,64), src.offset));
|
||||
Ldr(ARMFPRegister(dest, 64), MemOperand(src));
|
||||
}
|
||||
void loadDouble(const BaseIndex& src, FloatRegister dest) {
|
||||
ARMRegister base(src.base, 64);
|
||||
@ -1306,6 +1296,9 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
|
||||
movePtr(ImmWord((uintptr_t)address.addr), scratch64.asUnsized());
|
||||
ldr(ARMRegister(dest, 32), MemOperand(scratch64));
|
||||
}
|
||||
void load64(const Address& address, Register64 dest) {
|
||||
loadPtr(address, dest.reg);
|
||||
}
|
||||
|
||||
void load8SignExtend(const Address& address, Register dest) {
|
||||
Ldrsb(ARMRegister(dest, 32), MemOperand(ARMRegister(address.base, 64), address.offset));
|
||||
@ -1366,6 +1359,9 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
|
||||
Adds(scratch32, scratch32, Operand(imm.value));
|
||||
Str(scratch32, MemOperand(ARMRegister(dest.base, 64), dest.offset));
|
||||
}
|
||||
void add64(Imm32 imm, Register64 dest) {
|
||||
Add(ARMRegister(dest.reg, 64), ARMRegister(dest.reg, 64), Operand(imm.value));
|
||||
}
|
||||
|
||||
void sub32(Imm32 imm, Register dest) {
|
||||
Sub(ARMRegister(dest, 32), ARMRegister(dest, 32), Operand(imm.value));
|
||||
@ -1911,6 +1907,9 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
|
||||
Cmp(ARMRegister(value.valueReg(), 64), Operand(scratch64));
|
||||
B(label, cond);
|
||||
}
|
||||
void branchTest64(Condition cond, Register64 lhs, Register64 rhs, Register temp, Label* label) {
|
||||
branchTestPtr(cond, lhs.reg, rhs.reg, label);
|
||||
}
|
||||
|
||||
void compareDouble(DoubleCondition cond, FloatRegister lhs, FloatRegister rhs) {
|
||||
Fcmp(ARMFPRegister(lhs, 64), ARMFPRegister(rhs, 64));
|
||||
@ -2958,6 +2957,27 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
|
||||
Add(xdest, xsrc, Operand(xsrc, vixl::LSL, 1));
|
||||
}
|
||||
|
||||
void mul64(Imm64 imm, const Register64& dest) {
|
||||
vixl::UseScratchRegisterScope temps(this);
|
||||
const ARMRegister scratch64 = temps.AcquireX();
|
||||
MOZ_ASSERT(dest.reg != scratch64.asUnsized());
|
||||
mov(ImmWord(imm.value), scratch64.asUnsized());
|
||||
Mul(ARMRegister(dest.reg, 64), ARMRegister(dest.reg, 64), scratch64);
|
||||
}
|
||||
|
||||
void convertUInt64ToDouble(Register64 src, Register temp, FloatRegister dest) {
|
||||
Ucvtf(ARMFPRegister(dest, 64), ARMRegister(src.reg, 64));
|
||||
}
|
||||
void mulDoublePtr(ImmPtr imm, Register temp, FloatRegister dest) {
|
||||
vixl::UseScratchRegisterScope temps(this);
|
||||
const Register scratch = temps.AcquireX().asUnsized();
|
||||
MOZ_ASSERT(temp != scratch);
|
||||
movePtr(imm, scratch);
|
||||
const ARMFPRegister scratchDouble = temps.AcquireD();
|
||||
Ldr(scratchDouble, MemOperand(Address(scratch, 0)));
|
||||
fmul(ARMFPRegister(dest, 64), ARMFPRegister(dest, 64), scratchDouble);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void branchAdd32(Condition cond, T src, Register dest, Label* label) {
|
||||
adds32(src, dest);
|
||||
|
@ -284,23 +284,6 @@ class LAsmJSLoadFuncPtr : public LInstructionHelper<1, 1, 0>
|
||||
}
|
||||
};
|
||||
|
||||
// Math.random().
|
||||
class LRandom : public LCallInstructionHelper<1, 0, 2>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(Random)
|
||||
LRandom(const LDefinition& temp, const LDefinition& temp2) {
|
||||
setTemp(0, temp);
|
||||
setTemp(1, temp2);
|
||||
}
|
||||
const LDefinition* temp() {
|
||||
return getTemp(0);
|
||||
}
|
||||
const LDefinition* temp2() {
|
||||
return getTemp(1);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
|
@ -415,6 +415,10 @@ LIRGeneratorMIPSShared::visitAtomicTypedArrayElementBinop(MAtomicTypedArrayEleme
|
||||
void
|
||||
LIRGeneratorMIPSShared::visitRandom(MRandom* ins)
|
||||
{
|
||||
LRandom* lir = new(alloc()) LRandom(tempFixed(CallTempReg0), tempFixed(CallTempReg1));
|
||||
defineReturn(lir, ins);
|
||||
LRandom *lir = new(alloc()) LRandom(temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp());
|
||||
defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
|
||||
}
|
||||
|
@ -10,7 +10,6 @@
|
||||
|
||||
#include "jscntxt.h"
|
||||
#include "jscompartment.h"
|
||||
#include "jsmath.h"
|
||||
#include "jsnum.h"
|
||||
|
||||
#include "jit/CodeGenerator.h"
|
||||
@ -2120,16 +2119,10 @@ CodeGeneratorMIPS::visitNegF(LNegF* ins)
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorMIPS::visitRandom(LRandom* ins)
|
||||
CodeGeneratorMIPS::setReturnDoubleRegs(LiveRegisterSet* regs)
|
||||
{
|
||||
Register temp = ToRegister(ins->temp());
|
||||
Register temp2 = ToRegister(ins->temp2());
|
||||
|
||||
masm.loadJSContext(temp);
|
||||
|
||||
masm.setupUnalignedABICall(temp2);
|
||||
masm.passABIArg(temp);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
|
||||
|
||||
MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
|
||||
MOZ_ASSERT(ReturnFloat32Reg.code_ == ReturnDoubleReg.code_);
|
||||
regs->add(ReturnFloat32Reg);
|
||||
regs->add(ReturnDoubleReg.singleOverlay(1));
|
||||
regs->add(ReturnDoubleReg);
|
||||
}
|
||||
|
@ -241,7 +241,7 @@ class CodeGeneratorMIPS : public CodeGeneratorShared
|
||||
|
||||
void generateInvalidateEpilogue();
|
||||
|
||||
void visitRandom(LRandom* ins);
|
||||
void setReturnDoubleRegs(LiveRegisterSet* regs);
|
||||
|
||||
protected:
|
||||
void visitEffectiveAddress(LEffectiveAddress* ins);
|
||||
|
@ -173,3 +173,14 @@ LIRGeneratorMIPS::lowerTruncateFToInt32(MTruncateToInt32* ins)
|
||||
|
||||
define(new(alloc()) LTruncateFToInt32(useRegister(opd), LDefinition::BogusTemp()), ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGeneratorMIPS::visitRandom(MRandom* ins)
|
||||
{
|
||||
LRandom *lir = new(alloc()) LRandom(temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp());
|
||||
defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
|
||||
}
|
||||
|
@ -61,6 +61,13 @@ MacroAssembler::andPtr(Imm32 imm, Register dest)
|
||||
ma_and(dest, imm);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::and64(Imm64 imm, Register64 dest)
|
||||
{
|
||||
and32(Imm32(imm.value & LOW_32_MASK), dest.low);
|
||||
and32(Imm32((imm.value >> 32) & LOW_32_MASK), dest.high);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::or32(Register src, Register dest)
|
||||
{
|
||||
@ -93,6 +100,13 @@ MacroAssembler::orPtr(Imm32 imm, Register dest)
|
||||
ma_or(dest, imm);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::or64(Register64 src, Register64 dest)
|
||||
{
|
||||
or32(src.low, dest.low);
|
||||
or32(src.high, dest.high);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::xor32(Imm32 imm, Register dest)
|
||||
{
|
||||
@ -111,6 +125,47 @@ MacroAssembler::xorPtr(Imm32 imm, Register dest)
|
||||
ma_xor(dest, imm);
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
// Shift functions
|
||||
|
||||
void
|
||||
MacroAssembler::lshiftPtr(Imm32 imm, Register dest)
|
||||
{
|
||||
ma_sll(dest, dest, imm);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::lshift64(Imm32 imm, Register64 dest)
|
||||
{
|
||||
ScratchRegisterScope scratch(*this);
|
||||
as_sll(dest.high, dest.high, imm.value);
|
||||
as_srl(scratch, dest.low, 32 - imm.value);
|
||||
as_or(dest.high, dest.high, scratch);
|
||||
as_sll(dest.low, dest.low, imm.value);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::rshiftPtr(Imm32 imm, Register dest)
|
||||
{
|
||||
ma_srl(dest, dest, imm);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::rshiftPtrArithmetic(Imm32 imm, Register dest)
|
||||
{
|
||||
ma_sra(dest, dest, imm);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::rshift64(Imm32 imm, Register64 dest)
|
||||
{
|
||||
ScratchRegisterScope scratch(*this);
|
||||
as_srl(dest.low, dest.low, imm.value);
|
||||
as_sll(scratch, dest.high, 32 - imm.value);
|
||||
as_or(dest.low, dest.low, scratch);
|
||||
as_srl(dest.high, dest.high, imm.value);
|
||||
}
|
||||
|
||||
//}}} check_macroassembler_style
|
||||
// ===============================================================
|
||||
|
||||
|
@ -77,6 +77,63 @@ MacroAssemblerMIPSCompat::convertUInt32ToDouble(Register src, FloatRegister dest
|
||||
as_addd(dest, dest, SecondScratchDoubleReg);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::mul64(Imm64 imm, const Register64& dest)
|
||||
{
|
||||
// LOW32 = LOW(LOW(dest) * LOW(imm));
|
||||
// HIGH32 = LOW(HIGH(dest) * LOW(imm)) [multiply imm into upper bits]
|
||||
// + LOW(LOW(dest) * HIGH(imm)) [multiply dest into upper bits]
|
||||
// + HIGH(LOW(dest) * LOW(imm)) [carry]
|
||||
|
||||
// HIGH(dest) = LOW(HIGH(dest) * LOW(imm));
|
||||
ma_li(ScratchRegister, Imm32(imm.value & LOW_32_MASK));
|
||||
as_multu(dest.high, ScratchRegister);
|
||||
as_mflo(dest.high);
|
||||
|
||||
// mfhi:mflo = LOW(dest) * LOW(imm);
|
||||
as_multu(dest.low, ScratchRegister);
|
||||
|
||||
// HIGH(dest) += mfhi;
|
||||
as_mfhi(ScratchRegister);
|
||||
as_addu(dest.high, dest.high, ScratchRegister);
|
||||
|
||||
if (((imm.value >> 32) & LOW_32_MASK) == 5) {
|
||||
// Optimized case for Math.random().
|
||||
|
||||
// HIGH(dest) += LOW(LOW(dest) * HIGH(imm));
|
||||
as_sll(ScratchRegister, dest.low, 2);
|
||||
as_addu(ScratchRegister, ScratchRegister, dest.low);
|
||||
as_addu(dest.high, dest.high, ScratchRegister);
|
||||
|
||||
// LOW(dest) = mflo;
|
||||
as_mflo(dest.low);
|
||||
} else {
|
||||
// tmp = mflo
|
||||
as_mflo(SecondScratchReg);
|
||||
|
||||
// HIGH(dest) += LOW(LOW(dest) * HIGH(imm));
|
||||
ma_li(ScratchRegister, Imm32((imm.value >> 32) & LOW_32_MASK));
|
||||
as_multu(dest.low, ScratchRegister);
|
||||
as_mflo(ScratchRegister);
|
||||
as_addu(dest.high, dest.high, ScratchRegister);
|
||||
|
||||
// LOW(dest) = tmp;
|
||||
ma_move(dest.low, SecondScratchReg);
|
||||
}
|
||||
}
|
||||
|
||||
static const double TO_DOUBLE_HIGH_SCALE = 0x100000000;
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::convertUInt64ToDouble(Register64 src, Register temp, FloatRegister dest)
|
||||
{
|
||||
convertUInt32ToDouble(src.high, dest);
|
||||
loadConstantDouble(TO_DOUBLE_HIGH_SCALE, ScratchDoubleReg);
|
||||
mulDouble(ScratchDoubleReg, dest);
|
||||
convertUInt32ToDouble(src.low, ScratchDoubleReg);
|
||||
addDouble(ScratchDoubleReg, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::convertUInt32ToFloat32(Register src, FloatRegister dest)
|
||||
{
|
||||
@ -2558,6 +2615,20 @@ MacroAssemblerMIPSCompat::branchTestBooleanTruthy(bool b, const ValueOperand& op
|
||||
ma_b(operand.payloadReg(), operand.payloadReg(), label, b ? NonZero : Zero);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::branchTest64(Condition cond, Register64 lhs, Register64 rhs,
|
||||
Register temp, Label* label)
|
||||
{
|
||||
if (cond == Assembler::Zero) {
|
||||
MOZ_ASSERT(lhs.low == rhs.low);
|
||||
MOZ_ASSERT(lhs.high == rhs.high);
|
||||
as_or(ScratchRegister, lhs.low, lhs.high);
|
||||
branchTestPtr(cond, ScratchRegister, ScratchRegister, label);
|
||||
} else {
|
||||
MOZ_CRASH("Unsupported condition");
|
||||
}
|
||||
}
|
||||
|
||||
Register
|
||||
MacroAssemblerMIPSCompat::extractObject(const Address& address, Register scratch)
|
||||
{
|
||||
|
@ -65,6 +65,10 @@ static Register CallReg = t9;
|
||||
static const int defaultShift = 3;
|
||||
static_assert(1 << defaultShift == sizeof(JS::Value), "The defaultShift is wrong");
|
||||
|
||||
static const uint32_t LOW_32_MASK = (1LL << 32) - 1;
|
||||
static const int32_t LOW_32_OFFSET = 0;
|
||||
static const int32_t HIGH_32_OFFSET = 4;
|
||||
|
||||
class MacroAssemblerMIPS : public Assembler
|
||||
{
|
||||
protected:
|
||||
@ -676,6 +680,8 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
||||
loadPtr(lhs, SecondScratchReg);
|
||||
branchTestPtr(cond, SecondScratchReg, imm, label);
|
||||
}
|
||||
void branchTest64(Condition cond, Register64 lhs, Register64 rhs, Register temp,
|
||||
Label* label);
|
||||
void branchPtr(Condition cond, Register lhs, Register rhs, Label* label) {
|
||||
ma_b(lhs, rhs, label, cond);
|
||||
}
|
||||
@ -1101,6 +1107,11 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
||||
void add32(Register src, Register dest);
|
||||
void add32(Imm32 imm, Register dest);
|
||||
void add32(Imm32 imm, const Address& dest);
|
||||
void add64(Imm32 imm, Register64 dest) {
|
||||
as_addiu(dest.low, dest.low, imm.value);
|
||||
as_sltiu(ScratchRegister, dest.low, imm.value);
|
||||
as_addu(dest.high, dest.high, ScratchRegister);
|
||||
}
|
||||
void sub32(Imm32 imm, Register dest);
|
||||
void sub32(Register src, Register dest);
|
||||
|
||||
@ -1140,6 +1151,10 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
||||
|
||||
void move32(Imm32 imm, Register dest);
|
||||
void move32(Register src, Register dest);
|
||||
void move64(Register64 src, Register64 dest) {
|
||||
move32(src.low, dest.low);
|
||||
move32(src.high, dest.high);
|
||||
}
|
||||
|
||||
void movePtr(Register src, Register dest);
|
||||
void movePtr(ImmWord imm, Register dest);
|
||||
@ -1163,6 +1178,10 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
||||
void load32(const BaseIndex& address, Register dest);
|
||||
void load32(AbsoluteAddress address, Register dest);
|
||||
void load32(AsmJSAbsoluteAddress address, Register dest);
|
||||
void load64(const Address& address, Register64 dest) {
|
||||
load32(Address(address.base, address.offset + LOW_32_OFFSET), dest.low);
|
||||
load32(Address(address.base, address.offset + HIGH_32_OFFSET), dest.high);
|
||||
}
|
||||
|
||||
void loadPtr(const Address& address, Register dest);
|
||||
void loadPtr(const BaseIndex& src, Register dest);
|
||||
@ -1233,6 +1252,11 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
||||
store32(src, address);
|
||||
}
|
||||
|
||||
void store64(Register64 src, Address address) {
|
||||
store32(src.low, Address(address.base, address.offset + LOW_32_OFFSET));
|
||||
store32(src.high, Address(address.base, address.offset + HIGH_32_OFFSET));
|
||||
}
|
||||
|
||||
template <typename T> void storePtr(ImmWord imm, T address);
|
||||
template <typename T> void storePtr(ImmPtr imm, T address);
|
||||
template <typename T> void storePtr(ImmGCPtr imm, T address);
|
||||
@ -1302,6 +1326,15 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
||||
as_addu(dest, dest, src);
|
||||
}
|
||||
|
||||
void mul64(Imm64 imm, const Register64& dest);
|
||||
|
||||
void convertUInt64ToDouble(Register64 src, Register temp, FloatRegister dest);
|
||||
void mulDoublePtr(ImmPtr imm, Register temp, FloatRegister dest) {
|
||||
movePtr(imm, ScratchRegister);
|
||||
loadDouble(Address(ScratchRegister, 0), ScratchDoubleReg);
|
||||
mulDouble(ScratchDoubleReg, dest);
|
||||
}
|
||||
|
||||
void breakpoint();
|
||||
|
||||
void branchDouble(DoubleCondition cond, FloatRegister lhs, FloatRegister rhs,
|
||||
@ -1316,16 +1349,6 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
||||
void restoreStackPointer();
|
||||
static void calculateAlignedStackPointer(void** stackPointer);
|
||||
|
||||
void rshiftPtr(Imm32 imm, Register dest) {
|
||||
ma_srl(dest, dest, imm);
|
||||
}
|
||||
void rshiftPtrArithmetic(Imm32 imm, Register dest) {
|
||||
ma_sra(dest, dest, imm);
|
||||
}
|
||||
void lshiftPtr(Imm32 imm, Register dest) {
|
||||
ma_sll(dest, dest, imm);
|
||||
}
|
||||
|
||||
// If source is a double, load it into dest. If source is int32,
|
||||
// convert it to double. Else, branch to failure.
|
||||
void ensureDouble(const ValueOperand& source, FloatRegister dest, Label* failure);
|
||||
|
@ -51,6 +51,7 @@ class CodeGeneratorNone : public CodeGeneratorShared
|
||||
ValueOperand ToOutValue(LInstruction*) { MOZ_CRASH(); }
|
||||
ValueOperand ToTempValue(LInstruction*, size_t) { MOZ_CRASH(); }
|
||||
void generateInvalidateEpilogue() { MOZ_CRASH(); }
|
||||
void setReturnDoubleRegs(LiveRegisterSet* regs) { MOZ_CRASH(); }
|
||||
};
|
||||
|
||||
typedef CodeGeneratorNone CodeGeneratorSpecific;
|
||||
|
@ -112,7 +112,6 @@ class LModPowTwoI : public LInstructionHelper<1, 1, 0>
|
||||
class LGuardShape : public LInstruction {};
|
||||
class LGuardObjectGroup : public LInstruction {};
|
||||
class LMulI : public LInstruction {};
|
||||
class LRandom : public LInstructionHelper<1, 0, 5> {};
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
@ -87,7 +87,6 @@ class LIRGeneratorNone : public LIRGeneratorShared
|
||||
void visitSimdValueX4(MSimdValueX4* lir) { MOZ_CRASH(); }
|
||||
void visitSubstr(MSubstr*) { MOZ_CRASH(); }
|
||||
void visitSimdBinaryArith(js::jit::MSimdBinaryArith*) { MOZ_CRASH(); }
|
||||
void visitRandom(MRandom* ) { MOZ_CRASH(); }
|
||||
|
||||
};
|
||||
|
||||
|
@ -422,9 +422,6 @@ class MacroAssemblerNone : public Assembler
|
||||
template <typename T> void storeUnboxedValue(ConstantOrRegister, MIRType, T, MIRType) { MOZ_CRASH(); }
|
||||
template <typename T> void storeUnboxedPayload(ValueOperand value, T, size_t) { MOZ_CRASH(); }
|
||||
|
||||
void rshiftPtr(Imm32, Register) { MOZ_CRASH(); }
|
||||
void rshiftPtrArithmetic(Imm32, Register) { MOZ_CRASH(); }
|
||||
void lshiftPtr(Imm32, Register) { MOZ_CRASH(); }
|
||||
void convertUInt32ToDouble(Register, FloatRegister) { MOZ_CRASH(); }
|
||||
void convertUInt32ToFloat32(Register, FloatRegister) { MOZ_CRASH(); }
|
||||
void inc64(AbsoluteAddress) { MOZ_CRASH(); }
|
||||
|
@ -125,6 +125,15 @@ struct ImmWord
|
||||
{ }
|
||||
};
|
||||
|
||||
// Used for 64-bit immediates which do not require relocation.
|
||||
struct Imm64
|
||||
{
|
||||
uint64_t value;
|
||||
|
||||
explicit Imm64(uint64_t value) : value(value)
|
||||
{ }
|
||||
};
|
||||
|
||||
#ifdef DEBUG
|
||||
static inline bool
|
||||
IsCompilingAsmJS()
|
||||
|
@ -7176,6 +7176,57 @@ class LArrowNewTarget : public LInstructionHelper<BOX_PIECES, 1, 0>
|
||||
}
|
||||
};
|
||||
|
||||
// Math.random().
|
||||
#ifdef JS_PUNBOX64
|
||||
# define LRANDOM_NUM_TEMPS 3
|
||||
#else
|
||||
# define LRANDOM_NUM_TEMPS 5
|
||||
#endif
|
||||
|
||||
class LRandom : public LInstructionHelper<1, 0, LRANDOM_NUM_TEMPS>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(Random)
|
||||
LRandom(const LDefinition &tempMaybeEAX, const LDefinition &tempMaybeEDX,
|
||||
const LDefinition &temp1
|
||||
#ifndef JS_PUNBOX64
|
||||
, const LDefinition &temp2, const LDefinition &temp3
|
||||
#endif
|
||||
)
|
||||
{
|
||||
setTemp(0, tempMaybeEAX);
|
||||
setTemp(1, tempMaybeEDX);
|
||||
setTemp(2, temp1);
|
||||
#ifndef JS_PUNBOX64
|
||||
setTemp(3, temp2);
|
||||
setTemp(4, temp3);
|
||||
#endif
|
||||
}
|
||||
// On x86, following 2 methods return eax and edx necessary for mull.
|
||||
// On others, following 2 methods return ordinary temporary registers.
|
||||
const LDefinition* tempMaybeEAX() {
|
||||
return getTemp(0);
|
||||
}
|
||||
const LDefinition* tempMaybeEDX() {
|
||||
return getTemp(1);
|
||||
}
|
||||
const LDefinition *temp1() {
|
||||
return getTemp(2);
|
||||
}
|
||||
#ifndef JS_PUNBOX64
|
||||
const LDefinition *temp2() {
|
||||
return getTemp(3);
|
||||
}
|
||||
const LDefinition *temp3() {
|
||||
return getTemp(4);
|
||||
}
|
||||
#endif
|
||||
|
||||
MRandom* mir() const {
|
||||
return mir_->toRandom();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
|
782
js/src/jit/x64/BaseAssembler-x64.h
Normal file
782
js/src/jit/x64/BaseAssembler-x64.h
Normal file
@ -0,0 +1,782 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef jit_x64_BaseAssembler_x64_h
|
||||
#define jit_x64_BaseAssembler_x64_h
|
||||
|
||||
#include "jit/x86-shared/BaseAssembler-x86-shared.h"
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
namespace X86Encoding {
|
||||
|
||||
class BaseAssemblerX64 : public BaseAssembler
|
||||
{
|
||||
public:
|
||||
|
||||
// Arithmetic operations:
|
||||
|
||||
void addq_rr(RegisterID src, RegisterID dst)
|
||||
{
|
||||
spew("addq %s, %s", GPReg64Name(src), GPReg64Name(dst));
|
||||
m_formatter.oneByteOp64(OP_ADD_GvEv, src, dst);
|
||||
}
|
||||
|
||||
void addq_mr(int32_t offset, RegisterID base, RegisterID dst)
|
||||
{
|
||||
spew("addq " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst));
|
||||
m_formatter.oneByteOp64(OP_ADD_GvEv, offset, base, dst);
|
||||
}
|
||||
|
||||
void addq_mr(const void* addr, RegisterID dst)
|
||||
{
|
||||
spew("addq %p, %s", addr, GPReg64Name(dst));
|
||||
m_formatter.oneByteOp64(OP_ADD_GvEv, addr, dst);
|
||||
}
|
||||
|
||||
void addq_ir(int32_t imm, RegisterID dst)
|
||||
{
|
||||
spew("addq $%d, %s", imm, GPReg64Name(dst));
|
||||
if (CAN_SIGN_EXTEND_8_32(imm)) {
|
||||
m_formatter.oneByteOp64(OP_GROUP1_EvIb, dst, GROUP1_OP_ADD);
|
||||
m_formatter.immediate8s(imm);
|
||||
} else {
|
||||
if (dst == rax)
|
||||
m_formatter.oneByteOp64(OP_ADD_EAXIv);
|
||||
else
|
||||
m_formatter.oneByteOp64(OP_GROUP1_EvIz, dst, GROUP1_OP_ADD);
|
||||
m_formatter.immediate32(imm);
|
||||
}
|
||||
}
|
||||
|
||||
void addq_im(int32_t imm, int32_t offset, RegisterID base)
|
||||
{
|
||||
spew("addq $%d, " MEM_ob, imm, ADDR_ob(offset, base));
|
||||
if (CAN_SIGN_EXTEND_8_32(imm)) {
|
||||
m_formatter.oneByteOp64(OP_GROUP1_EvIb, offset, base, GROUP1_OP_ADD);
|
||||
m_formatter.immediate8s(imm);
|
||||
} else {
|
||||
m_formatter.oneByteOp64(OP_GROUP1_EvIz, offset, base, GROUP1_OP_ADD);
|
||||
m_formatter.immediate32(imm);
|
||||
}
|
||||
}
|
||||
|
||||
void addq_im(int32_t imm, const void* addr)
|
||||
{
|
||||
spew("addq $%d, %p", imm, addr);
|
||||
if (CAN_SIGN_EXTEND_8_32(imm)) {
|
||||
m_formatter.oneByteOp64(OP_GROUP1_EvIb, addr, GROUP1_OP_ADD);
|
||||
m_formatter.immediate8s(imm);
|
||||
} else {
|
||||
m_formatter.oneByteOp64(OP_GROUP1_EvIz, addr, GROUP1_OP_ADD);
|
||||
m_formatter.immediate32(imm);
|
||||
}
|
||||
}
|
||||
|
||||
void andq_rr(RegisterID src, RegisterID dst)
|
||||
{
|
||||
spew("andq %s, %s", GPReg64Name(src), GPReg64Name(dst));
|
||||
m_formatter.oneByteOp64(OP_AND_GvEv, src, dst);
|
||||
}
|
||||
|
||||
void andq_mr(int32_t offset, RegisterID base, RegisterID dst)
|
||||
{
|
||||
spew("andq " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst));
|
||||
m_formatter.oneByteOp64(OP_AND_GvEv, offset, base, dst);
|
||||
}
|
||||
|
||||
void andq_mr(int32_t offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
|
||||
{
|
||||
spew("andq " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), GPReg64Name(dst));
|
||||
m_formatter.oneByteOp64(OP_AND_GvEv, offset, base, index, scale, dst);
|
||||
}
|
||||
|
||||
void andq_mr(const void* addr, RegisterID dst)
|
||||
{
|
||||
spew("andq %p, %s", addr, GPReg64Name(dst));
|
||||
m_formatter.oneByteOp64(OP_AND_GvEv, addr, dst);
|
||||
}
|
||||
|
||||
void orq_mr(int32_t offset, RegisterID base, RegisterID dst)
|
||||
{
|
||||
spew("orq " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst));
|
||||
m_formatter.oneByteOp64(OP_OR_GvEv, offset, base, dst);
|
||||
}
|
||||
|
||||
void orq_mr(const void* addr, RegisterID dst)
|
||||
{
|
||||
spew("orq %p, %s", addr, GPReg64Name(dst));
|
||||
m_formatter.oneByteOp64(OP_OR_GvEv, addr, dst);
|
||||
}
|
||||
|
||||
void andq_ir(int32_t imm, RegisterID dst)
|
||||
{
|
||||
spew("andq $0x%" PRIx64 ", %s", int64_t(imm), GPReg64Name(dst));
|
||||
if (CAN_SIGN_EXTEND_8_32(imm)) {
|
||||
m_formatter.oneByteOp64(OP_GROUP1_EvIb, dst, GROUP1_OP_AND);
|
||||
m_formatter.immediate8s(imm);
|
||||
} else {
|
||||
if (dst == rax)
|
||||
m_formatter.oneByteOp64(OP_AND_EAXIv);
|
||||
else
|
||||
m_formatter.oneByteOp64(OP_GROUP1_EvIz, dst, GROUP1_OP_AND);
|
||||
m_formatter.immediate32(imm);
|
||||
}
|
||||
}
|
||||
|
||||
void negq_r(RegisterID dst)
|
||||
{
|
||||
spew("negq %s", GPReg64Name(dst));
|
||||
m_formatter.oneByteOp64(OP_GROUP3_Ev, dst, GROUP3_OP_NEG);
|
||||
}
|
||||
|
||||
void orq_rr(RegisterID src, RegisterID dst)
|
||||
{
|
||||
spew("orq %s, %s", GPReg64Name(src), GPReg64Name(dst));
|
||||
m_formatter.oneByteOp64(OP_OR_GvEv, src, dst);
|
||||
}
|
||||
|
||||
void orq_ir(int32_t imm, RegisterID dst)
|
||||
{
|
||||
spew("orq $0x%" PRIx64 ", %s", int64_t(imm), GPReg64Name(dst));
|
||||
if (CAN_SIGN_EXTEND_8_32(imm)) {
|
||||
m_formatter.oneByteOp64(OP_GROUP1_EvIb, dst, GROUP1_OP_OR);
|
||||
m_formatter.immediate8s(imm);
|
||||
} else {
|
||||
if (dst == rax)
|
||||
m_formatter.oneByteOp64(OP_OR_EAXIv);
|
||||
else
|
||||
m_formatter.oneByteOp64(OP_GROUP1_EvIz, dst, GROUP1_OP_OR);
|
||||
m_formatter.immediate32(imm);
|
||||
}
|
||||
}
|
||||
|
||||
void notq_r(RegisterID dst)
|
||||
{
|
||||
spew("notq %s", GPReg64Name(dst));
|
||||
m_formatter.oneByteOp64(OP_GROUP3_Ev, dst, GROUP3_OP_NOT);
|
||||
}
|
||||
|
||||
void subq_rr(RegisterID src, RegisterID dst)
|
||||
{
|
||||
spew("subq %s, %s", GPReg64Name(src), GPReg64Name(dst));
|
||||
m_formatter.oneByteOp64(OP_SUB_GvEv, src, dst);
|
||||
}
|
||||
|
||||
void subq_rm(RegisterID src, int32_t offset, RegisterID base)
|
||||
{
|
||||
spew("subq %s, " MEM_ob, GPReg64Name(src), ADDR_ob(offset, base));
|
||||
m_formatter.oneByteOp64(OP_SUB_EvGv, offset, base, src);
|
||||
}
|
||||
|
||||
void subq_mr(int32_t offset, RegisterID base, RegisterID dst)
|
||||
{
|
||||
spew("subq " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst));
|
||||
m_formatter.oneByteOp64(OP_SUB_GvEv, offset, base, dst);
|
||||
}
|
||||
|
||||
void subq_mr(const void* addr, RegisterID dst)
|
||||
{
|
||||
spew("subq %p, %s", addr, GPReg64Name(dst));
|
||||
m_formatter.oneByteOp64(OP_SUB_GvEv, addr, dst);
|
||||
}
|
||||
|
||||
void subq_ir(int32_t imm, RegisterID dst)
|
||||
{
|
||||
spew("subq $%d, %s", imm, GPReg64Name(dst));
|
||||
if (CAN_SIGN_EXTEND_8_32(imm)) {
|
||||
m_formatter.oneByteOp64(OP_GROUP1_EvIb, dst, GROUP1_OP_SUB);
|
||||
m_formatter.immediate8s(imm);
|
||||
} else {
|
||||
if (dst == rax)
|
||||
m_formatter.oneByteOp64(OP_SUB_EAXIv);
|
||||
else
|
||||
m_formatter.oneByteOp64(OP_GROUP1_EvIz, dst, GROUP1_OP_SUB);
|
||||
m_formatter.immediate32(imm);
|
||||
}
|
||||
}
|
||||
|
||||
void xorq_rr(RegisterID src, RegisterID dst)
|
||||
{
|
||||
spew("xorq %s, %s", GPReg64Name(src), GPReg64Name(dst));
|
||||
m_formatter.oneByteOp64(OP_XOR_GvEv, src, dst);
|
||||
}
|
||||
|
||||
void xorq_ir(int32_t imm, RegisterID dst)
|
||||
{
|
||||
spew("xorq $0x%" PRIx64 ", %s", int64_t(imm), GPReg64Name(dst));
|
||||
if (CAN_SIGN_EXTEND_8_32(imm)) {
|
||||
m_formatter.oneByteOp64(OP_GROUP1_EvIb, dst, GROUP1_OP_XOR);
|
||||
m_formatter.immediate8s(imm);
|
||||
} else {
|
||||
if (dst == rax)
|
||||
m_formatter.oneByteOp64(OP_XOR_EAXIv);
|
||||
else
|
||||
m_formatter.oneByteOp64(OP_GROUP1_EvIz, dst, GROUP1_OP_XOR);
|
||||
m_formatter.immediate32(imm);
|
||||
}
|
||||
}
|
||||
|
||||
void sarq_CLr(RegisterID dst)
|
||||
{
|
||||
spew("sarq %%cl, %s", GPReg64Name(dst));
|
||||
m_formatter.oneByteOp64(OP_GROUP2_EvCL, dst, GROUP2_OP_SAR);
|
||||
}
|
||||
|
||||
void sarq_ir(int32_t imm, RegisterID dst)
|
||||
{
|
||||
MOZ_ASSERT(imm < 64);
|
||||
spew("sarq $%d, %s", imm, GPReg64Name(dst));
|
||||
if (imm == 1)
|
||||
m_formatter.oneByteOp64(OP_GROUP2_Ev1, dst, GROUP2_OP_SAR);
|
||||
else {
|
||||
m_formatter.oneByteOp64(OP_GROUP2_EvIb, dst, GROUP2_OP_SAR);
|
||||
m_formatter.immediate8u(imm);
|
||||
}
|
||||
}
|
||||
|
||||
void shlq_ir(int32_t imm, RegisterID dst)
|
||||
{
|
||||
MOZ_ASSERT(imm < 64);
|
||||
spew("shlq $%d, %s", imm, GPReg64Name(dst));
|
||||
if (imm == 1)
|
||||
m_formatter.oneByteOp64(OP_GROUP2_Ev1, dst, GROUP2_OP_SHL);
|
||||
else {
|
||||
m_formatter.oneByteOp64(OP_GROUP2_EvIb, dst, GROUP2_OP_SHL);
|
||||
m_formatter.immediate8u(imm);
|
||||
}
|
||||
}
|
||||
|
||||
void shrq_ir(int32_t imm, RegisterID dst)
|
||||
{
|
||||
MOZ_ASSERT(imm < 64);
|
||||
spew("shrq $%d, %s", imm, GPReg64Name(dst));
|
||||
if (imm == 1)
|
||||
m_formatter.oneByteOp64(OP_GROUP2_Ev1, dst, GROUP2_OP_SHR);
|
||||
else {
|
||||
m_formatter.oneByteOp64(OP_GROUP2_EvIb, dst, GROUP2_OP_SHR);
|
||||
m_formatter.immediate8u(imm);
|
||||
}
|
||||
}
|
||||
|
||||
void imulq_rr(RegisterID src, RegisterID dst)
|
||||
{
|
||||
spew("imulq %s, %s", GPReg64Name(src), GPReg64Name(dst));
|
||||
m_formatter.twoByteOp64(OP2_IMUL_GvEv, src, dst);
|
||||
}
|
||||
|
||||
// Comparisons:
|
||||
|
||||
void cmpq_rr(RegisterID rhs, RegisterID lhs)
|
||||
{
|
||||
spew("cmpq %s, %s", GPReg64Name(rhs), GPReg64Name(lhs));
|
||||
m_formatter.oneByteOp64(OP_CMP_GvEv, rhs, lhs);
|
||||
}
|
||||
|
||||
void cmpq_rm(RegisterID rhs, int32_t offset, RegisterID base)
|
||||
{
|
||||
spew("cmpq %s, " MEM_ob, GPReg64Name(rhs), ADDR_ob(offset, base));
|
||||
m_formatter.oneByteOp64(OP_CMP_EvGv, offset, base, rhs);
|
||||
}
|
||||
|
||||
void cmpq_mr(int32_t offset, RegisterID base, RegisterID lhs)
|
||||
{
|
||||
spew("cmpq " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(lhs));
|
||||
m_formatter.oneByteOp64(OP_CMP_GvEv, offset, base, lhs);
|
||||
}
|
||||
|
||||
void cmpq_ir(int32_t rhs, RegisterID lhs)
|
||||
{
|
||||
if (rhs == 0) {
|
||||
testq_rr(lhs, lhs);
|
||||
return;
|
||||
}
|
||||
|
||||
spew("cmpq $0x%" PRIx64 ", %s", int64_t(rhs), GPReg64Name(lhs));
|
||||
if (CAN_SIGN_EXTEND_8_32(rhs)) {
|
||||
m_formatter.oneByteOp64(OP_GROUP1_EvIb, lhs, GROUP1_OP_CMP);
|
||||
m_formatter.immediate8s(rhs);
|
||||
} else {
|
||||
if (lhs == rax)
|
||||
m_formatter.oneByteOp64(OP_CMP_EAXIv);
|
||||
else
|
||||
m_formatter.oneByteOp64(OP_GROUP1_EvIz, lhs, GROUP1_OP_CMP);
|
||||
m_formatter.immediate32(rhs);
|
||||
}
|
||||
}
|
||||
|
||||
void cmpq_im(int32_t rhs, int32_t offset, RegisterID base)
|
||||
{
|
||||
spew("cmpq $0x%" PRIx64 ", " MEM_ob, int64_t(rhs), ADDR_ob(offset, base));
|
||||
if (CAN_SIGN_EXTEND_8_32(rhs)) {
|
||||
m_formatter.oneByteOp64(OP_GROUP1_EvIb, offset, base, GROUP1_OP_CMP);
|
||||
m_formatter.immediate8s(rhs);
|
||||
} else {
|
||||
m_formatter.oneByteOp64(OP_GROUP1_EvIz, offset, base, GROUP1_OP_CMP);
|
||||
m_formatter.immediate32(rhs);
|
||||
}
|
||||
}
|
||||
|
||||
void cmpq_im(int32_t rhs, int32_t offset, RegisterID base, RegisterID index, int scale)
|
||||
{
|
||||
spew("cmpq $0x%x, " MEM_obs, rhs, ADDR_obs(offset, base, index, scale));
|
||||
if (CAN_SIGN_EXTEND_8_32(rhs)) {
|
||||
m_formatter.oneByteOp64(OP_GROUP1_EvIb, offset, base, index, scale, GROUP1_OP_CMP);
|
||||
m_formatter.immediate8s(rhs);
|
||||
} else {
|
||||
m_formatter.oneByteOp64(OP_GROUP1_EvIz, offset, base, index, scale, GROUP1_OP_CMP);
|
||||
m_formatter.immediate32(rhs);
|
||||
}
|
||||
}
|
||||
void cmpq_im(int32_t rhs, const void* addr)
|
||||
{
|
||||
spew("cmpq $0x%" PRIx64 ", %p", int64_t(rhs), addr);
|
||||
if (CAN_SIGN_EXTEND_8_32(rhs)) {
|
||||
m_formatter.oneByteOp64(OP_GROUP1_EvIb, addr, GROUP1_OP_CMP);
|
||||
m_formatter.immediate8s(rhs);
|
||||
} else {
|
||||
m_formatter.oneByteOp64(OP_GROUP1_EvIz, addr, GROUP1_OP_CMP);
|
||||
m_formatter.immediate32(rhs);
|
||||
}
|
||||
}
|
||||
void cmpq_rm(RegisterID rhs, const void* addr)
|
||||
{
|
||||
spew("cmpq %s, %p", GPReg64Name(rhs), addr);
|
||||
m_formatter.oneByteOp64(OP_CMP_EvGv, addr, rhs);
|
||||
}
|
||||
|
||||
void testq_rr(RegisterID rhs, RegisterID lhs)
|
||||
{
|
||||
spew("testq %s, %s", GPReg64Name(rhs), GPReg64Name(lhs));
|
||||
m_formatter.oneByteOp64(OP_TEST_EvGv, lhs, rhs);
|
||||
}
|
||||
|
||||
void testq_ir(int32_t rhs, RegisterID lhs)
|
||||
{
|
||||
// If the mask fits in a 32-bit immediate, we can use testl with a
|
||||
// 32-bit subreg.
|
||||
if (CAN_ZERO_EXTEND_32_64(rhs)) {
|
||||
testl_ir(rhs, lhs);
|
||||
return;
|
||||
}
|
||||
spew("testq $0x%" PRIx64 ", %s", int64_t(rhs), GPReg64Name(lhs));
|
||||
if (lhs == rax)
|
||||
m_formatter.oneByteOp64(OP_TEST_EAXIv);
|
||||
else
|
||||
m_formatter.oneByteOp64(OP_GROUP3_EvIz, lhs, GROUP3_OP_TEST);
|
||||
m_formatter.immediate32(rhs);
|
||||
}
|
||||
|
||||
void testq_i32m(int32_t rhs, int32_t offset, RegisterID base)
|
||||
{
|
||||
spew("testq $0x%" PRIx64 ", " MEM_ob, int64_t(rhs), ADDR_ob(offset, base));
|
||||
m_formatter.oneByteOp64(OP_GROUP3_EvIz, offset, base, GROUP3_OP_TEST);
|
||||
m_formatter.immediate32(rhs);
|
||||
}
|
||||
|
||||
void testq_i32m(int32_t rhs, int32_t offset, RegisterID base, RegisterID index, int scale)
|
||||
{
|
||||
spew("testq $0x%4x, " MEM_obs, rhs, ADDR_obs(offset, base, index, scale));
|
||||
m_formatter.oneByteOp64(OP_GROUP3_EvIz, offset, base, index, scale, GROUP3_OP_TEST);
|
||||
m_formatter.immediate32(rhs);
|
||||
}
|
||||
|
||||
// Various move ops:
|
||||
|
||||
void xchgq_rr(RegisterID src, RegisterID dst)
|
||||
{
|
||||
spew("xchgq %s, %s", GPReg64Name(src), GPReg64Name(dst));
|
||||
m_formatter.oneByteOp64(OP_XCHG_GvEv, src, dst);
|
||||
}
|
||||
void xchgq_rm(RegisterID src, int32_t offset, RegisterID base)
|
||||
{
|
||||
spew("xchgq %s, " MEM_ob, GPReg64Name(src), ADDR_ob(offset, base));
|
||||
m_formatter.oneByteOp64(OP_XCHG_GvEv, offset, base, src);
|
||||
}
|
||||
void xchgq_rm(RegisterID src, int32_t offset, RegisterID base, RegisterID index, int scale)
|
||||
{
|
||||
spew("xchgq %s, " MEM_obs, GPReg64Name(src), ADDR_obs(offset, base, index, scale));
|
||||
m_formatter.oneByteOp64(OP_XCHG_GvEv, offset, base, index, scale, src);
|
||||
}
|
||||
|
||||
void movq_rr(RegisterID src, RegisterID dst)
|
||||
{
|
||||
spew("movq %s, %s", GPReg64Name(src), GPReg64Name(dst));
|
||||
m_formatter.oneByteOp64(OP_MOV_GvEv, src, dst);
|
||||
}
|
||||
|
||||
void movq_rm(RegisterID src, int32_t offset, RegisterID base)
|
||||
{
|
||||
spew("movq %s, " MEM_ob, GPReg64Name(src), ADDR_ob(offset, base));
|
||||
m_formatter.oneByteOp64(OP_MOV_EvGv, offset, base, src);
|
||||
}
|
||||
|
||||
void movq_rm_disp32(RegisterID src, int32_t offset, RegisterID base)
|
||||
{
|
||||
spew("movq %s, " MEM_o32b, GPReg64Name(src), ADDR_o32b(offset, base));
|
||||
m_formatter.oneByteOp64_disp32(OP_MOV_EvGv, offset, base, src);
|
||||
}
|
||||
|
||||
void movq_rm(RegisterID src, int32_t offset, RegisterID base, RegisterID index, int scale)
|
||||
{
|
||||
spew("movq %s, " MEM_obs, GPReg64Name(src), ADDR_obs(offset, base, index, scale));
|
||||
m_formatter.oneByteOp64(OP_MOV_EvGv, offset, base, index, scale, src);
|
||||
}
|
||||
|
||||
void movq_rm(RegisterID src, const void* addr)
|
||||
{
|
||||
if (src == rax && !IsAddressImmediate(addr)) {
|
||||
movq_EAXm(addr);
|
||||
return;
|
||||
}
|
||||
|
||||
spew("movq %s, %p", GPReg64Name(src), addr);
|
||||
m_formatter.oneByteOp64(OP_MOV_EvGv, addr, src);
|
||||
}
|
||||
|
||||
void movq_mEAX(const void* addr)
|
||||
{
|
||||
if (IsAddressImmediate(addr)) {
|
||||
movq_mr(addr, rax);
|
||||
return;
|
||||
}
|
||||
|
||||
spew("movq %p, %%rax", addr);
|
||||
m_formatter.oneByteOp64(OP_MOV_EAXOv);
|
||||
m_formatter.immediate64(reinterpret_cast<int64_t>(addr));
|
||||
}
|
||||
|
||||
void movq_EAXm(const void* addr)
|
||||
{
|
||||
if (IsAddressImmediate(addr)) {
|
||||
movq_rm(rax, addr);
|
||||
return;
|
||||
}
|
||||
|
||||
spew("movq %%rax, %p", addr);
|
||||
m_formatter.oneByteOp64(OP_MOV_OvEAX);
|
||||
m_formatter.immediate64(reinterpret_cast<int64_t>(addr));
|
||||
}
|
||||
|
||||
void movq_mr(int32_t offset, RegisterID base, RegisterID dst)
|
||||
{
|
||||
spew("movq " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst));
|
||||
m_formatter.oneByteOp64(OP_MOV_GvEv, offset, base, dst);
|
||||
}
|
||||
|
||||
void movq_mr_disp32(int32_t offset, RegisterID base, RegisterID dst)
|
||||
{
|
||||
spew("movq " MEM_o32b ", %s", ADDR_o32b(offset, base), GPReg64Name(dst));
|
||||
m_formatter.oneByteOp64_disp32(OP_MOV_GvEv, offset, base, dst);
|
||||
}
|
||||
|
||||
void movq_mr(int32_t offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
|
||||
{
|
||||
spew("movq " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), GPReg64Name(dst));
|
||||
m_formatter.oneByteOp64(OP_MOV_GvEv, offset, base, index, scale, dst);
|
||||
}
|
||||
|
||||
void movq_mr(const void* addr, RegisterID dst)
|
||||
{
|
||||
if (dst == rax && !IsAddressImmediate(addr)) {
|
||||
movq_mEAX(addr);
|
||||
return;
|
||||
}
|
||||
|
||||
spew("movq %p, %s", addr, GPReg64Name(dst));
|
||||
m_formatter.oneByteOp64(OP_MOV_GvEv, addr, dst);
|
||||
}
|
||||
|
||||
void leaq_mr(int32_t offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
|
||||
{
|
||||
spew("leaq " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), GPReg64Name(dst)),
|
||||
m_formatter.oneByteOp64(OP_LEA, offset, base, index, scale, dst);
|
||||
}
|
||||
|
||||
void movq_i32m(int32_t imm, int32_t offset, RegisterID base)
|
||||
{
|
||||
spew("movq $%d, " MEM_ob, imm, ADDR_ob(offset, base));
|
||||
m_formatter.oneByteOp64(OP_GROUP11_EvIz, offset, base, GROUP11_MOV);
|
||||
m_formatter.immediate32(imm);
|
||||
}
|
||||
|
||||
void movq_i32m(int32_t imm, int32_t offset, RegisterID base, RegisterID index, int scale)
|
||||
{
|
||||
spew("movq $%d, " MEM_obs, imm, ADDR_obs(offset, base, index, scale));
|
||||
m_formatter.oneByteOp64(OP_GROUP11_EvIz, offset, base, index, scale, GROUP11_MOV);
|
||||
m_formatter.immediate32(imm);
|
||||
}
|
||||
void movq_i32m(int32_t imm, const void* addr)
|
||||
{
|
||||
spew("movq $%d, %p", imm, addr);
|
||||
m_formatter.oneByteOp64(OP_GROUP11_EvIz, addr, GROUP11_MOV);
|
||||
m_formatter.immediate32(imm);
|
||||
}
|
||||
|
||||
// Note that this instruction sign-extends its 32-bit immediate field to 64
|
||||
// bits and loads the 64-bit value into a 64-bit register.
|
||||
//
|
||||
// Note also that this is similar to the movl_i32r instruction, except that
|
||||
// movl_i32r *zero*-extends its 32-bit immediate, and it has smaller code
|
||||
// size, so it's preferred for values which could use either.
|
||||
void movq_i32r(int32_t imm, RegisterID dst)
|
||||
{
|
||||
spew("movq $%d, %s", imm, GPRegName(dst));
|
||||
m_formatter.oneByteOp64(OP_GROUP11_EvIz, dst, GROUP11_MOV);
|
||||
m_formatter.immediate32(imm);
|
||||
}
|
||||
|
||||
void movq_i64r(int64_t imm, RegisterID dst)
|
||||
{
|
||||
spew("movabsq $0x%" PRIx64 ", %s", imm, GPReg64Name(dst));
|
||||
m_formatter.oneByteOp64(OP_MOV_EAXIv, dst);
|
||||
m_formatter.immediate64(imm);
|
||||
}
|
||||
|
||||
void movslq_rr(RegisterID src, RegisterID dst)
|
||||
{
|
||||
spew("movslq %s, %s", GPReg32Name(src), GPReg64Name(dst));
|
||||
m_formatter.oneByteOp64(OP_MOVSXD_GvEv, src, dst);
|
||||
}
|
||||
void movslq_mr(int32_t offset, RegisterID base, RegisterID dst)
|
||||
{
|
||||
spew("movslq " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst));
|
||||
m_formatter.oneByteOp64(OP_MOVSXD_GvEv, offset, base, dst);
|
||||
}
|
||||
void movslq_mr(int32_t offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
|
||||
{
|
||||
spew("movslq " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), GPReg64Name(dst));
|
||||
m_formatter.oneByteOp64(OP_MOVSXD_GvEv, offset, base, index, scale, dst);
|
||||
}
|
||||
|
||||
MOZ_WARN_UNUSED_RESULT JmpSrc
|
||||
movl_ripr(RegisterID dst)
|
||||
{
|
||||
m_formatter.oneByteRipOp(OP_MOV_GvEv, 0, (RegisterID)dst);
|
||||
JmpSrc label(m_formatter.size());
|
||||
spew("movl " MEM_o32r ", %s", ADDR_o32r(label.offset()), GPReg32Name(dst));
|
||||
return label;
|
||||
}
|
||||
|
||||
MOZ_WARN_UNUSED_RESULT JmpSrc
|
||||
movl_rrip(RegisterID src)
|
||||
{
|
||||
m_formatter.oneByteRipOp(OP_MOV_EvGv, 0, (RegisterID)src);
|
||||
JmpSrc label(m_formatter.size());
|
||||
spew("movl %s, " MEM_o32r "", GPReg32Name(src), ADDR_o32r(label.offset()));
|
||||
return label;
|
||||
}
|
||||
|
||||
MOZ_WARN_UNUSED_RESULT JmpSrc
|
||||
movq_ripr(RegisterID dst)
|
||||
{
|
||||
m_formatter.oneByteRipOp64(OP_MOV_GvEv, 0, dst);
|
||||
JmpSrc label(m_formatter.size());
|
||||
spew("movq " MEM_o32r ", %s", ADDR_o32r(label.offset()), GPRegName(dst));
|
||||
return label;
|
||||
}
|
||||
|
||||
void leaq_mr(int32_t offset, RegisterID base, RegisterID dst)
|
||||
{
|
||||
spew("leaq " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst));
|
||||
m_formatter.oneByteOp64(OP_LEA, offset, base, dst);
|
||||
}
|
||||
|
||||
MOZ_WARN_UNUSED_RESULT JmpSrc
|
||||
leaq_rip(RegisterID dst)
|
||||
{
|
||||
m_formatter.oneByteRipOp64(OP_LEA, 0, dst);
|
||||
JmpSrc label(m_formatter.size());
|
||||
spew("leaq " MEM_o32r ", %s", ADDR_o32r(label.offset()), GPRegName(dst));
|
||||
return label;
|
||||
}
|
||||
|
||||
// Flow control:
|
||||
|
||||
void jmp_rip(int ripOffset)
|
||||
{
|
||||
// rip-relative addressing.
|
||||
spew("jmp *%d(%%rip)", ripOffset);
|
||||
m_formatter.oneByteRipOp(OP_GROUP5_Ev, ripOffset, GROUP5_OP_JMPN);
|
||||
}
|
||||
|
||||
void immediate64(int64_t imm)
|
||||
{
|
||||
spew(".quad %lld", (long long)imm);
|
||||
m_formatter.immediate64(imm);
|
||||
}
|
||||
|
||||
// SSE operations:
|
||||
|
||||
void vcvtsq2sd_rr(RegisterID src1, XMMRegisterID src0, XMMRegisterID dst)
|
||||
{
|
||||
twoByteOpInt64Simd("vcvtsi2sd", VEX_SD, OP2_CVTSI2SD_VsdEd, src1, src0, dst);
|
||||
}
|
||||
void vcvtsq2ss_rr(RegisterID src1, XMMRegisterID src0, XMMRegisterID dst)
|
||||
{
|
||||
twoByteOpInt64Simd("vcvtsi2ss", VEX_SS, OP2_CVTSI2SD_VsdEd, src1, src0, dst);
|
||||
}
|
||||
|
||||
void vcvtsi2sdq_rr(RegisterID src, XMMRegisterID dst)
|
||||
{
|
||||
twoByteOpInt64Simd("vcvtsi2sdq", VEX_SD, OP2_CVTSI2SD_VsdEd, src, invalid_xmm, dst);
|
||||
}
|
||||
|
||||
void vcvttsd2sq_rr(XMMRegisterID src, RegisterID dst)
|
||||
{
|
||||
twoByteOpSimdInt64("vcvttsd2si", VEX_SD, OP2_CVTTSD2SI_GdWsd, src, dst);
|
||||
}
|
||||
|
||||
void vcvttss2sq_rr(XMMRegisterID src, RegisterID dst)
|
||||
{
|
||||
twoByteOpSimdInt64("vcvttss2si", VEX_SS, OP2_CVTTSD2SI_GdWsd, src, dst);
|
||||
}
|
||||
|
||||
void vmovq_rr(XMMRegisterID src, RegisterID dst)
|
||||
{
|
||||
// While this is called "vmovq", it actually uses the vmovd encoding
|
||||
// with a REX prefix modifying it to be 64-bit.
|
||||
twoByteOpSimdInt64("vmovq", VEX_PD, OP2_MOVD_EdVd, (XMMRegisterID)dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
void vmovq_rr(RegisterID src, XMMRegisterID dst)
|
||||
{
|
||||
// While this is called "vmovq", it actually uses the vmovd encoding
|
||||
// with a REX prefix modifying it to be 64-bit.
|
||||
twoByteOpInt64Simd("vmovq", VEX_PD, OP2_MOVD_VdEd, src, invalid_xmm, dst);
|
||||
}
|
||||
|
||||
MOZ_WARN_UNUSED_RESULT JmpSrc
|
||||
vmovsd_ripr(XMMRegisterID dst)
|
||||
{
|
||||
return twoByteRipOpSimd("vmovsd", VEX_SD, OP2_MOVSD_VsdWsd, invalid_xmm, dst);
|
||||
}
|
||||
MOZ_WARN_UNUSED_RESULT JmpSrc
|
||||
vmovss_ripr(XMMRegisterID dst)
|
||||
{
|
||||
return twoByteRipOpSimd("vmovss", VEX_SS, OP2_MOVSD_VsdWsd, invalid_xmm, dst);
|
||||
}
|
||||
MOZ_WARN_UNUSED_RESULT JmpSrc
|
||||
vmovsd_rrip(XMMRegisterID src)
|
||||
{
|
||||
return twoByteRipOpSimd("vmovsd", VEX_SD, OP2_MOVSD_WsdVsd, invalid_xmm, src);
|
||||
}
|
||||
MOZ_WARN_UNUSED_RESULT JmpSrc
|
||||
vmovss_rrip(XMMRegisterID src)
|
||||
{
|
||||
return twoByteRipOpSimd("vmovss", VEX_SS, OP2_MOVSD_WsdVsd, invalid_xmm, src);
|
||||
}
|
||||
MOZ_WARN_UNUSED_RESULT JmpSrc
|
||||
vmovdqa_rrip(XMMRegisterID src)
|
||||
{
|
||||
return twoByteRipOpSimd("vmovdqa", VEX_PD, OP2_MOVDQ_WdqVdq, invalid_xmm, src);
|
||||
}
|
||||
MOZ_WARN_UNUSED_RESULT JmpSrc
|
||||
vmovaps_rrip(XMMRegisterID src)
|
||||
{
|
||||
return twoByteRipOpSimd("vmovdqa", VEX_PS, OP2_MOVAPS_WsdVsd, invalid_xmm, src);
|
||||
}
|
||||
|
||||
MOZ_WARN_UNUSED_RESULT JmpSrc
|
||||
vmovaps_ripr(XMMRegisterID dst)
|
||||
{
|
||||
return twoByteRipOpSimd("vmovaps", VEX_PS, OP2_MOVAPS_VsdWsd, invalid_xmm, dst);
|
||||
}
|
||||
|
||||
MOZ_WARN_UNUSED_RESULT JmpSrc
|
||||
vmovdqa_ripr(XMMRegisterID dst)
|
||||
{
|
||||
return twoByteRipOpSimd("vmovdqa", VEX_PD, OP2_MOVDQ_VdqWdq, invalid_xmm, dst);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
MOZ_WARN_UNUSED_RESULT JmpSrc
|
||||
twoByteRipOpSimd(const char* name, VexOperandType ty, TwoByteOpcodeID opcode,
|
||||
XMMRegisterID src0, XMMRegisterID dst)
|
||||
{
|
||||
if (useLegacySSEEncoding(src0, dst)) {
|
||||
m_formatter.legacySSEPrefix(ty);
|
||||
m_formatter.twoByteRipOp(opcode, 0, dst);
|
||||
JmpSrc label(m_formatter.size());
|
||||
if (IsXMMReversedOperands(opcode))
|
||||
spew("%-11s%s, " MEM_o32r "", legacySSEOpName(name), XMMRegName(dst), ADDR_o32r(label.offset()));
|
||||
else
|
||||
spew("%-11s" MEM_o32r ", %s", legacySSEOpName(name), ADDR_o32r(label.offset()), XMMRegName(dst));
|
||||
return label;
|
||||
}
|
||||
|
||||
m_formatter.twoByteRipOpVex(ty, opcode, 0, src0, dst);
|
||||
JmpSrc label(m_formatter.size());
|
||||
if (src0 == invalid_xmm) {
|
||||
if (IsXMMReversedOperands(opcode))
|
||||
spew("%-11s%s, " MEM_o32r "", name, XMMRegName(dst), ADDR_o32r(label.offset()));
|
||||
else
|
||||
spew("%-11s" MEM_o32r ", %s", name, ADDR_o32r(label.offset()), XMMRegName(dst));
|
||||
} else {
|
||||
spew("%-11s" MEM_o32r ", %s, %s", name, ADDR_o32r(label.offset()), XMMRegName(src0), XMMRegName(dst));
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
void twoByteOpInt64Simd(const char* name, VexOperandType ty, TwoByteOpcodeID opcode,
|
||||
RegisterID rm, XMMRegisterID src0, XMMRegisterID dst)
|
||||
{
|
||||
if (useLegacySSEEncoding(src0, dst)) {
|
||||
if (IsXMMReversedOperands(opcode))
|
||||
spew("%-11s%s, %s", legacySSEOpName(name), XMMRegName(dst), GPRegName(rm));
|
||||
else
|
||||
spew("%-11s%s, %s", legacySSEOpName(name), GPRegName(rm), XMMRegName(dst));
|
||||
m_formatter.legacySSEPrefix(ty);
|
||||
m_formatter.twoByteOp64(opcode, rm, dst);
|
||||
return;
|
||||
}
|
||||
|
||||
if (src0 == invalid_xmm) {
|
||||
if (IsXMMReversedOperands(opcode))
|
||||
spew("%-11s%s, %s", name, XMMRegName(dst), GPRegName(rm));
|
||||
else
|
||||
spew("%-11s%s, %s", name, GPRegName(rm), XMMRegName(dst));
|
||||
} else {
|
||||
spew("%-11s%s, %s, %s", name, GPRegName(rm), XMMRegName(src0), XMMRegName(dst));
|
||||
}
|
||||
m_formatter.twoByteOpVex64(ty, opcode, rm, src0, dst);
|
||||
}
|
||||
|
||||
void twoByteOpSimdInt64(const char* name, VexOperandType ty, TwoByteOpcodeID opcode,
|
||||
XMMRegisterID rm, RegisterID dst)
|
||||
{
|
||||
if (useLegacySSEEncodingForOtherOutput()) {
|
||||
if (IsXMMReversedOperands(opcode))
|
||||
spew("%-11s%s, %s", legacySSEOpName(name), GPRegName(dst), XMMRegName(rm));
|
||||
else if (opcode == OP2_MOVD_EdVd)
|
||||
spew("%-11s%s, %s", legacySSEOpName(name), XMMRegName((XMMRegisterID)dst), GPRegName((RegisterID)rm));
|
||||
else
|
||||
spew("%-11s%s, %s", legacySSEOpName(name), XMMRegName(rm), GPRegName(dst));
|
||||
m_formatter.legacySSEPrefix(ty);
|
||||
m_formatter.twoByteOp64(opcode, (RegisterID)rm, dst);
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsXMMReversedOperands(opcode))
|
||||
spew("%-11s%s, %s", name, GPRegName(dst), XMMRegName(rm));
|
||||
else if (opcode == OP2_MOVD_EdVd)
|
||||
spew("%-11s%s, %s", name, XMMRegName((XMMRegisterID)dst), GPRegName((RegisterID)rm));
|
||||
else
|
||||
spew("%-11s%s, %s", name, XMMRegName(rm), GPRegName(dst));
|
||||
m_formatter.twoByteOpVex64(ty, opcode, (RegisterID)rm, invalid_xmm, (XMMRegisterID)dst);
|
||||
}
|
||||
};
|
||||
|
||||
typedef BaseAssemblerX64 BaseAssemblerSpecific;
|
||||
|
||||
} // namespace X86Encoding
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
#endif /* jit_x64_BaseAssembler_x64_h */
|
@ -841,129 +841,3 @@ CodeGeneratorX64::visitTruncateFToInt32(LTruncateFToInt32* ins)
|
||||
// call a stub if it fails.
|
||||
emitTruncateFloat32(input, output, ins->mir());
|
||||
}
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
// Out-of-line math_random_no_outparam call for LRandom.
|
||||
class OutOfLineRandom : public OutOfLineCodeBase<CodeGeneratorX64>
|
||||
{
|
||||
LRandom* lir_;
|
||||
|
||||
public:
|
||||
explicit OutOfLineRandom(LRandom* lir)
|
||||
: lir_(lir)
|
||||
{ }
|
||||
|
||||
void accept(CodeGeneratorX64* codegen) {
|
||||
codegen->visitOutOfLineRandom(this);
|
||||
}
|
||||
|
||||
LRandom* lir() const {
|
||||
return lir_;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
static const double RNG_DSCALE_INV = 1 / RNG_DSCALE;
|
||||
|
||||
void
|
||||
CodeGeneratorX64::visitRandom(LRandom* ins)
|
||||
{
|
||||
FloatRegister output = ToFloatRegister(ins->output());
|
||||
|
||||
Register JSCompartmentReg = ToRegister(ins->temp());
|
||||
Register rngStateReg = ToRegister(ins->temp2());
|
||||
Register highReg = ToRegister(ins->temp3());
|
||||
Register lowReg = ToRegister(ins->temp4());
|
||||
Register rngMaskReg = ToRegister(ins->temp5());
|
||||
|
||||
// rngState = cx->compartment()->rngState
|
||||
masm.loadJSContext(JSCompartmentReg);
|
||||
masm.loadPtr(Address(JSCompartmentReg, JSContext::offsetOfCompartment()), JSCompartmentReg);
|
||||
masm.loadPtr(Address(JSCompartmentReg, JSCompartment::offsetOfRngState()), rngStateReg);
|
||||
|
||||
// if rngState == 0, escape from inlined code and call
|
||||
// math_random_no_outparam.
|
||||
OutOfLineRandom* ool = new(alloc()) OutOfLineRandom(ins);
|
||||
addOutOfLineCode(ool, ins->mir());
|
||||
masm.branchTestPtr(Assembler::Zero, rngStateReg, rngStateReg, ool->entry());
|
||||
|
||||
// nextstate = rngState * RNG_MULTIPLIER;
|
||||
Register& rngMultiplierReg = lowReg;
|
||||
masm.movq(ImmWord(RNG_MULTIPLIER), rngMultiplierReg);
|
||||
masm.imulq(rngMultiplierReg, rngStateReg);
|
||||
|
||||
// nextstate += RNG_ADDEND;
|
||||
masm.addq(Imm32(RNG_ADDEND), rngStateReg);
|
||||
|
||||
// nextstate &= RNG_MASK;
|
||||
masm.movq(ImmWord(RNG_MASK), rngMaskReg);
|
||||
masm.andq(rngMaskReg, rngStateReg);
|
||||
|
||||
// rngState = nextstate
|
||||
|
||||
// if rngState == 0, escape from inlined code and call
|
||||
// math_random_no_outparam.
|
||||
masm.j(Assembler::Zero, ool->entry());
|
||||
|
||||
// high = (nextstate >> (RNG_STATE_WIDTH - RNG_HIGH_BITS)) << RNG_LOW_BITS;
|
||||
masm.movq(rngStateReg, highReg);
|
||||
masm.shrq(Imm32(RNG_STATE_WIDTH - RNG_HIGH_BITS), highReg);
|
||||
masm.shlq(Imm32(RNG_LOW_BITS), highReg);
|
||||
|
||||
// nextstate = rngState * RNG_MULTIPLIER;
|
||||
masm.imulq(rngMultiplierReg, rngStateReg);
|
||||
|
||||
// nextstate += RNG_ADDEND;
|
||||
masm.addq(Imm32(RNG_ADDEND), rngStateReg);
|
||||
|
||||
// nextstate &= RNG_MASK;
|
||||
masm.andq(rngMaskReg, rngStateReg);
|
||||
|
||||
// low = nextstate >> (RNG_STATE_WIDTH - RNG_LOW_BITS);
|
||||
masm.movq(rngStateReg, lowReg);
|
||||
masm.shrq(Imm32(RNG_STATE_WIDTH - RNG_LOW_BITS), lowReg);
|
||||
|
||||
// output = double(high | low);
|
||||
masm.orq(highReg, lowReg);
|
||||
masm.vcvtsi2sdq(lowReg, output);
|
||||
|
||||
// output = output * RNG_DSCALE_INV;
|
||||
Register& rngDscaleInvReg = lowReg;
|
||||
masm.movq(ImmPtr(&RNG_DSCALE_INV), rngDscaleInvReg);
|
||||
masm.vmulsd(Operand(rngDscaleInvReg, 0), output, output);
|
||||
|
||||
// cx->compartment()->rngState = nextstate
|
||||
masm.storePtr(rngStateReg, Address(JSCompartmentReg, JSCompartment::offsetOfRngState()));
|
||||
|
||||
masm.bind(ool->rejoin());
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorX64::visitOutOfLineRandom(OutOfLineRandom* ool)
|
||||
{
|
||||
LRandom* ins = ool->lir();
|
||||
Register temp = ToRegister(ins->temp());
|
||||
Register temp2 = ToRegister(ins->temp2());
|
||||
MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
|
||||
|
||||
LiveRegisterSet regs;
|
||||
regs.add(ReturnFloat32Reg);
|
||||
regs.add(ReturnDoubleReg);
|
||||
regs.add(ReturnInt32x4Reg);
|
||||
regs.add(ReturnFloat32x4Reg);
|
||||
saveVolatile(regs);
|
||||
|
||||
masm.loadJSContext(temp);
|
||||
|
||||
masm.setupUnalignedABICall(temp2);
|
||||
masm.passABIArg(temp);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
|
||||
|
||||
restoreVolatile(regs);
|
||||
|
||||
masm.jump(ool->rejoin());
|
||||
}
|
||||
|
@ -12,8 +12,6 @@
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
class OutOfLineRandom;
|
||||
|
||||
class CodeGeneratorX64 : public CodeGeneratorX86Shared
|
||||
{
|
||||
CodeGeneratorX64* thisFromCtor() {
|
||||
@ -61,8 +59,6 @@ class CodeGeneratorX64 : public CodeGeneratorX86Shared
|
||||
void visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc* ins);
|
||||
void visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble* lir);
|
||||
void visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32* lir);
|
||||
void visitRandom(LRandom* ins);
|
||||
void visitOutOfLineRandom(OutOfLineRandom* ool);
|
||||
};
|
||||
|
||||
typedef CodeGeneratorX64 CodeGeneratorSpecific;
|
||||
|
@ -99,41 +99,6 @@ class LAsmJSLoadFuncPtr : public LInstructionHelper<1, 1, 1>
|
||||
}
|
||||
};
|
||||
|
||||
// Math.random().
|
||||
class LRandom : public LInstructionHelper<1, 0, 5>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(Random)
|
||||
LRandom(const LDefinition &temp, const LDefinition &temp2, const LDefinition &temp3,
|
||||
const LDefinition &temp4, const LDefinition &temp5)
|
||||
{
|
||||
setTemp(0, temp);
|
||||
setTemp(1, temp2);
|
||||
setTemp(2, temp3);
|
||||
setTemp(3, temp4);
|
||||
setTemp(4, temp5);
|
||||
}
|
||||
const LDefinition* temp() {
|
||||
return getTemp(0);
|
||||
}
|
||||
const LDefinition* temp2() {
|
||||
return getTemp(1);
|
||||
}
|
||||
const LDefinition *temp3() {
|
||||
return getTemp(2);
|
||||
}
|
||||
const LDefinition *temp4() {
|
||||
return getTemp(3);
|
||||
}
|
||||
const LDefinition *temp5() {
|
||||
return getTemp(4);
|
||||
}
|
||||
|
||||
MRandom* mir() const {
|
||||
return mir_->toRandom();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
|
@ -337,8 +337,6 @@ void
|
||||
LIRGeneratorX64::visitRandom(MRandom* ins)
|
||||
{
|
||||
LRandom *lir = new(alloc()) LRandom(temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp());
|
||||
defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
|
||||
|
@ -29,6 +29,13 @@ MacroAssembler::andPtr(Imm32 imm, Register dest)
|
||||
andq(imm, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::and64(Imm64 imm, Register64 dest)
|
||||
{
|
||||
movq(ImmWord(uintptr_t(imm.value)), ScratchReg);
|
||||
andq(ScratchReg, dest.reg);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::orPtr(Register src, Register dest)
|
||||
{
|
||||
@ -41,6 +48,12 @@ MacroAssembler::orPtr(Imm32 imm, Register dest)
|
||||
orq(imm, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::or64(Register64 src, Register64 dest)
|
||||
{
|
||||
orq(src.reg, dest.reg);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::xorPtr(Register src, Register dest)
|
||||
{
|
||||
@ -53,6 +66,39 @@ MacroAssembler::xorPtr(Imm32 imm, Register dest)
|
||||
xorq(imm, dest);
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
// Shift functions
|
||||
|
||||
void
|
||||
MacroAssembler::lshiftPtr(Imm32 imm, Register dest)
|
||||
{
|
||||
shlq(imm, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::lshift64(Imm32 imm, Register64 dest)
|
||||
{
|
||||
shlq(imm, dest.reg);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::rshiftPtr(Imm32 imm, Register dest)
|
||||
{
|
||||
shrq(imm, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::rshiftPtrArithmetic(Imm32 imm, Register dest)
|
||||
{
|
||||
sarq(imm, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::rshift64(Imm32 imm, Register64 dest)
|
||||
{
|
||||
shrq(imm, dest.reg);
|
||||
}
|
||||
|
||||
//}}} check_macroassembler_style
|
||||
// ===============================================================
|
||||
|
||||
|
@ -183,6 +183,16 @@ MacroAssemblerX64::finish()
|
||||
MacroAssemblerX86Shared::finish();
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerX64::branchPrivatePtr(Condition cond, Address lhs, Register ptr, Label* label)
|
||||
{
|
||||
ScratchRegisterScope scratch(asMasm());
|
||||
if (ptr != scratch)
|
||||
movePtr(ptr, scratch);
|
||||
asMasm().rshiftPtr(Imm32(1), scratch);
|
||||
branchPtr(cond, lhs, scratch, label);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerX64::handleFailureWithHandlerTail(void* handler)
|
||||
{
|
||||
|
@ -604,6 +604,9 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
|
||||
void addPtr(const Address& src, Register dest) {
|
||||
addq(Operand(src), dest);
|
||||
}
|
||||
void add64(Imm32 imm, Register64 dest) {
|
||||
addq(imm, dest.reg);
|
||||
}
|
||||
void subPtr(Imm32 imm, Register dest) {
|
||||
subq(imm, dest);
|
||||
}
|
||||
@ -619,6 +622,10 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
|
||||
void mulBy3(const Register& src, const Register& dest) {
|
||||
lea(Operand(src, src, TimesTwo), dest);
|
||||
}
|
||||
void mul64(Imm64 imm, const Register64& dest) {
|
||||
movq(ImmWord(uintptr_t(imm.value)), ScratchReg);
|
||||
imulq(ScratchReg, dest.reg);
|
||||
}
|
||||
|
||||
void branch32(Condition cond, AbsoluteAddress lhs, Imm32 rhs, Label* label) {
|
||||
if (X86Encoding::IsAddressImmediate(lhs.addr)) {
|
||||
@ -685,14 +692,7 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
|
||||
branchPtr(cond, lhs, ImmWord(uintptr_t(ptr.value) >> 1), label);
|
||||
}
|
||||
|
||||
void branchPrivatePtr(Condition cond, Address lhs, Register ptr, Label* label) {
|
||||
ScratchRegisterScope scratch(asMasm());
|
||||
if (ptr != scratch)
|
||||
movePtr(ptr, scratch);
|
||||
rshiftPtr(Imm32(1), scratch);
|
||||
branchPtr(cond, lhs, scratch, label);
|
||||
}
|
||||
|
||||
void branchPrivatePtr(Condition cond, Address lhs, Register ptr, Label* label);
|
||||
template <typename T, typename S>
|
||||
void branchPtr(Condition cond, const T& lhs, const S& ptr, Label* label) {
|
||||
cmpPtr(Operand(lhs), ptr);
|
||||
@ -741,6 +741,10 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
|
||||
j(cond, label);
|
||||
}
|
||||
|
||||
void branchTest64(Condition cond, Register64 lhs, Register64 rhs, Register temp, Label* label) {
|
||||
branchTestPtr(cond, lhs.reg, rhs.reg, label);
|
||||
}
|
||||
|
||||
void movePtr(Register src, Register dest) {
|
||||
movq(src, dest);
|
||||
}
|
||||
@ -759,6 +763,9 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
|
||||
void movePtr(ImmGCPtr imm, Register dest) {
|
||||
movq(imm, dest);
|
||||
}
|
||||
void move64(Register64 src, Register64 dest) {
|
||||
movq(src.reg, dest.reg);
|
||||
}
|
||||
void loadPtr(AbsoluteAddress address, Register dest) {
|
||||
if (X86Encoding::IsAddressImmediate(address.addr)) {
|
||||
movq(Operand(address), dest);
|
||||
@ -790,6 +797,9 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
|
||||
load32(Address(scratch, 0x0), dest);
|
||||
}
|
||||
}
|
||||
void load64(const Address& address, Register64 dest) {
|
||||
movq(Operand(address), dest.reg);
|
||||
}
|
||||
template <typename T>
|
||||
void storePtr(ImmWord imm, T address) {
|
||||
if ((intptr_t)imm.value <= INT32_MAX && (intptr_t)imm.value >= INT32_MIN) {
|
||||
@ -837,14 +847,8 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
|
||||
store32(src, Address(scratch, 0x0));
|
||||
}
|
||||
}
|
||||
void rshiftPtr(Imm32 imm, Register dest) {
|
||||
shrq(imm, dest);
|
||||
}
|
||||
void rshiftPtrArithmetic(Imm32 imm, Register dest) {
|
||||
sarq(imm, dest);
|
||||
}
|
||||
void lshiftPtr(Imm32 imm, Register dest) {
|
||||
shlq(imm, dest);
|
||||
void store64(Register64 src, Address address) {
|
||||
movq(src.reg, Operand(address));
|
||||
}
|
||||
|
||||
void splitTag(Register src, Register dest) {
|
||||
@ -1370,6 +1374,15 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
|
||||
vcvtsq2ss(src, dest, dest);
|
||||
}
|
||||
|
||||
void convertUInt64ToDouble(Register64 src, Register temp, FloatRegister dest) {
|
||||
vcvtsi2sdq(src.reg, dest);
|
||||
}
|
||||
|
||||
void mulDoublePtr(ImmPtr imm, Register temp, FloatRegister dest) {
|
||||
movq(imm, ScratchReg);
|
||||
vmulsd(Operand(ScratchReg, 0), dest, dest);
|
||||
}
|
||||
|
||||
void inc64(AbsoluteAddress dest) {
|
||||
if (X86Encoding::IsAddressImmediate(dest.addr)) {
|
||||
addPtr(Imm32(1), Operand(dest));
|
||||
|
@ -10,7 +10,14 @@
|
||||
#include <cstddef>
|
||||
|
||||
#include "jit/shared/Assembler-shared.h"
|
||||
#include "jit/x86-shared/BaseAssembler-x86-shared.h"
|
||||
|
||||
#if defined(JS_CODEGEN_X86)
|
||||
# include "jit/x86/BaseAssembler-x86.h"
|
||||
#elif defined(JS_CODEGEN_X64)
|
||||
# include "jit/x64/BaseAssembler-x64.h"
|
||||
#else
|
||||
# error "Unknown architecture!"
|
||||
#endif
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
@ -261,7 +268,7 @@ class AssemblerX86Shared : public AssemblerShared
|
||||
}
|
||||
|
||||
protected:
|
||||
X86Encoding::BaseAssembler masm;
|
||||
X86Encoding::BaseAssemblerSpecific masm;
|
||||
|
||||
typedef X86Encoding::JmpSrc JmpSrc;
|
||||
typedef X86Encoding::JmpDst JmpDst;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -3644,5 +3644,18 @@ CodeGeneratorX86Shared::visitMemoryBarrier(LMemoryBarrier* ins)
|
||||
masm.storeLoadFence();
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorX86Shared::setReturnDoubleRegs(LiveRegisterSet* regs)
|
||||
{
|
||||
MOZ_ASSERT(ReturnFloat32Reg.encoding() == X86Encoding::xmm0);
|
||||
MOZ_ASSERT(ReturnDoubleReg.encoding() == X86Encoding::xmm0);
|
||||
MOZ_ASSERT(ReturnInt32x4Reg.encoding() == X86Encoding::xmm0);
|
||||
MOZ_ASSERT(ReturnFloat32x4Reg.encoding() == X86Encoding::xmm0);
|
||||
regs->add(ReturnFloat32Reg);
|
||||
regs->add(ReturnDoubleReg);
|
||||
regs->add(ReturnInt32x4Reg);
|
||||
regs->add(ReturnFloat32x4Reg);
|
||||
}
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
@ -300,6 +300,8 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared
|
||||
// Generating no result.
|
||||
template<typename S, typename T>
|
||||
void atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType, const S& value, const T& mem);
|
||||
|
||||
void setReturnDoubleRegs(LiveRegisterSet* regs);
|
||||
};
|
||||
|
||||
// An out-of-line bailout thunk.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user