Merge inbound to m-c. a=merge

This commit is contained in:
Ryan VanderMeulen 2015-02-17 21:22:47 -05:00
commit 2e72549296
198 changed files with 10951 additions and 6127 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -239,7 +239,7 @@ static int nr_ice_component_initialize_udp(struct nr_ice_ctx_ *ctx,nr_ice_compon
&ctx->turn_servers[j].turn_server,component->component_id,&cand))
ABORT(r);
cand->state=NR_ICE_CAND_STATE_INITIALIZING; /* Don't start */
cand->done_cb=nr_ice_initialize_finished_cb;
cand->done_cb=nr_ice_gather_finished_cb;
cand->cb_arg=cand;
TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp);
@ -393,6 +393,11 @@ int nr_ice_component_initialize(struct nr_ice_ctx_ *ctx,nr_ice_component *compon
Data pwd;
nr_ice_candidate *cand;
if (component->candidate_ct) {
r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): component with id %d already has candidates, probably restarting gathering because of a new stream",ctx->label,component->component_id);
return(0);
}
r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): initializing component with id %d",ctx->label,component->component_id);
if(addr_ct==0){
@ -435,7 +440,7 @@ int nr_ice_component_initialize(struct nr_ice_ctx_ *ctx,nr_ice_component *compon
cand=TAILQ_FIRST(&component->candidates);
while(cand){
if(cand->state!=NR_ICE_CAND_STATE_INITIALIZING){
if(r=nr_ice_candidate_initialize(cand,nr_ice_initialize_finished_cb,cand)){
if(r=nr_ice_candidate_initialize(cand,nr_ice_gather_finished_cb,cand)){
if(r!=R_WOULDBLOCK){
ctx->uninitialized_candidates--;
cand->state=NR_ICE_CAND_STATE_FAILED;

View File

@ -157,7 +157,7 @@ int nr_ice_ctx_set_turn_servers(nr_ice_ctx *ctx,nr_ice_turn_server *servers,int
return(_status);
}
int nr_ice_ctx_set_local_addrs(nr_ice_ctx *ctx,nr_local_addr *addrs,int ct)
static int nr_ice_ctx_set_local_addrs(nr_ice_ctx *ctx,nr_local_addr *addrs,int ct)
{
int _status,i,r;
@ -300,6 +300,7 @@ int nr_ice_fetch_turn_servers(int ct, nr_ice_turn_server **out)
}
#endif /* USE_TURN */
#define MAXADDRS 100 // Ridiculously high
int nr_ice_ctx_create(char *label, UINT4 flags, nr_ice_ctx **ctxp)
{
nr_ice_ctx *ctx=0;
@ -457,9 +458,9 @@ int nr_ice_ctx_destroy(nr_ice_ctx **ctxp)
return(0);
}
void nr_ice_initialize_finished_cb(NR_SOCKET s, int h, void *cb_arg)
void nr_ice_gather_finished_cb(NR_SOCKET s, int h, void *cb_arg)
{
int r,_status;
int r;
nr_ice_candidate *cand=cb_arg;
nr_ice_ctx *ctx;
@ -497,7 +498,6 @@ void nr_ice_initialize_finished_cb(NR_SOCKET s, int h, void *cb_arg)
if(ctx->uninitialized_candidates==0){
r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): All candidates initialized",ctx->label);
ctx->state=NR_ICE_STATE_INITIALIZED;
if (ctx->done_cb) {
ctx->done_cb(0,0,ctx->cb_arg);
}
@ -532,47 +532,47 @@ static int nr_ice_ctx_pair_new_trickle_candidates(nr_ice_ctx *ctx, nr_ice_candid
}
#define MAXADDRS 100 // Ridiculously high
int nr_ice_initialize(nr_ice_ctx *ctx, NR_async_cb done_cb, void *cb_arg)
int nr_ice_gather(nr_ice_ctx *ctx, NR_async_cb done_cb, void *cb_arg)
{
int r,_status;
nr_ice_media_stream *stream;
nr_local_addr addrs[MAXADDRS];
int i,addr_ct;
r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Initializing candidates",ctx->label);
ctx->state=NR_ICE_STATE_INITIALIZING;
ctx->done_cb=done_cb;
ctx->cb_arg=cb_arg;
if (!ctx->local_addrs) {
/* First, gather all the local addresses we have */
if(r=nr_stun_find_local_addresses(addrs,MAXADDRS,&addr_ct)) {
r_log(LOG_ICE,LOG_ERR,"ICE(%s): unable to find local addresses",ctx->label);
ABORT(r);
}
/* Sort interfaces by preference */
if(ctx->interface_prioritizer) {
for(i=0;i<addr_ct;i++){
if(r=nr_interface_prioritizer_add_interface(ctx->interface_prioritizer,addrs+i)) {
r_log(LOG_ICE,LOG_ERR,"ICE(%s): unable to add interface ",ctx->label);
ABORT(r);
}
}
if(r=nr_interface_prioritizer_sort_preference(ctx->interface_prioritizer)) {
r_log(LOG_ICE,LOG_ERR,"ICE(%s): unable to sort interface by preference",ctx->label);
ABORT(r);
}
}
if (r=nr_ice_ctx_set_local_addrs(ctx,addrs,addr_ct)) {
ABORT(r);
}
}
if(STAILQ_EMPTY(&ctx->streams)) {
r_log(LOG_ICE,LOG_ERR,"ICE(%s): Missing streams to initialize",ctx->label);
ABORT(R_BAD_ARGS);
}
/* First, gather all the local addresses we have */
if(r=nr_stun_find_local_addresses(addrs,MAXADDRS,&addr_ct)) {
r_log(LOG_ICE,LOG_ERR,"ICE(%s): unable to find local addresses",ctx->label);
ABORT(r);
}
/* Sort interfaces by preference */
if(ctx->interface_prioritizer) {
for(i=0;i<addr_ct;i++){
if(r=nr_interface_prioritizer_add_interface(ctx->interface_prioritizer,addrs+i)) {
r_log(LOG_ICE,LOG_ERR,"ICE(%s): unable to add interface ",ctx->label);
ABORT(r);
}
}
if(r=nr_interface_prioritizer_sort_preference(ctx->interface_prioritizer)) {
r_log(LOG_ICE,LOG_ERR,"ICE(%s): unable to sort interface by preference",ctx->label);
ABORT(r);
}
}
if (r=nr_ice_ctx_set_local_addrs(ctx,addrs,addr_ct)) {
ABORT(r);
}
r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Initializing candidates",ctx->label);
ctx->done_cb=done_cb;
ctx->cb_arg=cb_arg;
/* Initialize all the media stream/component pairs */
stream=STAILQ_FIRST(&ctx->streams);
@ -586,7 +586,6 @@ int nr_ice_initialize(nr_ice_ctx *ctx, NR_async_cb done_cb, void *cb_arg)
if(ctx->uninitialized_candidates)
ABORT(R_WOULDBLOCK);
_status=0;
abort:
return(_status);

View File

@ -113,10 +113,6 @@ typedef STAILQ_HEAD(nr_ice_stun_id_head_,nr_ice_stun_id_) nr_ice_stun_id_head;
struct nr_ice_ctx_ {
UINT4 flags;
int state;
#define NR_ICE_STATE_CREATED 1
#define NR_ICE_STATE_INITIALIZING 2
#define NR_ICE_STATE_INITIALIZED 3
char *label;
char *ufrag;
@ -162,9 +158,9 @@ int nr_ice_ctx_create(char *label, UINT4 flags, nr_ice_ctx **ctxp);
#define NR_ICE_CTX_FLAGS_LITE (1<<3)
int nr_ice_ctx_destroy(nr_ice_ctx **ctxp);
int nr_ice_initialize(nr_ice_ctx *ctx, NR_async_cb done_cb, void *cb_arg);
int nr_ice_gather(nr_ice_ctx *ctx, NR_async_cb done_cb, void *cb_arg);
int nr_ice_add_candidate(nr_ice_ctx *ctx, nr_ice_candidate *cand);
void nr_ice_initialize_finished_cb(NR_SOCKET s, int h, void *cb_arg);
void nr_ice_gather_finished_cb(NR_SOCKET s, int h, void *cb_arg);
int nr_ice_add_media_stream(nr_ice_ctx *ctx,char *label,int components, nr_ice_media_stream **streamp);
int nr_ice_get_global_attributes(nr_ice_ctx *ctx,char ***attrsp, int *attrctp);
int nr_ice_ctx_deliver_packet(nr_ice_ctx *ctx, nr_ice_component *comp, nr_transport_addr *source_addr, UCHAR *data, int len);

View File

@ -627,7 +627,7 @@ int nr_ice_media_stream_component_nominated(nr_ice_media_stream *stream,nr_ice_c
}
/* Now tell the peer_ctx that we're done */
if(r=nr_ice_peer_ctx_stream_done(stream->pctx,stream))
if(r=nr_ice_peer_ctx_check_if_done(stream->pctx))
ABORT(r);
done:
@ -668,7 +668,7 @@ int nr_ice_media_stream_component_failed(nr_ice_media_stream *stream,nr_ice_comp
}
/* Now tell the peer_ctx that we're done */
if(r=nr_ice_peer_ctx_stream_done(stream->pctx,stream))
if(r=nr_ice_peer_ctx_check_if_done(stream->pctx))
ABORT(r);
_status=0;

View File

@ -348,6 +348,11 @@ static void nr_ice_peer_ctx_start_trickle_timer(nr_ice_peer_ctx *pctx)
{
UINT4 grace_period_timeout=0;
if(pctx->trickle_grace_period_timer) {
NR_async_timer_cancel(pctx->trickle_grace_period_timer);
pctx->trickle_grace_period_timer=0;
}
NR_reg_get_uint4(NR_ICE_REG_TRICKLE_GRACE_PERIOD,&grace_period_timeout);
if (grace_period_timeout) {
@ -506,27 +511,47 @@ int nr_ice_peer_ctx_start_checks2(nr_ice_peer_ctx *pctx, int allow_non_first)
nr_ice_media_stream *stream;
int started = 0;
/* Might have added some streams */
pctx->reported_done = 0;
NR_async_timer_cancel(pctx->done_cb_timer);
pctx->done_cb_timer = 0;
pctx->checks_started = 0;
if((r=nr_ice_peer_ctx_check_if_done(pctx))) {
r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) initial done check failed",pctx->ctx->label,pctx->label);
ABORT(r);
}
if (pctx->reported_done) {
r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) in %s all streams were done",pctx->ctx->label,pctx->label,__FUNCTION__);
return (0);
}
stream=STAILQ_FIRST(&pctx->peer_streams);
if(!stream)
ABORT(R_FAILED);
while (stream) {
if(!TAILQ_EMPTY(&stream->check_list))
break;
assert(stream->ice_state != NR_ICE_MEDIA_STREAM_UNPAIRED);
if(!allow_non_first){
/* This test applies if:
if (stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_FROZEN) {
if(!TAILQ_EMPTY(&stream->check_list))
break;
1. allow_non_first is 0 (i.e., non-trickle ICE)
2. the first stream has an empty check list.
if(!allow_non_first){
/* This test applies if:
But in the non-trickle ICE case, the other side should have provided
some candidates or ICE is pretty much not going to work and we're
just going to fail. Hence R_FAILED as opposed to R_NOT_FOUND and
immediate termination here.
*/
r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) first stream has empty check list",pctx->ctx->label,pctx->label);
ABORT(R_FAILED);
1. allow_non_first is 0 (i.e., non-trickle ICE)
2. the first stream has an empty check list.
But in the non-trickle ICE case, the other side should have provided
some candidates or ICE is pretty much not going to work and we're
just going to fail. Hence R_FAILED as opposed to R_NOT_FOUND and
immediate termination here.
*/
r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) first stream has empty check list",pctx->ctx->label,pctx->label);
ABORT(R_FAILED);
}
}
stream=STAILQ_NEXT(stream, entry);
@ -620,9 +645,9 @@ static void nr_ice_peer_ctx_fire_done(NR_SOCKET s, int how, void *cb_arg)
}
}
/* OK, a stream just went ready. Examine all the streams to see if we're
/* Examine all the streams to see if we're
maybe miraculously done */
int nr_ice_peer_ctx_stream_done(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream)
int nr_ice_peer_ctx_check_if_done(nr_ice_peer_ctx *pctx)
{
int _status;
nr_ice_media_stream *str;

View File

@ -84,7 +84,7 @@ int nr_ice_peer_ctx_start_checks2(nr_ice_peer_ctx *pctx, int allow_non_first);
void nr_ice_peer_ctx_stream_started_checks(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream);
int nr_ice_peer_ctx_dump_state(nr_ice_peer_ctx *pctx,FILE *out);
int nr_ice_peer_ctx_log_state(nr_ice_peer_ctx *pctx);
int nr_ice_peer_ctx_stream_done(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream);
int nr_ice_peer_ctx_check_if_done(nr_ice_peer_ctx *pctx);
int nr_ice_peer_ctx_find_component(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component_id, nr_ice_component **compp);
int nr_ice_peer_ctx_deliver_packet_maybe(nr_ice_peer_ctx *pctx, nr_ice_component *comp, nr_transport_addr *source_addr, UCHAR *data, int len);
int nr_ice_peer_ctx_disable_component(nr_ice_peer_ctx *pctx, nr_ice_media_stream *lstream, int component_id);

View File

@ -91,27 +91,24 @@ public:
// Manage tracks. We take shared ownership of any track.
virtual nsresult AddTrack(const RefPtr<JsepTrack>& track) = 0;
virtual nsresult RemoveTrack(size_t track_index) = 0;
virtual nsresult RemoveTrack(const std::string& streamId,
const std::string& trackId) = 0;
virtual nsresult ReplaceTrack(size_t track_index,
const RefPtr<JsepTrack>& track) = 0;
virtual size_t GetLocalTrackCount() const = 0;
virtual nsresult GetLocalTrack(size_t index,
RefPtr<JsepTrack>* track) const = 0;
virtual std::vector<RefPtr<JsepTrack>> GetLocalTracks() const = 0;
virtual size_t GetRemoteTrackCount() const = 0;
virtual nsresult GetRemoteTrack(size_t index,
RefPtr<JsepTrack>* track) const = 0;
virtual std::vector<RefPtr<JsepTrack>> GetRemoteTracks() const = 0;
virtual std::vector<RefPtr<JsepTrack>> GetRemoteTracksAdded() const = 0;
virtual std::vector<RefPtr<JsepTrack>> GetRemoteTracksRemoved() const = 0;
// Access the negotiated track pairs.
virtual size_t GetNegotiatedTrackPairCount() const = 0;
virtual nsresult GetNegotiatedTrackPair(size_t index,
const JsepTrackPair** pair) const = 0;
virtual std::vector<JsepTrackPair> GetNegotiatedTrackPairs() const = 0;
// Access transports.
virtual size_t GetTransportCount() const = 0;
virtual nsresult GetTransport(size_t index,
RefPtr<JsepTransport>* transport) const = 0;
virtual std::vector<RefPtr<JsepTransport>> GetTransports() const = 0;
// Basic JSEP operations.
virtual nsresult CreateOffer(const JsepOfferOptions& options,
@ -129,7 +126,8 @@ public:
uint16_t level) = 0;
virtual nsresult AddLocalIceCandidate(const std::string& candidate,
const std::string& mid,
uint16_t level) = 0;
uint16_t level,
bool* skipped) = 0;
virtual nsresult EndOfLocalCandidates(const std::string& defaultCandidateAddr,
uint16_t defaultCandidatePort,
uint16_t level) = 0;
@ -154,6 +152,8 @@ public:
return states[state];
}
virtual bool AllLocalTracksAreAssigned() const = 0;
protected:
const std::string mName;
JsepSignalingState mState;

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,7 @@
#ifndef _JSEPSESSIONIMPL_H_
#define _JSEPSESSIONIMPL_H_
#include <set>
#include <string>
#include <vector>
@ -45,12 +46,8 @@ public:
virtual nsresult AddTrack(const RefPtr<JsepTrack>& track) MOZ_OVERRIDE;
virtual nsresult
RemoveTrack(size_t trackIndex) MOZ_OVERRIDE
{
mLastError.clear();
MOZ_CRASH(); // Stub
}
virtual nsresult RemoveTrack(const std::string& streamId,
const std::string& trackId) MOZ_OVERRIDE;
virtual nsresult SetIceCredentials(const std::string& ufrag,
const std::string& pwd) MOZ_OVERRIDE;
@ -89,23 +86,15 @@ public:
MOZ_CRASH(); // Stub
}
virtual size_t
GetLocalTrackCount() const MOZ_OVERRIDE
{
return mLocalTracks.size();
}
virtual std::vector<RefPtr<JsepTrack>> GetLocalTracks() const MOZ_OVERRIDE;
virtual nsresult GetLocalTrack(size_t index,
RefPtr<JsepTrack>* track) const MOZ_OVERRIDE;
virtual std::vector<RefPtr<JsepTrack>> GetRemoteTracks() const MOZ_OVERRIDE;
virtual size_t
GetRemoteTrackCount() const MOZ_OVERRIDE
{
return mRemoteTracks.size();
}
virtual std::vector<RefPtr<JsepTrack>>
GetRemoteTracksAdded() const MOZ_OVERRIDE;
virtual nsresult GetRemoteTrack(size_t index,
RefPtr<JsepTrack>* track) const MOZ_OVERRIDE;
virtual std::vector<RefPtr<JsepTrack>>
GetRemoteTracksRemoved() const MOZ_OVERRIDE;
virtual nsresult CreateOffer(const JsepOfferOptions& options,
std::string* offer) MOZ_OVERRIDE;
@ -129,7 +118,8 @@ public:
virtual nsresult AddLocalIceCandidate(const std::string& candidate,
const std::string& mid,
uint16_t level) MOZ_OVERRIDE;
uint16_t level,
bool* skipped) MOZ_OVERRIDE;
virtual nsresult EndOfLocalCandidates(const std::string& defaultCandidateAddr,
uint16_t defaultCandidatePort,
@ -152,41 +142,19 @@ public:
}
// Access transports.
virtual size_t
GetTransportCount() const MOZ_OVERRIDE
virtual std::vector<RefPtr<JsepTransport>>
GetTransports() const MOZ_OVERRIDE
{
return mTransports.size();
return mTransports;
}
virtual nsresult
GetTransport(size_t index,
RefPtr<JsepTransport>* transport) const MOZ_OVERRIDE
virtual std::vector<JsepTrackPair>
GetNegotiatedTrackPairs() const MOZ_OVERRIDE
{
if (index >= mTransports.size())
return NS_ERROR_INVALID_ARG;
*transport = mTransports[index];
return NS_OK;
return mNegotiatedTrackPairs;
}
// Access the negotiated track pairs.
virtual size_t
GetNegotiatedTrackPairCount() const MOZ_OVERRIDE
{
return mNegotiatedTrackPairs.size();
}
virtual nsresult
GetNegotiatedTrackPair(size_t index, const JsepTrackPair** pair) const MOZ_OVERRIDE
{
if (index >= mNegotiatedTrackPairs.size())
return NS_ERROR_INVALID_ARG;
*pair = &mNegotiatedTrackPairs[index];
return NS_OK;
}
virtual bool AllLocalTracksAreAssigned() const MOZ_OVERRIDE;
private:
struct JsepDtlsFingerprint {
@ -197,6 +165,7 @@ private:
struct JsepSendingTrack {
RefPtr<JsepTrack> mTrack;
Maybe<size_t> mAssignedMLine;
bool mSetInLocalDescription;
};
struct JsepReceivingTrack {
@ -230,34 +199,44 @@ private:
nsresult SetRemoteDescriptionOffer(UniquePtr<Sdp> offer);
nsresult SetRemoteDescriptionAnswer(JsepSdpType type, UniquePtr<Sdp> answer);
nsresult ValidateLocalDescription(const Sdp& description);
nsresult ValidateRemoteDescription(const Sdp& description);
nsresult SetRemoteTracksFromDescription(const Sdp& remoteDescription);
// Non-const because we use our Uuid generator
nsresult CreateReceivingTrack(size_t mline,
const Sdp& sdp,
const SdpMediaSection& msection);
const SdpMediaSection& msection,
RefPtr<JsepTrack>* track);
nsresult HandleNegotiatedSession(const UniquePtr<Sdp>& local,
const UniquePtr<Sdp>& remote);
nsresult DetermineSendingDirection(SdpDirectionAttribute::Direction offer,
SdpDirectionAttribute::Direction answer,
bool* sending, bool* receiving);
nsresult AddTransportAttributes(SdpMediaSection* msection,
SdpSetupAttribute::Role dtlsRole);
// Non-const so it can assign m-line index to tracks
nsresult AddOfferMSectionsByType(SdpMediaSection::MediaType type,
Maybe<size_t> offerToReceive,
Sdp* sdp);
nsresult BindTrackToMsection(JsepSendingTrack* track,
SdpMediaSection* msection);
nsresult CreateReoffer(const Sdp& oldLocalSdp,
const Sdp& oldAnswer,
Sdp* newSdp);
void SetupBundle(Sdp* sdp) const;
void SetupMsidSemantic(const std::vector<std::string>& msids, Sdp* sdp) const;
nsresult GetIdsFromMsid(const Sdp& sdp,
const SdpMediaSection& msection,
std::string* streamId,
std::string* trackId);
nsresult GetRemoteIds(const Sdp& sdp,
const SdpMediaSection& msection,
std::string* streamId,
std::string* trackId);
nsresult GetMsids(const SdpMediaSection& msection,
std::vector<SdpMsidAttributeList::Msid>* msids);
nsresult CreateOfferMSection(SdpMediaSection::MediaType type,
SdpDirectionAttribute::Direction direction,
SdpMediaSection::Protocol proto,
Sdp* sdp);
nsresult GetFreeMsectionForSend(SdpMediaSection::MediaType type,
Sdp* sdp,
SdpMediaSection** msection);
nsresult CreateAnswerMSection(const JsepAnswerOptions& options,
size_t mlineIndex,
const SdpMediaSection& remoteMsection,
@ -282,20 +261,37 @@ private:
const std::string& mid,
uint16_t level);
SdpMediaSection* FindMsectionByMid(Sdp& sdp,
const std::string& mid) const;
const SdpMediaSection* FindMsectionByMid(const Sdp& sdp,
const std::string& mid) const;
const SdpGroupAttributeList::Group* FindBundleGroup(const Sdp& sdp) const;
void DisableMsection(Sdp& sdp, SdpMediaSection& msection) const;
nsresult GetNegotiatedBundleInfo(std::set<std::string>* bundleMids,
const SdpMediaSection** bundleMsection);
nsresult GetBundleInfo(const Sdp& sdp,
std::set<std::string>* bundleMids,
const SdpMediaSection** bundleMsection);
bool IsBundleSlave(const Sdp& localSdp, uint16_t level);
void DisableMsection(Sdp* sdp, SdpMediaSection* msection) const;
nsresult EnableMsection(SdpMediaSection* msection);
nsresult SetUniquePayloadTypes();
nsresult GetAllPayloadTypes(const JsepTrackNegotiatedDetails& trackDetails,
std::vector<uint8_t>* payloadTypesOut);
std::string GetCNAME(const SdpMediaSection& msection) const;
bool MsectionIsDisabled(const SdpMediaSection& msection) const;
std::vector<JsepSendingTrack> mLocalTracks;
std::vector<JsepReceivingTrack> mRemoteTracks;
// By the most recent SetRemoteDescription
std::vector<JsepReceivingTrack> mRemoteTracksAdded;
std::vector<JsepReceivingTrack> mRemoteTracksRemoved;
std::vector<RefPtr<JsepTransport> > mTransports;
std::vector<JsepTrackPair> mNegotiatedTrackPairs;
@ -312,6 +308,7 @@ private:
std::vector<SdpExtmapAttributeList::Extmap> mVideoRtpExtensions;
UniquePtr<JsepUuidGenerator> mUuidGen;
std::string mDefaultRemoteStreamId;
std::map<size_t, std::string> mDefaultRemoteTrackIdsByLevel;
std::string mCNAME;
UniquePtr<Sdp> mGeneratedLocalDescription; // Created but not set.
UniquePtr<Sdp> mCurrentLocalDescription;

View File

@ -41,13 +41,13 @@ const unsigned int WebrtcAudioConduit::CODEC_PLNAME_SIZE = 32;
/**
* Factory Method for AudioConduit
*/
mozilla::RefPtr<AudioSessionConduit> AudioSessionConduit::Create(AudioSessionConduit *aOther)
mozilla::RefPtr<AudioSessionConduit> AudioSessionConduit::Create()
{
CSFLogDebug(logTag, "%s ", __FUNCTION__);
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
WebrtcAudioConduit* obj = new WebrtcAudioConduit();
if(obj->Init(static_cast<WebrtcAudioConduit*>(aOther)) != kMediaConduitNoError)
if(obj->Init() != kMediaConduitNoError)
{
CSFLogError(logTag, "%s AudioConduit Init Failed ", __FUNCTION__);
delete obj;
@ -74,55 +74,39 @@ WebrtcAudioConduit::~WebrtcAudioConduit()
// The first one of a pair to be deleted shuts down media for both
if(mPtrVoEXmedia)
{
if (!mShutDown) {
mPtrVoEXmedia->SetExternalRecordingStatus(false);
mPtrVoEXmedia->SetExternalPlayoutStatus(false);
}
mPtrVoEXmedia->SetExternalRecordingStatus(false);
mPtrVoEXmedia->SetExternalPlayoutStatus(false);
}
//Deal with the transport
if(mPtrVoENetwork)
{
if (!mShutDown) {
mPtrVoENetwork->DeRegisterExternalTransport(mChannel);
}
mPtrVoENetwork->DeRegisterExternalTransport(mChannel);
}
if(mPtrVoEBase)
{
if (!mShutDown) {
mPtrVoEBase->StopPlayout(mChannel);
mPtrVoEBase->StopSend(mChannel);
mPtrVoEBase->StopReceive(mChannel);
mPtrVoEBase->DeleteChannel(mChannel);
mPtrVoEBase->Terminate();
}
mPtrVoEBase->StopPlayout(mChannel);
mPtrVoEBase->StopSend(mChannel);
mPtrVoEBase->StopReceive(mChannel);
mPtrVoEBase->DeleteChannel(mChannel);
mPtrVoEBase->Terminate();
}
if (mOtherDirection)
{
// mOtherDirection owns these now!
mOtherDirection->mOtherDirection = nullptr;
// let other side we terminated the channel
mOtherDirection->mShutDown = true;
mVoiceEngine = nullptr;
} else {
// We shouldn't delete the VoiceEngine until all these are released!
// And we can't use a Scoped ptr, since the order is arbitrary
mPtrVoENetwork = nullptr;
mPtrVoEBase = nullptr;
mPtrVoECodec = nullptr;
mPtrVoEXmedia = nullptr;
mPtrVoEProcessing = nullptr;
mPtrVoEVideoSync = nullptr;
mPtrVoERTP_RTCP = nullptr;
mPtrRTP = nullptr;
// We shouldn't delete the VoiceEngine until all these are released!
// And we can't use a Scoped ptr, since the order is arbitrary
mPtrVoENetwork = nullptr;
mPtrVoEBase = nullptr;
mPtrVoECodec = nullptr;
mPtrVoEXmedia = nullptr;
mPtrVoEProcessing = nullptr;
mPtrVoEVideoSync = nullptr;
mPtrVoERTP_RTCP = nullptr;
mPtrRTP = nullptr;
// only one opener can call Delete. Have it be the last to close.
if(mVoiceEngine)
{
webrtc::VoiceEngine::Delete(mVoiceEngine);
}
if(mVoiceEngine)
{
webrtc::VoiceEngine::Delete(mVoiceEngine);
}
}
@ -210,42 +194,32 @@ bool WebrtcAudioConduit::GetRTCPSenderReport(DOMHighResTimeStamp* timestamp,
/*
* WebRTCAudioConduit Implementation
*/
MediaConduitErrorCode WebrtcAudioConduit::Init(WebrtcAudioConduit *other)
MediaConduitErrorCode WebrtcAudioConduit::Init()
{
CSFLogDebug(logTag, "%s this=%p other=%p", __FUNCTION__, this, other);
CSFLogDebug(logTag, "%s this=%p", __FUNCTION__, this);
if (other) {
MOZ_ASSERT(!other->mOtherDirection);
other->mOtherDirection = this;
mOtherDirection = other;
// only one can call ::Create()/GetVoiceEngine()
MOZ_ASSERT(other->mVoiceEngine);
mVoiceEngine = other->mVoiceEngine;
} else {
#ifdef MOZ_WIDGET_ANDROID
jobject context = jsjni_GetGlobalContextRef();
jobject context = jsjni_GetGlobalContextRef();
// get the JVM
JavaVM *jvm = jsjni_GetVM();
JNIEnv* jenv = jsjni_GetJNIForThread();
// get the JVM
JavaVM *jvm = jsjni_GetVM();
JNIEnv* jenv = jsjni_GetJNIForThread();
if (webrtc::VoiceEngine::SetAndroidObjects(jvm, jenv, (void*)context) != 0) {
CSFLogError(logTag, "%s Unable to set Android objects", __FUNCTION__);
return kMediaConduitSessionNotInited;
}
#endif
// Per WebRTC APIs below function calls return nullptr on failure
if(!(mVoiceEngine = webrtc::VoiceEngine::Create()))
{
CSFLogError(logTag, "%s Unable to create voice engine", __FUNCTION__);
if (webrtc::VoiceEngine::SetAndroidObjects(jvm, jenv, (void*)context) != 0) {
CSFLogError(logTag, "%s Unable to set Android objects", __FUNCTION__);
return kMediaConduitSessionNotInited;
}
#endif
EnableWebRtcLog();
// Per WebRTC APIs below function calls return nullptr on failure
if(!(mVoiceEngine = webrtc::VoiceEngine::Create()))
{
CSFLogError(logTag, "%s Unable to create voice engine", __FUNCTION__);
return kMediaConduitSessionNotInited;
}
EnableWebRtcLog();
if(!(mPtrVoEBase = VoEBase::GetInterface(mVoiceEngine)))
{
CSFLogError(logTag, "%s Unable to initialize VoEBase", __FUNCTION__);
@ -292,61 +266,65 @@ MediaConduitErrorCode WebrtcAudioConduit::Init(WebrtcAudioConduit *other)
return kMediaConduitSessionNotInited;
}
if (other) {
mChannel = other->mChannel;
} else {
// init the engine with our audio device layer
if(mPtrVoEBase->Init() == -1)
{
CSFLogError(logTag, "%s VoiceEngine Base Not Initialized", __FUNCTION__);
return kMediaConduitSessionNotInited;
}
if( (mChannel = mPtrVoEBase->CreateChannel()) == -1)
{
CSFLogError(logTag, "%s VoiceEngine Channel creation failed",__FUNCTION__);
return kMediaConduitChannelError;
}
CSFLogDebug(logTag, "%s Channel Created %d ",__FUNCTION__, mChannel);
if(mPtrVoENetwork->RegisterExternalTransport(mChannel, *this) == -1)
{
CSFLogError(logTag, "%s VoiceEngine, External Transport Failed",__FUNCTION__);
return kMediaConduitTransportRegistrationFail;
}
if(mPtrVoEXmedia->SetExternalRecordingStatus(true) == -1)
{
CSFLogError(logTag, "%s SetExternalRecordingStatus Failed %d",__FUNCTION__,
mPtrVoEBase->LastError());
return kMediaConduitExternalPlayoutError;
}
if(mPtrVoEXmedia->SetExternalPlayoutStatus(true) == -1)
{
CSFLogError(logTag, "%s SetExternalPlayoutStatus Failed %d ",__FUNCTION__,
mPtrVoEBase->LastError());
return kMediaConduitExternalRecordingError;
}
CSFLogDebug(logTag , "%s AudioSessionConduit Initialization Done (%p)",__FUNCTION__, this);
// init the engine with our audio device layer
if(mPtrVoEBase->Init() == -1)
{
CSFLogError(logTag, "%s VoiceEngine Base Not Initialized", __FUNCTION__);
return kMediaConduitSessionNotInited;
}
if( (mChannel = mPtrVoEBase->CreateChannel()) == -1)
{
CSFLogError(logTag, "%s VoiceEngine Channel creation failed",__FUNCTION__);
return kMediaConduitChannelError;
}
CSFLogDebug(logTag, "%s Channel Created %d ",__FUNCTION__, mChannel);
if(mPtrVoENetwork->RegisterExternalTransport(mChannel, *this) == -1)
{
CSFLogError(logTag, "%s VoiceEngine, External Transport Failed",__FUNCTION__);
return kMediaConduitTransportRegistrationFail;
}
if(mPtrVoEXmedia->SetExternalRecordingStatus(true) == -1)
{
CSFLogError(logTag, "%s SetExternalRecordingStatus Failed %d",__FUNCTION__,
mPtrVoEBase->LastError());
return kMediaConduitExternalPlayoutError;
}
if(mPtrVoEXmedia->SetExternalPlayoutStatus(true) == -1)
{
CSFLogError(logTag, "%s SetExternalPlayoutStatus Failed %d ",__FUNCTION__,
mPtrVoEBase->LastError());
return kMediaConduitExternalRecordingError;
}
CSFLogDebug(logTag , "%s AudioSessionConduit Initialization Done (%p)",__FUNCTION__, this);
return kMediaConduitNoError;
}
// AudioSessionConduit Implementation
MediaConduitErrorCode
WebrtcAudioConduit::AttachTransport(mozilla::RefPtr<TransportInterface> aTransport)
WebrtcAudioConduit::SetTransmitterTransport(mozilla::RefPtr<TransportInterface> aTransport)
{
CSFLogDebug(logTag, "%s ", __FUNCTION__);
if(!aTransport)
{
CSFLogError(logTag, "%s NULL Transport", __FUNCTION__);
return kMediaConduitInvalidTransport;
}
ReentrantMonitorAutoEnter enter(mTransportMonitor);
// set the transport
mTransport = aTransport;
mTransmitterTransport = aTransport;
return kMediaConduitNoError;
}
MediaConduitErrorCode
WebrtcAudioConduit::SetReceiverTransport(mozilla::RefPtr<TransportInterface> aTransport)
{
CSFLogDebug(logTag, "%s ", __FUNCTION__);
ReentrantMonitorAutoEnter enter(mTransportMonitor);
// set the transport
mReceiverTransport = aTransport;
return kMediaConduitNoError;
}
@ -364,20 +342,11 @@ WebrtcAudioConduit::ConfigureSendMediaCodec(const AudioCodecConfig* codecConfig)
return condError;
}
//are we transmitting already, stop and apply the send codec
if(mEngineTransmitting)
{
CSFLogDebug(logTag, "%s Engine Already Sending. Attemping to Stop ", __FUNCTION__);
if(mPtrVoEBase->StopSend(mChannel) == -1)
{
CSFLogError(logTag, "%s StopSend() Failed %d ", __FUNCTION__,
mPtrVoEBase->LastError());
return kMediaConduitUnknownError;
}
condError = StopTransmitting();
if (condError != kMediaConduitNoError) {
return condError;
}
mEngineTransmitting = false;
if(!CodecConfigToWebRTCCodec(codecConfig,cinst))
{
CSFLogError(logTag,"%s CodecConfig to WebRTC Codec Failed ",__FUNCTION__);
@ -413,12 +382,9 @@ WebrtcAudioConduit::ConfigureSendMediaCodec(const AudioCodecConfig* codecConfig)
}
#endif
//Let's Send Transport State-machine on the Engine
if(mPtrVoEBase->StartSend(mChannel) == -1)
{
error = mPtrVoEBase->LastError();
CSFLogError(logTag, "%s StartSend failed %d", __FUNCTION__, error);
return kMediaConduitUnknownError;
condError = StartTransmitting();
if (condError != kMediaConduitNoError) {
return condError;
}
//Copy the applied config for future reference.
@ -431,7 +397,6 @@ WebrtcAudioConduit::ConfigureSendMediaCodec(const AudioCodecConfig* codecConfig)
codecConfig->mChannels,
codecConfig->mRate);
mEngineTransmitting = true;
return kMediaConduitNoError;
}
@ -446,25 +411,11 @@ WebrtcAudioConduit::ConfigureRecvMediaCodecs(
// Are we receiving already? If so, stop receiving and playout
// since we can't apply new recv codec when the engine is playing.
if(mEngineReceiving)
{
CSFLogDebug(logTag, "%s Engine Already Receiving. Attemping to Stop ", __FUNCTION__);
// AudioEngine doesn't fail fatally on stopping reception. Ref:voe_errors.h.
// hence we need not be strict in failing here on errors
mPtrVoEBase->StopReceive(mChannel);
CSFLogDebug(logTag, "%s Attemping to Stop playout ", __FUNCTION__);
if(mPtrVoEBase->StopPlayout(mChannel) == -1)
{
if( mPtrVoEBase->LastError() == VE_CANNOT_STOP_PLAYOUT)
{
CSFLogDebug(logTag, "%s Stop-Playout Failed %d", __FUNCTION__, mPtrVoEBase->LastError());
return kMediaConduitPlayoutError;
}
}
condError = StopReceiving();
if (condError != kMediaConduitNoError) {
return condError;
}
mEngineReceiving = false;
if(codecConfigList.empty())
{
CSFLogError(logTag, "%s Zero number of codecs to configure", __FUNCTION__);
@ -517,25 +468,11 @@ WebrtcAudioConduit::ConfigureRecvMediaCodecs(
}
//If we are here, atleast one codec should have been set
if(mPtrVoEBase->StartReceive(mChannel) == -1)
{
error = mPtrVoEBase->LastError();
CSFLogError(logTag , "%s StartReceive Failed %d ",__FUNCTION__, error);
if(error == VE_RECV_SOCKET_ERROR)
{
return kMediaConduitSocketError;
}
return kMediaConduitUnknownError;
condError = StartReceiving();
if (condError != kMediaConduitNoError) {
return condError;
}
if(mPtrVoEBase->StartPlayout(mChannel) == -1)
{
CSFLogError(logTag, "%s Starting playout Failed", __FUNCTION__);
return kMediaConduitPlayoutError;
}
//we should be good here for setting this.
mEngineReceiving = true;
DumpCodecDB();
return kMediaConduitNoError;
}
@ -788,64 +725,145 @@ WebrtcAudioConduit::ReceivedRTCPPacket(const void *data, int len)
return kMediaConduitNoError;
}
//WebRTC::RTP Callback Implementation
int WebrtcAudioConduit::SendPacket(int channel, const void* data, int len)
MediaConduitErrorCode
WebrtcAudioConduit::StopTransmitting()
{
CSFLogDebug(logTag, "%s : channel %d %s", __FUNCTION__, channel,
(mEngineReceiving && mOtherDirection) ? "(using mOtherDirection)" : "");
if (mEngineReceiving)
if(mEngineTransmitting)
{
if (mOtherDirection)
CSFLogDebug(logTag, "%s Engine Already Sending. Attemping to Stop ", __FUNCTION__);
if(mPtrVoEBase->StopSend(mChannel) == -1)
{
return mOtherDirection->SendPacket(channel, data, len);
}
CSFLogDebug(logTag, "%s : Asked to send RTP without an RTP sender on channel %d",
__FUNCTION__, channel);
return -1;
} else {
#ifdef MOZILLA_INTERNAL_API
if (PR_LOG_TEST(GetLatencyLog(), PR_LOG_DEBUG)) {
if (mProcessing.Length() > 0) {
TimeStamp started = mProcessing[0].mTimeStamp;
mProcessing.RemoveElementAt(0);
mProcessing.RemoveElementAt(0); // 20ms packetization! Could automate this by watching sizes
TimeDuration t = TimeStamp::Now() - started;
int64_t delta = t.ToMilliseconds();
LogTime(AsyncLatencyLogger::AudioSendRTP, ((uint64_t) this), delta);
}
}
#endif
if(mTransport && (mTransport->SendRtpPacket(data, len) == NS_OK))
{
CSFLogDebug(logTag, "%s Sent RTP Packet ", __FUNCTION__);
return len;
} else {
CSFLogError(logTag, "%s RTP Packet Send Failed ", __FUNCTION__);
return -1;
CSFLogError(logTag, "%s StopSend() Failed %d ", __FUNCTION__,
mPtrVoEBase->LastError());
return kMediaConduitUnknownError;
}
mEngineTransmitting = false;
}
return kMediaConduitNoError;
}
int WebrtcAudioConduit::SendRTCPPacket(int channel, const void* data, int len)
MediaConduitErrorCode
WebrtcAudioConduit::StartTransmitting()
{
if (!mEngineTransmitting) {
//Let's Send Transport State-machine on the Engine
if(mPtrVoEBase->StartSend(mChannel) == -1)
{
int error = mPtrVoEBase->LastError();
CSFLogError(logTag, "%s StartSend failed %d", __FUNCTION__, error);
return kMediaConduitUnknownError;
}
mEngineTransmitting = true;
}
return kMediaConduitNoError;
}
MediaConduitErrorCode
WebrtcAudioConduit::StopReceiving()
{
if(mEngineReceiving)
{
CSFLogDebug(logTag, "%s Engine Already Receiving. Attemping to Stop ", __FUNCTION__);
// AudioEngine doesn't fail fatally on stopping reception. Ref:voe_errors.h.
// hence we need not be strict in failing here on errors
mPtrVoEBase->StopReceive(mChannel);
CSFLogDebug(logTag, "%s Attemping to Stop playout ", __FUNCTION__);
if(mPtrVoEBase->StopPlayout(mChannel) == -1)
{
if( mPtrVoEBase->LastError() == VE_CANNOT_STOP_PLAYOUT)
{
CSFLogDebug(logTag, "%s Stop-Playout Failed %d", __FUNCTION__, mPtrVoEBase->LastError());
return kMediaConduitPlayoutError;
}
}
mEngineReceiving = false;
}
return kMediaConduitNoError;
}
MediaConduitErrorCode
WebrtcAudioConduit::StartReceiving()
{
if (!mEngineReceiving) {
if(mPtrVoEBase->StartReceive(mChannel) == -1)
{
int error = mPtrVoEBase->LastError();
CSFLogError(logTag , "%s StartReceive Failed %d ",__FUNCTION__, error);
if(error == VE_RECV_SOCKET_ERROR)
{
return kMediaConduitSocketError;
}
return kMediaConduitUnknownError;
}
if(mPtrVoEBase->StartPlayout(mChannel) == -1)
{
CSFLogError(logTag, "%s Starting playout Failed", __FUNCTION__);
return kMediaConduitPlayoutError;
}
mEngineReceiving = true;
}
return kMediaConduitNoError;
}
//WebRTC::RTP Callback Implementation
// Called on AudioGUM or MSG thread
int WebrtcAudioConduit::SendPacket(int channel, const void* data, int len)
{
CSFLogDebug(logTag, "%s : channel %d", __FUNCTION__, channel);
if (mEngineTransmitting)
{
if (mOtherDirection)
{
return mOtherDirection->SendRTCPPacket(channel, data, len);
#ifdef MOZILLA_INTERNAL_API
if (PR_LOG_TEST(GetLatencyLog(), PR_LOG_DEBUG)) {
if (mProcessing.Length() > 0) {
TimeStamp started = mProcessing[0].mTimeStamp;
mProcessing.RemoveElementAt(0);
mProcessing.RemoveElementAt(0); // 20ms packetization! Could automate this by watching sizes
TimeDuration t = TimeStamp::Now() - started;
int64_t delta = t.ToMilliseconds();
LogTime(AsyncLatencyLogger::AudioSendRTP, ((uint64_t) this), delta);
}
}
#endif
ReentrantMonitorAutoEnter enter(mTransportMonitor);
if(mTransmitterTransport &&
(mTransmitterTransport->SendRtpPacket(data, len) == NS_OK))
{
CSFLogDebug(logTag, "%s Sent RTP Packet ", __FUNCTION__);
return len;
} else {
CSFLogError(logTag, "%s RTP Packet Send Failed ", __FUNCTION__);
return -1;
}
}
// Called on WebRTC Process thread and perhaps others
int WebrtcAudioConduit::SendRTCPPacket(int channel, const void* data, int len)
{
CSFLogDebug(logTag, "%s : channel %d , len %d, first rtcp = %u ",
__FUNCTION__,
channel,
len,
static_cast<unsigned>(((uint8_t *) data)[1]));
// We come here if we have only one pipeline/conduit setup,
// such as for unidirectional streams.
// We also end up here if we are receiving
if(mTransport && mTransport->SendRtcpPacket(data, len) == NS_OK)
ReentrantMonitorAutoEnter enter(mTransportMonitor);
if(mReceiverTransport &&
mReceiverTransport->SendRtcpPacket(data, len) == NS_OK)
{
// Might be a sender report, might be a receiver report, we don't know.
CSFLogDebug(logTag, "%s Sent RTCP Packet ", __FUNCTION__);
return len;
} else if(mTransmitterTransport &&
(mTransmitterTransport->SendRtcpPacket(data, len) == NS_OK)) {
CSFLogDebug(logTag, "%s Sent RTCP Packet (sender report) ", __FUNCTION__);
return len;
} else {
CSFLogError(logTag, "%s RTCP Packet Send Failed ", __FUNCTION__);
return -1;
@ -1011,7 +1029,6 @@ WebrtcAudioConduit::ValidateCodecConfig(const AudioCodecConfig* codecInfo,
if(codecAppliedAlready)
{
CSFLogDebug(logTag, "%s Codec %s Already Applied ", __FUNCTION__, codecInfo->mName.c_str());
return kMediaConduitCodecInUse;
}
return kMediaConduitNoError;
}

View File

@ -64,6 +64,11 @@ public:
*/
virtual MediaConduitErrorCode ReceivedRTCPPacket(const void *data, int len) MOZ_OVERRIDE;
virtual MediaConduitErrorCode StopTransmitting() MOZ_OVERRIDE;
virtual MediaConduitErrorCode StartTransmitting() MOZ_OVERRIDE;
virtual MediaConduitErrorCode StopReceiving() MOZ_OVERRIDE;
virtual MediaConduitErrorCode StartReceiving() MOZ_OVERRIDE;
/**
* Function to configure send codec for the audio session
* @param sendSessionConfig: CodecConfiguration
@ -94,7 +99,10 @@ public:
* Register External Transport to this Conduit. RTP and RTCP frames from the VoiceEngine
* shall be passed to the registered transport for transporting externally.
*/
virtual MediaConduitErrorCode AttachTransport(mozilla::RefPtr<TransportInterface> aTransport) MOZ_OVERRIDE;
virtual MediaConduitErrorCode SetTransmitterTransport(mozilla::RefPtr<TransportInterface> aTransport) MOZ_OVERRIDE;
virtual MediaConduitErrorCode SetReceiverTransport(mozilla::RefPtr<TransportInterface> aTransport) MOZ_OVERRIDE;
/**
* Function to deliver externally captured audio sample for encoding and transport
* @param audioData [in]: Pointer to array containing a frame of audio
@ -154,10 +162,10 @@ public:
virtual uint64_t CodecPluginID() MOZ_OVERRIDE { return 0; }
WebrtcAudioConduit():
mOtherDirection(nullptr),
mShutDown(false),
mVoiceEngine(nullptr),
mTransport(nullptr),
mTransportMonitor("WebrtcAudioConduit"),
mTransmitterTransport(nullptr),
mReceiverTransport(nullptr),
mEngineTransmitting(false),
mEngineReceiving(false),
mChannel(-1),
@ -173,7 +181,7 @@ public:
virtual ~WebrtcAudioConduit();
MediaConduitErrorCode Init(WebrtcAudioConduit *other);
MediaConduitErrorCode Init();
int GetChannel() { return mChannel; }
webrtc::VoiceEngine* GetVoiceEngine() { return mVoiceEngine; }
@ -242,17 +250,10 @@ private:
//Utility function to dump recv codec database
void DumpCodecDB() const;
// The two sides of a send/receive pair of conduits each keep a pointer to the other.
// The also share a single VoiceEngine and mChannel. Shutdown must be coordinated
// carefully to avoid double-freeing or accessing after one frees.
WebrtcAudioConduit* mOtherDirection;
// Other side has shut down our channel and related items already
bool mShutDown;
// These are shared by both directions. They're released by the last
// conduit to die
webrtc::VoiceEngine* mVoiceEngine;
mozilla::RefPtr<TransportInterface> mTransport;
mozilla::ReentrantMonitor mTransportMonitor;
mozilla::RefPtr<TransportInterface> mTransmitterTransport;
mozilla::RefPtr<TransportInterface> mReceiverTransport;
ScopedCustomReleasePtr<webrtc::VoENetwork> mPtrVoENetwork;
ScopedCustomReleasePtr<webrtc::VoEBase> mPtrVoEBase;
ScopedCustomReleasePtr<webrtc::VoECodec> mPtrVoECodec;
@ -262,8 +263,8 @@ private:
ScopedCustomReleasePtr<webrtc::VoERTP_RTCP> mPtrVoERTP_RTCP;
ScopedCustomReleasePtr<webrtc::VoERTP_RTCP> mPtrRTP;
//engine states of our interets
bool mEngineTransmitting; // If true => VoiceEngine Send-subsystem is up
bool mEngineReceiving; // If true => VoiceEngine Receive-subsystem is up
mozilla::Atomic<bool> mEngineTransmitting; // If true => VoiceEngine Send-subsystem is up
mozilla::Atomic<bool> mEngineReceiving; // If true => VoiceEngine Receive-subsystem is up
// and playout is enabled
// Keep track of each inserted RTP block and the time it was inserted
// so we can estimate the clock time for a specific TimeStamp coming out

View File

@ -152,14 +152,34 @@ public:
*/
virtual MediaConduitErrorCode ReceivedRTCPPacket(const void *data, int len) = 0;
virtual MediaConduitErrorCode StopTransmitting() = 0;
virtual MediaConduitErrorCode StartTransmitting() = 0;
virtual MediaConduitErrorCode StopReceiving() = 0;
virtual MediaConduitErrorCode StartReceiving() = 0;
/**
* Function to attach Transport end-point of the Media conduit.
* Function to attach transmitter transport end-point of the Media conduit.
* @param aTransport: Reference to the concrete teansport implementation
* When nullptr, unsets the transmitter transport endpoint.
* Note: Multiple invocations of this call , replaces existing transport with
* with the new one.
* Note: This transport is used for RTP, and RTCP if no receiver transport is
* set. In the future, we should ensure that RTCP sender reports use this
* regardless of whether the receiver transport is set.
*/
virtual MediaConduitErrorCode AttachTransport(RefPtr<TransportInterface> aTransport) = 0;
virtual MediaConduitErrorCode SetTransmitterTransport(RefPtr<TransportInterface> aTransport) = 0;
/**
* Function to attach receiver transport end-point of the Media conduit.
* @param aTransport: Reference to the concrete teansport implementation
* When nullptr, unsets the receiver transport endpoint.
* Note: Multiple invocations of this call , replaces existing transport with
* with the new one.
* Note: This transport is used for RTCP.
* Note: In the future, we should avoid using this for RTCP sender reports.
*/
virtual MediaConduitErrorCode SetReceiverTransport(RefPtr<TransportInterface> aTransport) = 0;
virtual bool SetLocalSSRC(unsigned int ssrc) = 0;
virtual bool GetLocalSSRC(unsigned int* ssrc) = 0;
@ -234,8 +254,7 @@ public:
* return: Concrete VideoSessionConduitObject or nullptr in the case
* of failure
*/
static RefPtr<VideoSessionConduit> Create(VideoSessionConduit *aOther,
bool receiving);
static RefPtr<VideoSessionConduit> Create();
enum FrameRequestType
{
@ -360,7 +379,7 @@ public:
* return: Concrete AudioSessionConduitObject or nullptr in the case
* of failure
*/
static mozilla::RefPtr<AudioSessionConduit> Create(AudioSessionConduit *aOther);
static mozilla::RefPtr<AudioSessionConduit> Create();
virtual ~AudioSessionConduit() {}

View File

@ -43,14 +43,13 @@ const unsigned int WebrtcVideoConduit::CODEC_PLNAME_SIZE = 32;
* Factory Method for VideoConduit
*/
mozilla::RefPtr<VideoSessionConduit>
VideoSessionConduit::Create(VideoSessionConduit *aOther,
bool receiving)
VideoSessionConduit::Create()
{
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
CSFLogDebug(logTag, "%s ", __FUNCTION__);
WebrtcVideoConduit* obj = new WebrtcVideoConduit();
if(obj->Init(static_cast<WebrtcVideoConduit*>(aOther), receiving) != kMediaConduitNoError)
if(obj->Init() != kMediaConduitNoError)
{
CSFLogError(logTag, "%s VideoConduit Init Failed ", __FUNCTION__);
delete obj;
@ -61,10 +60,10 @@ VideoSessionConduit::Create(VideoSessionConduit *aOther,
}
WebrtcVideoConduit::WebrtcVideoConduit():
mOtherDirection(nullptr),
mShutDown(false),
mVideoEngine(nullptr),
mTransport(nullptr),
mTransportMonitor("WebrtcVideoConduit"),
mTransmitterTransport(nullptr),
mReceiverTransport(nullptr),
mRenderer(nullptr),
mPtrExtCapture(nullptr),
mEngineTransmitting(false),
@ -77,6 +76,7 @@ WebrtcVideoConduit::WebrtcVideoConduit():
mReceivingWidth(640),
mReceivingHeight(480),
mSendingFramerate(DEFAULT_VIDEO_MAX_FRAMERATE),
mNumReceivingStreams(1),
mVideoLatencyTestEnable(false),
mVideoLatencyAvg(0),
mMinBitrate(200),
@ -101,13 +101,9 @@ WebrtcVideoConduit::~WebrtcVideoConduit()
//Deal with External Capturer
if(mPtrViECapture)
{
if (!mShutDown) {
mPtrViECapture->DisconnectCaptureDevice(mCapId);
mPtrViECapture->ReleaseCaptureDevice(mCapId);
mPtrExtCapture = nullptr;
if (mOtherDirection)
mOtherDirection->mPtrExtCapture = nullptr;
}
mPtrViECapture->DisconnectCaptureDevice(mCapId);
mPtrViECapture->ReleaseCaptureDevice(mCapId);
mPtrExtCapture = nullptr;
}
if (mPtrExtCodec) {
@ -118,60 +114,45 @@ WebrtcVideoConduit::~WebrtcVideoConduit()
//Deal with External Renderer
if(mPtrViERender)
{
if (!mShutDown) {
if(mRenderer) {
mPtrViERender->StopRender(mChannel);
}
mPtrViERender->RemoveRenderer(mChannel);
if(mRenderer) {
mPtrViERender->StopRender(mChannel);
}
mPtrViERender->RemoveRenderer(mChannel);
}
//Deal with the transport
if(mPtrViENetwork)
{
if (!mShutDown) {
mPtrViENetwork->DeregisterSendTransport(mChannel);
}
mPtrViENetwork->DeregisterSendTransport(mChannel);
}
if(mPtrViEBase)
{
if (!mShutDown) {
mPtrViEBase->StopSend(mChannel);
mPtrViEBase->StopReceive(mChannel);
SyncTo(nullptr);
mPtrViEBase->DeleteChannel(mChannel);
}
mPtrViEBase->StopSend(mChannel);
mPtrViEBase->StopReceive(mChannel);
SyncTo(nullptr);
mPtrViEBase->DeleteChannel(mChannel);
}
if (mOtherDirection)
{
// mOtherDirection owns these now!
mOtherDirection->mOtherDirection = nullptr;
// let other side we terminated the channel
mOtherDirection->mShutDown = true;
mVideoEngine = nullptr;
} else {
// mVideoCodecStat has a back-ptr to mPtrViECodec that must be released first
if (mVideoCodecStat) {
mVideoCodecStat->EndOfCallStats();
}
mVideoCodecStat = nullptr;
// We can't delete the VideoEngine until all these are released!
// And we can't use a Scoped ptr, since the order is arbitrary
mPtrViEBase = nullptr;
mPtrViECapture = nullptr;
mPtrViECodec = nullptr;
mPtrViENetwork = nullptr;
mPtrViERender = nullptr;
mPtrRTP = nullptr;
mPtrExtCodec = nullptr;
// mVideoCodecStat has a back-ptr to mPtrViECodec that must be released first
if (mVideoCodecStat) {
mVideoCodecStat->EndOfCallStats();
}
mVideoCodecStat = nullptr;
// We can't delete the VideoEngine until all these are released!
// And we can't use a Scoped ptr, since the order is arbitrary
mPtrViEBase = nullptr;
mPtrViECapture = nullptr;
mPtrViECodec = nullptr;
mPtrViENetwork = nullptr;
mPtrViERender = nullptr;
mPtrRTP = nullptr;
mPtrExtCodec = nullptr;
// only one opener can call Delete. Have it be the last to close.
if(mVideoEngine)
{
webrtc::VideoEngine::Delete(mVideoEngine);
}
// only one opener can call Delete. Have it be the last to close.
if(mVideoEngine)
{
webrtc::VideoEngine::Delete(mVideoEngine);
}
}
@ -288,10 +269,9 @@ bool WebrtcVideoConduit::GetRTCPSenderReport(DOMHighResTimeStamp* timestamp,
* Performs initialization of the MANDATORY components of the Video Engine
*/
MediaConduitErrorCode
WebrtcVideoConduit::Init(WebrtcVideoConduit *other,
bool receiving)
WebrtcVideoConduit::Init()
{
CSFLogDebug(logTag, "%s this=%p other=%p", __FUNCTION__, this, other);
CSFLogDebug(logTag, "%s this=%p", __FUNCTION__, this);
#ifdef MOZILLA_INTERNAL_API
// already know we must be on MainThread barring unit test weirdness
@ -328,36 +308,26 @@ WebrtcVideoConduit::Init(WebrtcVideoConduit *other,
}
#endif
if (other) {
MOZ_ASSERT(!other->mOtherDirection);
other->mOtherDirection = this;
mOtherDirection = other;
// only one can call ::Create()/GetVideoEngine()
MOZ_ASSERT(other->mVideoEngine);
mVideoEngine = other->mVideoEngine;
} else {
#ifdef MOZ_WIDGET_ANDROID
// get the JVM
JavaVM *jvm = jsjni_GetVM();
// get the JVM
JavaVM *jvm = jsjni_GetVM();
if (webrtc::VideoEngine::SetAndroidObjects(jvm) != 0) {
CSFLogError(logTag, "%s: could not set Android objects", __FUNCTION__);
return kMediaConduitSessionNotInited;
}
if (webrtc::VideoEngine::SetAndroidObjects(jvm) != 0) {
CSFLogError(logTag, "%s: could not set Android objects", __FUNCTION__);
return kMediaConduitSessionNotInited;
}
#endif
// Per WebRTC APIs below function calls return nullptr on failure
mVideoEngine = webrtc::VideoEngine::Create();
if(!mVideoEngine)
{
CSFLogError(logTag, "%s Unable to create video engine ", __FUNCTION__);
return kMediaConduitSessionNotInited;
}
EnableWebRtcLog();
// Per WebRTC APIs below function calls return nullptr on failure
mVideoEngine = webrtc::VideoEngine::Create();
if(!mVideoEngine)
{
CSFLogError(logTag, "%s Unable to create video engine ", __FUNCTION__);
return kMediaConduitSessionNotInited;
}
EnableWebRtcLog();
if( !(mPtrViEBase = ViEBase::GetInterface(mVideoEngine)))
{
CSFLogError(logTag, "%s Unable to get video base interface ", __FUNCTION__);
@ -408,71 +378,68 @@ WebrtcVideoConduit::Init(WebrtcVideoConduit *other,
return kMediaConduitSessionNotInited;
}
if (other) {
mChannel = other->mChannel;
mPtrExtCapture = other->mPtrExtCapture;
mCapId = other->mCapId;
} else {
CSFLogDebug(logTag, "%s Engine Created: Init'ng the interfaces ",__FUNCTION__);
CSFLogDebug(logTag, "%s Engine Created: Init'ng the interfaces ",__FUNCTION__);
if(mPtrViEBase->Init() == -1)
{
CSFLogError(logTag, " %s Video Engine Init Failed %d ",__FUNCTION__,
mPtrViEBase->LastError());
return kMediaConduitSessionNotInited;
}
if(mPtrViEBase->CreateChannel(mChannel) == -1)
{
CSFLogError(logTag, " %s Channel creation Failed %d ",__FUNCTION__,
mPtrViEBase->LastError());
return kMediaConduitChannelError;
}
if(mPtrViENetwork->RegisterSendTransport(mChannel, *this) == -1)
{
CSFLogError(logTag, "%s ViENetwork Failed %d ", __FUNCTION__,
mPtrViEBase->LastError());
return kMediaConduitTransportRegistrationFail;
}
if(mPtrViECapture->AllocateExternalCaptureDevice(mCapId,
mPtrExtCapture) == -1)
{
CSFLogError(logTag, "%s Unable to Allocate capture module: %d ",
__FUNCTION__, mPtrViEBase->LastError());
return kMediaConduitCaptureError;
}
if(mPtrViECapture->ConnectCaptureDevice(mCapId,mChannel) == -1)
{
CSFLogError(logTag, "%s Unable to Connect capture module: %d ",
__FUNCTION__,mPtrViEBase->LastError());
return kMediaConduitCaptureError;
}
// Set up some parameters, per juberti. Set MTU.
if(mPtrViENetwork->SetMTU(mChannel, 1200) != 0)
{
CSFLogError(logTag, "%s MTU Failed %d ", __FUNCTION__,
mPtrViEBase->LastError());
return kMediaConduitMTUError;
}
// Turn on RTCP and loss feedback reporting.
if(mPtrRTP->SetRTCPStatus(mChannel, webrtc::kRtcpCompound_RFC4585) != 0)
{
CSFLogError(logTag, "%s RTCPStatus Failed %d ", __FUNCTION__,
mPtrViEBase->LastError());
return kMediaConduitRTCPStatusError;
}
if(mPtrViEBase->Init() == -1)
{
CSFLogError(logTag, " %s Video Engine Init Failed %d ",__FUNCTION__,
mPtrViEBase->LastError());
return kMediaConduitSessionNotInited;
}
if (receiving) {
if (mPtrViERender->AddRenderer(mChannel,
webrtc::kVideoI420,
(webrtc::ExternalRenderer*) this) == -1) {
CSFLogError(logTag, "%s Failed to added external renderer ", __FUNCTION__);
return kMediaConduitInvalidRenderer;
}
if(mPtrViEBase->CreateChannel(mChannel) == -1)
{
CSFLogError(logTag, " %s Channel creation Failed %d ",__FUNCTION__,
mPtrViEBase->LastError());
return kMediaConduitChannelError;
}
if(mPtrViENetwork->RegisterSendTransport(mChannel, *this) == -1)
{
CSFLogError(logTag, "%s ViENetwork Failed %d ", __FUNCTION__,
mPtrViEBase->LastError());
return kMediaConduitTransportRegistrationFail;
}
if(mPtrViECapture->AllocateExternalCaptureDevice(mCapId,
mPtrExtCapture) == -1)
{
CSFLogError(logTag, "%s Unable to Allocate capture module: %d ",
__FUNCTION__, mPtrViEBase->LastError());
return kMediaConduitCaptureError;
}
if(mPtrViECapture->ConnectCaptureDevice(mCapId,mChannel) == -1)
{
CSFLogError(logTag, "%s Unable to Connect capture module: %d ",
__FUNCTION__,mPtrViEBase->LastError());
return kMediaConduitCaptureError;
}
// Set up some parameters, per juberti. Set MTU.
if(mPtrViENetwork->SetMTU(mChannel, 1200) != 0)
{
CSFLogError(logTag, "%s MTU Failed %d ", __FUNCTION__,
mPtrViEBase->LastError());
return kMediaConduitMTUError;
}
// Turn on RTCP and loss feedback reporting.
if(mPtrRTP->SetRTCPStatus(mChannel, webrtc::kRtcpCompound_RFC4585) != 0)
{
CSFLogError(logTag, "%s RTCPStatus Failed %d ", __FUNCTION__,
mPtrViEBase->LastError());
return kMediaConduitRTCPStatusError;
}
if (mPtrViERender->AddRenderer(mChannel,
webrtc::kVideoI420,
(webrtc::ExternalRenderer*) this) == -1) {
CSFLogError(logTag, "%s Failed to added external renderer ", __FUNCTION__);
return kMediaConduitInvalidRenderer;
}
if (mLoadManager) {
mPtrViEBase->RegisterCpuOveruseObserver(mChannel, mLoadManager);
mPtrViEBase->SetLoadManager(mLoadManager);
}
CSFLogError(logTag, "%s Initialization Done", __FUNCTION__);
@ -491,17 +458,9 @@ WebrtcVideoConduit::SyncTo(WebrtcAudioConduit *aConduit)
mPtrViEBase->SetVoiceEngine(aConduit->GetVoiceEngine());
mPtrViEBase->ConnectAudioChannel(mChannel, aConduit->GetChannel());
// NOTE: this means the VideoConduit will keep the AudioConduit alive!
} else if ((mOtherDirection && mOtherDirection->mSyncedTo) || mSyncedTo) {
mPtrViEBase->DisconnectAudioChannel(mChannel);
mPtrViEBase->SetVoiceEngine(nullptr);
}
// Now manage the shared sync reference (ugly)
if (mSyncedTo || !mOtherDirection ) {
mSyncedTo = aConduit;
} else {
mOtherDirection->mSyncedTo = aConduit;
}
mSyncedTo = aConduit;
}
MediaConduitErrorCode
@ -517,21 +476,28 @@ WebrtcVideoConduit::AttachRenderer(mozilla::RefPtr<VideoRenderer> aVideoRenderer
return kMediaConduitInvalidRenderer;
}
//Start Rendering if we haven't already
if(!mRenderer)
// This function is called only from main, so we only need to protect against
// modifying mRenderer while any webrtc.org code is trying to use it.
bool wasRendering;
{
mRenderer = aVideoRenderer; // must be done before StartRender()
ReentrantMonitorAutoEnter enter(mTransportMonitor);
wasRendering = !!mRenderer;
mRenderer = aVideoRenderer;
// Make sure the renderer knows the resolution
mRenderer->FrameSizeChange(mReceivingWidth,
mReceivingHeight,
mNumReceivingStreams);
}
if (!wasRendering) {
if(mPtrViERender->StartRender(mChannel) == -1)
{
CSFLogError(logTag, "%s Starting the Renderer Failed %d ", __FUNCTION__,
mPtrViEBase->LastError());
ReentrantMonitorAutoEnter enter(mTransportMonitor);
mRenderer = nullptr;
return kMediaConduitRendererFail;
}
} else {
//Assign the new renderer - overwrites if there is already one
mRenderer = aVideoRenderer;
}
return kMediaConduitNoError;
@ -540,24 +506,36 @@ WebrtcVideoConduit::AttachRenderer(mozilla::RefPtr<VideoRenderer> aVideoRenderer
void
WebrtcVideoConduit::DetachRenderer()
{
if(mRenderer)
{
mPtrViERender->StopRender(mChannel);
mRenderer = nullptr;
ReentrantMonitorAutoEnter enter(mTransportMonitor);
if(mRenderer)
{
mRenderer = nullptr;
}
}
mPtrViERender->StopRender(mChannel);
}
MediaConduitErrorCode
WebrtcVideoConduit::AttachTransport(mozilla::RefPtr<TransportInterface> aTransport)
WebrtcVideoConduit::SetTransmitterTransport(mozilla::RefPtr<TransportInterface> aTransport)
{
CSFLogDebug(logTag, "%s ", __FUNCTION__);
if(!aTransport)
{
CSFLogError(logTag, "%s NULL Transport", __FUNCTION__);
return kMediaConduitInvalidTransport;
}
ReentrantMonitorAutoEnter enter(mTransportMonitor);
// set the transport
mTransport = aTransport;
mTransmitterTransport = aTransport;
return kMediaConduitNoError;
}
MediaConduitErrorCode
WebrtcVideoConduit::SetReceiverTransport(mozilla::RefPtr<TransportInterface> aTransport)
{
CSFLogDebug(logTag, "%s ", __FUNCTION__);
ReentrantMonitorAutoEnter enter(mTransportMonitor);
// set the transport
mReceiverTransport = aTransport;
return kMediaConduitNoError;
}
@ -587,26 +565,11 @@ WebrtcVideoConduit::ConfigureSendMediaCodec(const VideoCodecConfig* codecConfig)
if(CheckCodecsForMatch(mCurSendCodecConfig, codecConfig))
{
CSFLogDebug(logTag, "%s Codec has been applied already ", __FUNCTION__);
return kMediaConduitCodecInUse;
}
//transmitting already ?
if(mEngineTransmitting)
{
CSFLogDebug(logTag, "%s Engine Already Sending. Attemping to Stop ", __FUNCTION__);
if(mPtrViEBase->StopSend(mChannel) == -1)
{
CSFLogError(logTag, "%s StopSend() Failed %d ",__FUNCTION__,
mPtrViEBase->LastError());
return kMediaConduitUnknownError;
}
mEngineTransmitting = false;
}
if (mLoadManager) {
mPtrViEBase->RegisterCpuOveruseObserver(mChannel, mLoadManager);
mPtrViEBase->SetLoadManager(mLoadManager);
condError = StopTransmitting();
if (condError != kMediaConduitNoError) {
return condError;
}
if (mExternalSendCodec &&
@ -687,11 +650,9 @@ WebrtcVideoConduit::ConfigureSendMediaCodec(const VideoCodecConfig* codecConfig)
}
}
if(mPtrViEBase->StartSend(mChannel) == -1)
{
CSFLogError(logTag, "%s Start Send Error %d ", __FUNCTION__,
mPtrViEBase->LastError());
return kMediaConduitUnknownError;
condError = StartTransmitting();
if (condError != kMediaConduitNoError) {
return condError;
}
//Copy the applied config for future reference.
@ -701,8 +662,6 @@ WebrtcVideoConduit::ConfigureSendMediaCodec(const VideoCodecConfig* codecConfig)
mPtrRTP->SetRembStatus(mChannel, true, false);
// by now we should be successfully started the transmission
mEngineTransmitting = true;
return kMediaConduitNoError;
}
@ -712,32 +671,14 @@ WebrtcVideoConduit::ConfigureRecvMediaCodecs(
{
CSFLogDebug(logTag, "%s ", __FUNCTION__);
MediaConduitErrorCode condError = kMediaConduitNoError;
int error = 0; //webrtc engine errors
bool success = false;
std::string payloadName;
// are we receiving already? If so, stop receiving and playout
// since we can't apply new recv codec when the engine is playing.
if(mEngineReceiving)
{
CSFLogDebug(logTag, "%s Engine Already Receiving . Attemping to Stop ", __FUNCTION__);
if(mPtrViEBase->StopReceive(mChannel) == -1)
{
error = mPtrViEBase->LastError();
if(error == kViEBaseUnknownError)
{
CSFLogDebug(logTag, "%s StopReceive() Success ", __FUNCTION__);
mEngineReceiving = false;
} else {
CSFLogError(logTag, "%s StopReceive() Failed %d ", __FUNCTION__,
mPtrViEBase->LastError());
return kMediaConduitUnknownError;
}
}
condError = StopReceiving();
if (condError != kMediaConduitNoError) {
return condError;
}
mEngineReceiving = false;
if(codecConfigList.empty())
{
CSFLogError(logTag, "%s Zero number of codecs to configure", __FUNCTION__);
@ -777,7 +718,6 @@ WebrtcVideoConduit::ConfigureRecvMediaCodecs(
webrtc::VideoCodec video_codec;
mEngineReceiving = false;
memset(&video_codec, 0, sizeof(webrtc::VideoCodec));
if (mExternalRecvCodec &&
@ -907,19 +847,13 @@ WebrtcVideoConduit::ConfigureRecvMediaCodecs(
}
mUsingNackBasic = use_nack_basic;
//Start Receive on the video engine
if(mPtrViEBase->StartReceive(mChannel) == -1)
{
error = mPtrViEBase->LastError();
CSFLogError(logTag, "%s Start Receive Error %d ", __FUNCTION__, error);
return kMediaConduitUnknownError;
condError = StartReceiving();
if (condError != kMediaConduitNoError) {
return condError;
}
// by now we should be successfully started the reception
mPtrRTP->SetRembStatus(mChannel, false, true);
mEngineReceiving = true;
DumpCodecDB();
return kMediaConduitNoError;
}
@ -1217,52 +1151,125 @@ WebrtcVideoConduit::ReceivedRTCPPacket(const void *data, int len)
return kMediaConduitNoError;
}
MediaConduitErrorCode
WebrtcVideoConduit::StopTransmitting()
{
if(mEngineTransmitting)
{
CSFLogDebug(logTag, "%s Engine Already Sending. Attemping to Stop ", __FUNCTION__);
if(mPtrViEBase->StopSend(mChannel) == -1)
{
CSFLogError(logTag, "%s StopSend() Failed %d ",__FUNCTION__,
mPtrViEBase->LastError());
return kMediaConduitUnknownError;
}
mEngineTransmitting = false;
}
return kMediaConduitNoError;
}
MediaConduitErrorCode
WebrtcVideoConduit::StartTransmitting()
{
if (!mEngineTransmitting) {
if(mPtrViEBase->StartSend(mChannel) == -1)
{
CSFLogError(logTag, "%s Start Send Error %d ", __FUNCTION__,
mPtrViEBase->LastError());
return kMediaConduitUnknownError;
}
mEngineTransmitting = true;
}
return kMediaConduitNoError;
}
MediaConduitErrorCode
WebrtcVideoConduit::StopReceiving()
{
// Are we receiving already? If so, stop receiving and playout
// since we can't apply new recv codec when the engine is playing.
if(mEngineReceiving)
{
CSFLogDebug(logTag, "%s Engine Already Receiving . Attemping to Stop ", __FUNCTION__);
if(mPtrViEBase->StopReceive(mChannel) == -1)
{
int error = mPtrViEBase->LastError();
if(error == kViEBaseUnknownError)
{
CSFLogDebug(logTag, "%s StopReceive() Success ", __FUNCTION__);
} else {
CSFLogError(logTag, "%s StopReceive() Failed %d ", __FUNCTION__,
mPtrViEBase->LastError());
return kMediaConduitUnknownError;
}
}
mEngineReceiving = false;
}
return kMediaConduitNoError;
}
MediaConduitErrorCode
WebrtcVideoConduit::StartReceiving()
{
if (!mEngineReceiving) {
CSFLogDebug(logTag, "%s Attemping to start... ", __FUNCTION__);
//Start Receive on the video engine
if(mPtrViEBase->StartReceive(mChannel) == -1)
{
int error = mPtrViEBase->LastError();
CSFLogError(logTag, "%s Start Receive Error %d ", __FUNCTION__, error);
return kMediaConduitUnknownError;
}
mEngineReceiving = true;
}
return kMediaConduitNoError;
}
//WebRTC::RTP Callback Implementation
// Called on MSG thread
int WebrtcVideoConduit::SendPacket(int channel, const void* data, int len)
{
CSFLogDebug(logTag, "%s : channel %d len %d %s", __FUNCTION__, channel, len,
(mEngineReceiving && mOtherDirection) ? "(using mOtherDirection)" : "");
CSFLogDebug(logTag, "%s : channel %d len %d", __FUNCTION__, channel, len);
if (mEngineReceiving)
ReentrantMonitorAutoEnter enter(mTransportMonitor);
if(mTransmitterTransport &&
(mTransmitterTransport->SendRtpPacket(data, len) == NS_OK))
{
if (mOtherDirection)
{
return mOtherDirection->SendPacket(channel, data, len);
}
CSFLogDebug(logTag, "%s : Asked to send RTP without an RTP sender on channel %d",
__FUNCTION__, channel);
return -1;
CSFLogDebug(logTag, "%s Sent RTP Packet ", __FUNCTION__);
return len;
} else {
if(mTransport && (mTransport->SendRtpPacket(data, len) == NS_OK))
{
CSFLogDebug(logTag, "%s Sent RTP Packet ", __FUNCTION__);
return len;
} else {
CSFLogError(logTag, "%s RTP Packet Send Failed ", __FUNCTION__);
return -1;
}
CSFLogError(logTag, "%s RTP Packet Send Failed ", __FUNCTION__);
return -1;
}
}
// Called from multiple threads including webrtc Process thread
int WebrtcVideoConduit::SendRTCPPacket(int channel, const void* data, int len)
{
CSFLogDebug(logTag, "%s : channel %d , len %d ", __FUNCTION__, channel,len);
if (mEngineTransmitting)
{
if (mOtherDirection)
{
return mOtherDirection->SendRTCPPacket(channel, data, len);
}
}
// We come here if we have only one pipeline/conduit setup,
// such as for unidirectional streams.
// We also end up here if we are receiving
if(mTransport && mTransport->SendRtcpPacket(data, len) == NS_OK)
ReentrantMonitorAutoEnter enter(mTransportMonitor);
if(mReceiverTransport &&
mReceiverTransport->SendRtcpPacket(data, len) == NS_OK)
{
// Might be a sender report, might be a receiver report, we don't know.
CSFLogDebug(logTag, "%s Sent RTCP Packet ", __FUNCTION__);
return len;
} else if(mTransmitterTransport &&
(mTransmitterTransport->SendRtpPacket(data, len) == NS_OK)) {
CSFLogDebug(logTag, "%s Sent RTCP Packet (sender report) ", __FUNCTION__);
return len;
} else {
CSFLogError(logTag, "%s RTCP Packet Send Failed ", __FUNCTION__);
return -1;
@ -1278,8 +1285,10 @@ WebrtcVideoConduit::FrameSizeChange(unsigned int width,
CSFLogDebug(logTag, "%s ", __FUNCTION__);
ReentrantMonitorAutoEnter enter(mTransportMonitor);
mReceivingWidth = width;
mReceivingHeight = height;
mNumReceivingStreams = numStreams;
if(mRenderer)
{
@ -1301,6 +1310,7 @@ WebrtcVideoConduit::DeliverFrame(unsigned char* buffer,
{
CSFLogDebug(logTag, "%s Buffer Size %d", __FUNCTION__, buffer_size);
ReentrantMonitorAutoEnter enter(mTransportMonitor);
if(mRenderer)
{
layers::Image* img = nullptr;
@ -1479,7 +1489,6 @@ WebrtcVideoConduit::ValidateCodecConfig(const VideoCodecConfig* codecInfo,
if(codecAppliedAlready)
{
CSFLogDebug(logTag, "%s Codec %s Already Applied ", __FUNCTION__, codecInfo->mName.c_str());
return kMediaConduitCodecInUse;
}
return kMediaConduitNoError;
}

View File

@ -94,6 +94,11 @@ public:
*/
virtual MediaConduitErrorCode ReceivedRTCPPacket(const void *data, int len) MOZ_OVERRIDE;
virtual MediaConduitErrorCode StopTransmitting() MOZ_OVERRIDE;
virtual MediaConduitErrorCode StartTransmitting() MOZ_OVERRIDE;
virtual MediaConduitErrorCode StopReceiving() MOZ_OVERRIDE;
virtual MediaConduitErrorCode StartReceiving() MOZ_OVERRIDE;
/**
* Function to configure send codec for the video session
* @param sendSessionConfig: CodecConfiguration
@ -120,7 +125,9 @@ public:
* Register Transport for this Conduit. RTP and RTCP frames from the VideoEngine
* shall be passed to the registered transport for transporting externally.
*/
virtual MediaConduitErrorCode AttachTransport(mozilla::RefPtr<TransportInterface> aTransport) MOZ_OVERRIDE;
virtual MediaConduitErrorCode SetTransmitterTransport(mozilla::RefPtr<TransportInterface> aTransport) MOZ_OVERRIDE;
virtual MediaConduitErrorCode SetReceiverTransport(mozilla::RefPtr<TransportInterface> aTransport) MOZ_OVERRIDE;
/**
* Function to select and change the encoding resolution based on incoming frame size
@ -233,8 +240,7 @@ public:
WebrtcVideoConduit();
virtual ~WebrtcVideoConduit();
MediaConduitErrorCode Init(WebrtcVideoConduit *other,
bool receiving);
MediaConduitErrorCode Init();
int GetChannel() { return mChannel; }
webrtc::VideoEngine* GetVideoEngine() { return mVideoEngine; }
@ -297,17 +303,10 @@ private:
// Video Latency Test averaging filter
void VideoLatencyUpdate(uint64_t new_sample);
// The two sides of a send/receive pair of conduits each keep a pointer to the other.
// They also share a single VideoEngine and mChannel. Shutdown must be coordinated
// carefully to avoid double-freeing or accessing after one frees.
WebrtcVideoConduit* mOtherDirection;
// The other side has shut down our mChannel and related items already
bool mShutDown;
// A few of these are shared by both directions. They're released by the last
// conduit to die.
webrtc::VideoEngine* mVideoEngine; // shared
mozilla::RefPtr<TransportInterface> mTransport;
webrtc::VideoEngine* mVideoEngine;
mozilla::ReentrantMonitor mTransportMonitor;
mozilla::RefPtr<TransportInterface> mTransmitterTransport;
mozilla::RefPtr<TransportInterface> mReceiverTransport;
mozilla::RefPtr<VideoRenderer> mRenderer;
ScopedCustomReleasePtr<webrtc::ViEBase> mPtrViEBase;
@ -318,11 +317,11 @@ private:
ScopedCustomReleasePtr<webrtc::ViERTP_RTCP> mPtrRTP;
ScopedCustomReleasePtr<webrtc::ViEExternalCodec> mPtrExtCodec;
webrtc::ViEExternalCapture* mPtrExtCapture; // shared
webrtc::ViEExternalCapture* mPtrExtCapture;
// Engine state we are concerned with.
bool mEngineTransmitting; //If true ==> Transmit Sub-system is up and running
bool mEngineReceiving; // if true ==> Receive Sus-sysmtem up and running
mozilla::Atomic<bool> mEngineTransmitting; //If true ==> Transmit Sub-system is up and running
mozilla::Atomic<bool> mEngineReceiving; // if true ==> Receive Sus-sysmtem up and running
int mChannel; // Video Channel for this conduit
int mCapId; // Capturer for this conduit
@ -333,6 +332,7 @@ private:
unsigned short mReceivingWidth;
unsigned short mReceivingHeight;
unsigned int mSendingFramerate;
unsigned short mNumReceivingStreams;
bool mVideoLatencyTestEnable;
uint64_t mVideoLatencyAvg;
uint32_t mMinBitrate;

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