Merge mozilla-central to fx-team

This commit is contained in:
Carsten "Tomcat" Book 2015-09-21 14:04:54 +02:00
commit 7696f0bc8e
125 changed files with 3943 additions and 2264 deletions

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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"
}

View File

@ -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"/>

View File

@ -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"/>

View File

@ -205,11 +205,7 @@ function beginTest() {
}
}
var prefs = [
[ "canvas.capturestream.enabled", true ],
];
SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
beginTest();
</script>
</pre>
</body>

View File

@ -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>

View File

@ -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

View File

@ -107,9 +107,6 @@ function beginTest() {
SimpleTest.waitForExplicitFinish();
var prefs = [
[ "canvas.capturestream.enabled", true ],
];
SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
beginTest();
</script>

View File

@ -171,9 +171,6 @@ function beginTest() {
SimpleTest.waitForExplicitFinish();
var prefs = [
[ "canvas.capturestream.enabled", true ],
];
SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
beginTest();
</script>

View File

@ -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)
{

View File

@ -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; }

View File

@ -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));
}

View File

@ -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

View File

@ -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

View File

@ -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; }

View File

@ -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;
};

View File

@ -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],

View File

@ -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>

View File

@ -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,

View File

@ -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)
{

View File

@ -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,

View File

@ -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.

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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)
{

View File

@ -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 {

View File

@ -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,

View File

@ -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,

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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();

View File

@ -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)
{

View File

@ -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);

View File

@ -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()
{

View File

@ -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;

View File

@ -6,6 +6,7 @@
XPIDL_SOURCES += [
'nsIMozNavigatorNetwork.idl',
'nsITCPSocketCallback.idl',
'nsIUDPSocketChild.idl',
]

View 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);
};

View File

@ -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!

View File

@ -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;
};

View File

@ -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");

View File

@ -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
}

View File

@ -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.

View File

@ -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]

View File

@ -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>

View File

@ -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.

View File

@ -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
};

View 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();

View File

@ -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(&regs);
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

View File

@ -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

View File

@ -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:

View File

@ -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:

View File

@ -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);
}

View File

@ -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>

View File

@ -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

View File

@ -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));
}

View File

@ -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
// ===============================================================

View File

@ -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)
{

View File

@ -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);

View File

@ -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);
}

View File

@ -220,7 +220,7 @@ class CodeGeneratorARM64 : public CodeGeneratorShared
void generateInvalidateEpilogue();
void visitRandom(LRandom* ins);
void setReturnDoubleRegs(LiveRegisterSet* regs);
protected:
void postAsmJSCall(LAsmJSCall* lir) {

View File

@ -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

View File

@ -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));
}

View File

@ -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
// ===============================================================

View File

@ -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)
{

View File

@ -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);

View File

@ -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

View File

@ -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));
}

View File

@ -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);
}

View File

@ -241,7 +241,7 @@ class CodeGeneratorMIPS : public CodeGeneratorShared
void generateInvalidateEpilogue();
void visitRandom(LRandom* ins);
void setReturnDoubleRegs(LiveRegisterSet* regs);
protected:
void visitEffectiveAddress(LEffectiveAddress* ins);

View File

@ -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));
}

View File

@ -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
// ===============================================================

View File

@ -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)
{

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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(); }
};

View File

@ -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(); }

View File

@ -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()

View File

@ -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

View 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 */

View File

@ -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());
}

View File

@ -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;

View File

@ -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

View File

@ -337,8 +337,6 @@ void
LIRGeneratorX64::visitRandom(MRandom* ins)
{
LRandom *lir = new(alloc()) LRandom(temp(),
temp(),
temp(),
temp(),
temp());
defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));

View File

@ -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
// ===============================================================

View File

@ -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)
{

View File

@ -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));

View File

@ -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

View File

@ -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

View File

@ -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