import changes from `dev' branch of rmottola/Arctic-Fox:

- Bug 1231975 - Part 3: Break a reference cycle between PendingResolution and DNSRequestChild. r=drno (ecf45de535)
- Bug 1231975 - Part 4: Add some logging and simplification in TestNrSocket. r=drno (fa811f7743)
- Bug 1231975 - Part 5: Fix an intermittent failure caused by the NAT simulator erroneously canceling NR_ASYNC_WAIT_READ. r=drno (e436a8cc75)
- var-let (d228288673)
- Bug 1256022 - dom/network slow GC on mochitest fix r=dragana (e0dffd5033)
- Bug 1231130 - added mHadLocalInstance to constructor.r=jaas (c4f6d0c530)
- Bug 1121290: Use "%ls" instead of "%s" in _snwprintf_s format string# r=bsmedberg (e0434aca5a)
- Bug 1206952 - Rename MagicRequest to ByteRangeRequest. r=sicking (6780309aa2)
- Bug 1206952 - Convert PluginStreamListener to use channel->AsyncOpen2(). r=sicking (8f41f3e148)
- Bug 1262335 - Part 2. Remove Android GB/HC defines from OMX. r=snorp (e5b7435d92)
- Bug 956899 - Add a std::condition_variable work-alike; r=froydnj (98076c707e)
- Bug 1254123 - Handle OOM more gracefully in js::ErrorToException. (r=Waldo) (f9a2ef18d1)
- Bug 1255128 - Standard argument coercion in new ArrayBuffer(length). r=nbp. Thanks to snowmantw for tests. (06e5cedd80)
- Bug 1251919 - Nuke Debugger wrappers on failure. (r=shu) (64bc41b1f1)
- Bug 1254190 - Propagate failure of matchAllDebuggeeGlobals() in Debugger. (r=shu) (927bf01ec5)
- Bug 1254172 - Make UnboxedLayout::makeNativeGroup robust to unknownProperties on unboxed type. (r=jandem) (398a6b3aa7)
- Bug 1268213 - BlobImplFile::GetTypeRunnable can be a WorkerMainThreadRunnable, r=khuey (30e4ff4b75)
- Bug 1268231 - Get rid of StopSyncLoopRunnable, r=khuey (29b0a0ed4f)
- Bug 1267904 - Add telemetry for WorkerMainThreadRunnable, r=khuey (970d39bcce)
- Bug 1264968 part 2 - Allow persisting attributes of xul:window if its owner document is not root. r=enndeakin (ca8182b534)
- Bug 1244948 - silence the 'loaded script twice' warning. r=bz (fa571b837c)
This commit is contained in:
2024-08-30 21:17:13 +08:00
parent 17528bae8d
commit 4751a4e732
48 changed files with 1365 additions and 246 deletions
+10 -22
View File
@@ -866,43 +866,33 @@ BlobImplFile::GetSize(ErrorResult& aRv)
namespace {
class GetTypeRunnable final : public Runnable
class GetTypeRunnable final : public WorkerMainThreadRunnable
{
public:
GetTypeRunnable(WorkerPrivate* aWorkerPrivate,
nsIEventTarget* aSyncLoopTarget,
BlobImpl* aBlobImpl)
: mWorkerPrivate(aWorkerPrivate)
, mSyncLoopTarget(aSyncLoopTarget)
: WorkerMainThreadRunnable(aWorkerPrivate,
NS_LITERAL_CSTRING("BlobImplFile :: GetType"))
, mBlobImpl(aBlobImpl)
{
MOZ_ASSERT(aWorkerPrivate);
MOZ_ASSERT(aSyncLoopTarget);
MOZ_ASSERT(aBlobImpl);
aWorkerPrivate->AssertIsOnWorkerThread();
}
NS_IMETHOD
Run() override
bool
MainThreadRun() override
{
MOZ_ASSERT(NS_IsMainThread());
nsAutoString type;
mBlobImpl->GetType(type);
RefPtr<MainThreadStopSyncLoopRunnable> runnable =
new MainThreadStopSyncLoopRunnable(mWorkerPrivate,
mSyncLoopTarget.forget(), true);
NS_WARN_IF(!runnable->Dispatch());
return NS_OK;
return true;
}
private:
~GetTypeRunnable()
{}
WorkerPrivate* mWorkerPrivate;
nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
RefPtr<BlobImpl> mBlobImpl;
};
@@ -925,14 +915,12 @@ BlobImplFile::GetType(nsAString& aType)
return;
}
AutoSyncLoopHolder syncLoop(workerPrivate);
RefPtr<GetTypeRunnable> runnable =
new GetTypeRunnable(workerPrivate, syncLoop.EventTarget(), this);
nsresult rv = NS_DispatchToMainThread(runnable);
NS_WARN_IF(NS_FAILED(rv));
new GetTypeRunnable(workerPrivate, this);
NS_WARN_IF(!syncLoop.Run());
ErrorResult rv;
runnable->Dispatch(rv);
NS_WARN_IF(rv.Failed());
return;
}
+11 -6
View File
@@ -288,7 +288,8 @@ public:
const char16_t* aError,
const char16_t** aFormatStrings,
uint32_t aFormatStringsLen)
: WorkerMainThreadRunnable(aImpl->mWorkerPrivate)
: WorkerMainThreadRunnable(aImpl->mWorkerPrivate,
NS_LITERAL_CSTRING("WebSocket :: print error on console"))
, mImpl(aImpl)
, mBundleURI(aBundleURI)
, mError(aError)
@@ -573,7 +574,8 @@ class DisconnectInternalRunnable final : public WorkerMainThreadRunnable
{
public:
explicit DisconnectInternalRunnable(WebSocketImpl* aImpl)
: WorkerMainThreadRunnable(aImpl->mWorkerPrivate)
: WorkerMainThreadRunnable(aImpl->mWorkerPrivate,
NS_LITERAL_CSTRING("WebSocket :: disconnect"))
, mImpl(aImpl)
{ }
@@ -991,8 +993,9 @@ private:
class WebSocketMainThreadRunnable : public WorkerMainThreadRunnable
{
public:
WebSocketMainThreadRunnable(WorkerPrivate* aWorkerPrivate)
: WorkerMainThreadRunnable(aWorkerPrivate)
WebSocketMainThreadRunnable(WorkerPrivate* aWorkerPrivate,
const nsACString& aTelemetryKey)
: WorkerMainThreadRunnable(aWorkerPrivate, aTelemetryKey)
{
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
@@ -1030,7 +1033,8 @@ public:
const nsACString& aScriptFile, uint32_t aScriptLine,
uint32_t aScriptColumn,
ErrorResult& aRv, bool* aConnectionFailed)
: WebSocketMainThreadRunnable(aImpl->mWorkerPrivate)
: WebSocketMainThreadRunnable(aImpl->mWorkerPrivate,
NS_LITERAL_CSTRING("WebSocket :: init"))
, mImpl(aImpl)
, mURL(aURL)
, mProtocolArray(aProtocolArray)
@@ -1099,7 +1103,8 @@ class AsyncOpenRunnable final : public WebSocketMainThreadRunnable
{
public:
AsyncOpenRunnable(WebSocketImpl* aImpl, ErrorResult& aRv)
: WebSocketMainThreadRunnable(aImpl->mWorkerPrivate)
: WebSocketMainThreadRunnable(aImpl->mWorkerPrivate,
NS_LITERAL_CSTRING("WebSocket :: AsyncOpen"))
, mImpl(aImpl)
, mRv(aRv)
{
+2 -1
View File
@@ -73,7 +73,8 @@ public:
InitializeRunnable(WorkerPrivate* aWorkerPrivate, nsACString& aOrigin,
PrincipalInfo& aPrincipalInfo, bool& aPrivateBrowsing,
ErrorResult& aRv)
: WorkerMainThreadRunnable(aWorkerPrivate)
: WorkerMainThreadRunnable(aWorkerPrivate,
NS_LITERAL_CSTRING("BroadcastChannel :: Initialize"))
, mWorkerPrivate(GetCurrentThreadWorkerPrivate())
, mOrigin(aOrigin)
, mPrincipalInfo(aPrincipalInfo)
+4 -2
View File
@@ -269,7 +269,8 @@ public:
const gfx::IntSize& aSize,
const Maybe<IntRect>& aCropRect,
layers::Image** aImage)
: WorkerMainThreadRunnable(GetCurrentThreadWorkerPrivate())
: WorkerMainThreadRunnable(GetCurrentThreadWorkerPrivate(),
NS_LITERAL_CSTRING("ImageBitmap :: Create Image from Raw Data"))
, mImage(aImage)
, mBuffer(aBuffer)
, mBufferLength(aBufferLength)
@@ -1086,7 +1087,8 @@ class CreateImageBitmapFromBlobWorkerTask final : public WorkerSameThreadRunnabl
Blob& aBlob,
Maybe<IntRect>& aCropRect,
layers::Image** aImage)
: WorkerMainThreadRunnable(aWorkerPrivate)
: WorkerMainThreadRunnable(aWorkerPrivate,
NS_LITERAL_CSTRING("ImageBitmap :: Create Image from Blob"))
, mBlob(aBlob)
, mCropRect(aCropRect)
, mImage(aImage)
+2 -1
View File
@@ -718,7 +718,8 @@ class CancelPumpRunnable final : public WorkerMainThreadRunnable
FetchBody<Derived>* mBody;
public:
explicit CancelPumpRunnable(FetchBody<Derived>* aBody)
: WorkerMainThreadRunnable(aBody->mWorkerPrivate)
: WorkerMainThreadRunnable(aBody->mWorkerPrivate,
NS_LITERAL_CSTRING("Fetch :: Cancel Pump"))
, mBody(aBody)
{ }
+2 -1
View File
@@ -231,7 +231,8 @@ public:
ReferrerSameOriginChecker(workers::WorkerPrivate* aWorkerPrivate,
const nsAString& aReferrerURL,
nsresult& aResult)
: workers::WorkerMainThreadRunnable(aWorkerPrivate),
: workers::WorkerMainThreadRunnable(aWorkerPrivate,
NS_LITERAL_CSTRING("Fetch :: Referrer same origin check")),
mReferrerURL(aReferrerURL),
mResult(aResult)
{
+2 -2
View File
@@ -15,8 +15,8 @@ Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
// Ensure NetworkStatsService and NetworkStatsDB are loaded in the parent process
// to receive messages from the child processes.
let appInfo = Cc["@mozilla.org/xre/app-info;1"];
let isParentProcess = !appInfo || appInfo.getService(Ci.nsIXULRuntime)
var appInfo = Cc["@mozilla.org/xre/app-info;1"];
var isParentProcess = !appInfo || appInfo.getService(Ci.nsIXULRuntime)
.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
if (isParentProcess) {
Cu.import("resource://gre/modules/NetworkStatsService.jsm");
+36 -11
View File
@@ -162,6 +162,7 @@ TCPSocket::TCPSocket(nsIGlobalObject* aGlobal, const nsAString& aHost, uint16_t
, mSuspendCount(0)
, mTrackingNumber(0)
, mWaitingForStartTLS(false)
, mObserversActive(false)
#ifdef MOZ_WIDGET_GONK
, mTxBytes(0)
, mRxBytes(0)
@@ -182,6 +183,13 @@ TCPSocket::TCPSocket(nsIGlobalObject* aGlobal, const nsAString& aHost, uint16_t
TCPSocket::~TCPSocket()
{
if (mObserversActive) {
nsCOMPtr<nsIObserverService> obs = do_GetService("@mozilla.org/observer-service;1");
if (obs) {
obs->RemoveObserver(this, "inner-window-destroyed");
obs->RemoveObserver(this, "profile-change-net-teardown");
}
}
}
nsresult
@@ -261,7 +269,9 @@ TCPSocket::Init()
{
nsCOMPtr<nsIObserverService> obs = do_GetService("@mozilla.org/observer-service;1");
if (obs) {
obs->AddObserver(this, "inner-window-destroyed", true);
mObserversActive = true;
obs->AddObserver(this, "inner-window-destroyed", true); // weak reference
obs->AddObserver(this, "profile-change-net-teardown", true); // weak ref
}
if (XRE_GetProcessType() == GeckoProcessType_Content) {
@@ -379,6 +389,7 @@ NS_IMETHODIMP
CopierCallbacks::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext, nsresult aStatus)
{
mOwner->NotifyCopyComplete(aStatus);
mOwner = nullptr;
return NS_OK;
}
} // unnamed namespace
@@ -445,7 +456,10 @@ TCPSocket::NotifyCopyComplete(nsresult aStatus)
}
if (mReadyState == TCPReadyState::Closing) {
mSocketOutputStream->Close();
if (mSocketOutputStream) {
mSocketOutputStream->Close();
mSocketOutputStream = nullptr;
}
mReadyState = TCPReadyState::Closed;
FireEvent(NS_LITERAL_STRING("close"));
}
@@ -637,6 +651,9 @@ TCPSocket::MaybeReportErrorAndCloseIfOpen(nsresult status) {
if (mReadyState == TCPReadyState::Closed) {
return NS_OK;
}
// go through ::Closing state and then mark ::Closed
Close();
mReadyState = TCPReadyState::Closed;
if (NS_FAILED(status)) {
@@ -761,11 +778,19 @@ TCPSocket::Close()
}
uint32_t count = 0;
mMultiplexStream->GetCount(&count);
if (!count) {
mSocketOutputStream->Close();
if (mMultiplexStream) {
mMultiplexStream->GetCount(&count);
}
if (!count) {
if (mSocketOutputStream) {
mSocketOutputStream->Close();
mSocketOutputStream = nullptr;
}
}
if (mSocketInputStream) {
mSocketInputStream->Close();
mSocketInputStream = nullptr;
}
mSocketInputStream->Close();
}
void
@@ -958,6 +983,9 @@ TCPSocket::Constructor(const GlobalObject& aGlobal,
nsresult
TCPSocket::CreateInputStreamPump()
{
if (!mSocketInputStream) {
return NS_ERROR_NOT_AVAILABLE;
}
nsresult rv;
mInputStreamPump = do_CreateInstance("@mozilla.org/network/input-stream-pump;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
@@ -1179,13 +1207,10 @@ TCPSocket::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aD
}
if (innerID == mInnerWindowID) {
nsCOMPtr<nsIObserverService> obs = do_GetService("@mozilla.org/observer-service;1");
if (obs) {
obs->RemoveObserver(this, "inner-window-destroyed");
}
Close();
}
} else if (!strcmp(aTopic, "profile-change-net-teardown")) {
Close();
}
return NS_OK;
+2
View File
@@ -239,6 +239,8 @@ private:
// The buffered data awaiting the TLS upgrade to finish.
nsTArray<nsCOMPtr<nsIInputStream>> mPendingDataAfterStartTLS;
bool mObserversActive;
#ifdef MOZ_WIDGET_GONK
// Number of bytes sent.
uint32_t mTxBytes;
@@ -25,6 +25,9 @@ function runTest() {
ok(new TCPSocket('localhost', 80), "TCPSocket constructor should work for content that has the tcp-socket permission");
ok(navigator.mozTCPSocket.open('localhost', 80), "navigator.mozTCPSocket.open should work for content that has the tcp-socket permission");
// This just helps the test harness clean up quickly
SpecialPowers.forceCC();
SpecialPowers.forceGC();
SimpleTest.finish();
}
</script>
+10 -1
View File
@@ -40,6 +40,8 @@ Test of legacy navigator interface for opening TCPSocket/TCPServerSocket.
-1);
listeningServer.onconnect = function(ev) {
ok(true, "got server connect");
listeningServer.close();
listeningServer = null;
ev.socket.close()
}
@@ -48,7 +50,14 @@ Test of legacy navigator interface for opening TCPSocket/TCPServerSocket.
clientSocket.onopen = function() { ok(true, "got client open"); }
clientSocket.onclose = function() {
ok(true, "got client close");
SimpleTest.finish();
clientSocket.close();
clientSocket = null;
setTimeout(function() {
// This just helps the test harness clean up quickly
SpecialPowers.forceCC();
SpecialPowers.forceGC();
SimpleTest.finish();
}, 0);
}
}
</script>
+6 -3
View File
@@ -289,7 +289,8 @@ class GetPermissionRunnable final : public WorkerMainThreadRunnable
public:
explicit GetPermissionRunnable(WorkerPrivate* aWorker)
: WorkerMainThreadRunnable(aWorker)
: WorkerMainThreadRunnable(aWorker,
NS_LITERAL_CSTRING("Notification :: Get Permission"))
, mPermission(NotificationPermission::Denied)
{ }
@@ -2446,7 +2447,8 @@ class CloseNotificationRunnable final
public:
explicit CloseNotificationRunnable(Notification* aNotification)
: WorkerMainThreadRunnable(aNotification->mWorkerPrivate)
: WorkerMainThreadRunnable(aNotification->mWorkerPrivate,
NS_LITERAL_CSTRING("Notification :: Close Notification"))
, mNotification(aNotification)
, mHadObserver(false)
{}
@@ -2553,7 +2555,8 @@ class CheckLoadRunnable final : public WorkerMainThreadRunnable
public:
explicit CheckLoadRunnable(WorkerPrivate* aWorker, const nsACString& aScope)
: WorkerMainThreadRunnable(aWorker)
: WorkerMainThreadRunnable(aWorker,
NS_LITERAL_CSTRING("Notification :: Check Load"))
, mRv(NS_ERROR_DOM_SECURITY_ERR)
, mScope(aScope)
{ }
+88 -33
View File
@@ -8,6 +8,7 @@
#include "nsContentPolicyUtils.h"
#include "nsIDOMElement.h"
#include "nsIStreamConverterService.h"
#include "nsIStreamLoader.h"
#include "nsIHttpChannel.h"
#include "nsIHttpChannelInternal.h"
#include "nsIFileChannel.h"
@@ -34,7 +35,7 @@
#include "nsDataHashtable.h"
#include "nsNullPrincipal.h"
#define MAGIC_REQUEST_CONTEXT 0x01020304
#define BYTERANGE_REQUEST_CONTEXT 0x01020304
// nsPluginByteRangeStreamListener
@@ -55,7 +56,7 @@ private:
nsCOMPtr<nsIStreamListener> mStreamConverter;
nsWeakPtr mWeakPtrPluginStreamListenerPeer;
bool mRemoveMagicNumber;
bool mRemoveByteRangeRequest;
};
NS_IMPL_ISUPPORTS(nsPluginByteRangeStreamListener,
@@ -66,7 +67,7 @@ NS_IMPL_ISUPPORTS(nsPluginByteRangeStreamListener,
nsPluginByteRangeStreamListener::nsPluginByteRangeStreamListener(nsIWeakReference* aWeakPtr)
{
mWeakPtrPluginStreamListenerPeer = aWeakPtr;
mRemoveMagicNumber = false;
mRemoveByteRangeRequest = false;
}
nsPluginByteRangeStreamListener::~nsPluginByteRangeStreamListener()
@@ -152,7 +153,7 @@ nsPluginByteRangeStreamListener::OnStartRequest(nsIRequest *request, nsISupports
// if server cannot continue with byte range (206 status) and sending us whole object (200 status)
// reset this seekable stream & try serve it to plugin instance as a file
mStreamConverter = finalStreamListener;
mRemoveMagicNumber = true;
mRemoveByteRangeRequest = true;
rv = pslp->ServeStreamAsFile(request, ctxt);
return rv;
@@ -176,15 +177,15 @@ nsPluginByteRangeStreamListener::OnStopRequest(nsIRequest *request, nsISupports
NS_ERROR("OnStopRequest received for untracked byte-range request!");
}
if (mRemoveMagicNumber) {
// remove magic number from container
if (mRemoveByteRangeRequest) {
// remove byte range request from container
nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(ctxt);
if (container) {
uint32_t magicNumber = 0;
container->GetData(&magicNumber);
if (magicNumber == MAGIC_REQUEST_CONTEXT) {
uint32_t byteRangeRequest = 0;
container->GetData(&byteRangeRequest);
if (byteRangeRequest == BYTERANGE_REQUEST_CONTEXT) {
// to allow properly finish nsPluginStreamListenerPeer->OnStopRequest()
// set it to something that is not the magic number.
// set it to something that is not the byte range request.
container->SetData(0);
}
} else {
@@ -664,6 +665,63 @@ nsPluginStreamListenerPeer::MakeByteRangeString(NPByteRange* aRangeList, nsACStr
return;
}
// XXX: Converting the channel within nsPluginStreamListenerPeer
// to use asyncOpen2() and do not want to touch the fragile logic
// of byte range requests. Hence we just introduce this lightweight
// wrapper to proxy the context.
class PluginContextProxy final : public nsIStreamListener
{
public:
NS_DECL_ISUPPORTS
PluginContextProxy(nsIStreamListener *aListener, nsISupports* aContext)
: mListener(aListener)
, mContext(aContext)
{
MOZ_ASSERT(aListener);
MOZ_ASSERT(aContext);
}
NS_IMETHOD
OnDataAvailable(nsIRequest* aRequest,
nsISupports* aContext,
nsIInputStream *aIStream,
uint64_t aSourceOffset,
uint32_t aLength) override
{
// Proxy OnDataAvailable using the internal context
return mListener->OnDataAvailable(aRequest,
mContext,
aIStream,
aSourceOffset,
aLength);
}
NS_IMETHOD
OnStartRequest(nsIRequest* aRequest, nsISupports* aContext) override
{
// Proxy OnStartRequest using the internal context
return mListener->OnStartRequest(aRequest, mContext);
}
NS_IMETHOD
OnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
nsresult aStatusCode) override
{
// Proxy OnStopRequest using the inernal context
return mListener->OnStopRequest(aRequest,
mContext,
aStatusCode);
}
private:
~PluginContextProxy() {}
nsCOMPtr<nsIStreamListener> mListener;
nsCOMPtr<nsISupports> mContext;
};
NS_IMPL_ISUPPORTS(PluginContextProxy, nsIStreamListener)
nsresult
nsPluginStreamListenerPeer::RequestRead(NPByteRange* rangeList)
{
@@ -696,22 +754,19 @@ nsPluginStreamListenerPeer::RequestRead(NPByteRange* rangeList)
rv = NS_NewChannel(getter_AddRefs(channel),
mURL,
requestingNode,
nsILoadInfo::SEC_NORMAL,
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
nsIContentPolicy::TYPE_OTHER,
loadGroup,
callbacks,
nsIChannel::LOAD_BYPASS_SERVICE_WORKER);
}
else {
// in this else branch we really don't know where the load is coming
// from and in fact should use something better than just using
// a nullPrincipal as the loadingPrincipal.
nsCOMPtr<nsIPrincipal> principal = nsNullPrincipal::Create();
NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE);
// In this else branch we really don't know where the load is coming
// from. Let's fall back to using the SystemPrincipal for such Plugins.
rv = NS_NewChannel(getter_AddRefs(channel),
mURL,
principal,
nsILoadInfo::SEC_NORMAL,
nsContentUtils::GetSystemPrincipal(),
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
nsIContentPolicy::TYPE_OTHER,
loadGroup,
callbacks,
@@ -747,16 +802,16 @@ nsPluginStreamListenerPeer::RequestRead(NPByteRange* rangeList)
mPendingRequests += numRequests;
nsCOMPtr<nsISupportsPRUint32> container = do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv);
if (NS_FAILED(rv))
return rv;
rv = container->SetData(MAGIC_REQUEST_CONTEXT);
if (NS_FAILED(rv))
return rv;
NS_ENSURE_SUCCESS(rv, rv);
rv = container->SetData(BYTERANGE_REQUEST_CONTEXT);
NS_ENSURE_SUCCESS(rv, rv);
rv = channel->AsyncOpen(converter, container);
if (NS_SUCCEEDED(rv))
TrackRequest(channel);
return rv;
RefPtr<PluginContextProxy> pluginContextProxy =
new PluginContextProxy(converter, container);
rv = channel->AsyncOpen2(pluginContextProxy);
NS_ENSURE_SUCCESS(rv, rv);
TrackRequest(channel);
return NS_OK;
}
nsresult
@@ -847,12 +902,12 @@ NS_IMETHODIMP nsPluginStreamListenerPeer::OnDataAvailable(nsIRequest *request,
return NS_ERROR_FAILURE;
if (mAbort) {
uint32_t magicNumber = 0; // set it to something that is not the magic number.
uint32_t byteRangeRequest = 0; // set it to something that is not the byte range request.
nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(aContext);
if (container)
container->GetData(&magicNumber);
container->GetData(&byteRangeRequest);
if (magicNumber != MAGIC_REQUEST_CONTEXT) {
if (byteRangeRequest != BYTERANGE_REQUEST_CONTEXT) {
// this is not one of our range requests
mAbort = false;
return NS_BINDING_ABORTED;
@@ -985,9 +1040,9 @@ NS_IMETHODIMP nsPluginStreamListenerPeer::OnStopRequest(nsIRequest *request,
// we keep our connections around...
nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(aContext);
if (container) {
uint32_t magicNumber = 0; // set it to something that is not the magic number.
container->GetData(&magicNumber);
if (magicNumber == MAGIC_REQUEST_CONTEXT) {
uint32_t byteRangeRequest = 0; // something other than the byte range request.
container->GetData(&byteRangeRequest);
if (byteRangeRequest == BYTERANGE_REQUEST_CONTEXT) {
// this is one of our range requests
return NS_OK;
}
+1
View File
@@ -266,6 +266,7 @@ nsPluginTag::nsPluginTag(const char* aName,
: nsIInternalPluginTag(aName, aDescription, aFileName, aVersion),
mId(sNextId++),
mContentProcessRunningCount(0),
mHadLocalInstance(false),
mLibrary(nullptr),
mIsJavaPlugin(false),
mIsFlashPlugin(false),
+1 -1
View File
@@ -71,7 +71,7 @@ static char* GetKeyValue(void* verbuf, const WCHAR* key,
{
WCHAR keybuf[64]; // plenty for the template below, with the longest key
// we use (currently "FileDescription")
const WCHAR keyFormat[] = L"\\StringFileInfo\\%04X%04X\\%s";
const WCHAR keyFormat[] = L"\\StringFileInfo\\%04X%04X\\%ls";
WCHAR *buf = nullptr;
UINT blen;
+2 -1
View File
@@ -1072,7 +1072,8 @@ class SyncStopListeningRunnable final : public WorkerMainThreadRunnable
public:
SyncStopListeningRunnable(WorkerPrivate* aWorkerPrivate,
WorkerListener* aListener)
: WorkerMainThreadRunnable(aWorkerPrivate)
: WorkerMainThreadRunnable(aWorkerPrivate,
NS_LITERAL_CSTRING("ServiceWorkerRegistration :: StopListening"))
, mListener(aListener)
{}
+16 -6
View File
@@ -77,7 +77,8 @@ public:
CreateURLRunnable(WorkerPrivate* aWorkerPrivate, BlobImpl* aBlobImpl,
const mozilla::dom::objectURLOptions& aOptions,
nsAString& aURL)
: WorkerMainThreadRunnable(aWorkerPrivate)
: WorkerMainThreadRunnable(aWorkerPrivate,
NS_LITERAL_CSTRING("URL :: CreateURL"))
, mBlobImpl(aBlobImpl)
, mURL(aURL)
{
@@ -168,7 +169,8 @@ private:
public:
RevokeURLRunnable(WorkerPrivate* aWorkerPrivate,
const nsAString& aURL)
: WorkerMainThreadRunnable(aWorkerPrivate)
: WorkerMainThreadRunnable(aWorkerPrivate,
NS_LITERAL_CSTRING("URL :: RevokeURL"))
, mURL(aURL)
{}
@@ -229,7 +231,8 @@ public:
ConstructorRunnable(WorkerPrivate* aWorkerPrivate,
const nsAString& aURL, const Optional<nsAString>& aBase,
mozilla::ErrorResult& aRv)
: WorkerMainThreadRunnable(aWorkerPrivate)
: WorkerMainThreadRunnable(aWorkerPrivate,
NS_LITERAL_CSTRING("URL :: Constructor"))
, mURL(aURL)
, mRv(aRv)
{
@@ -244,7 +247,8 @@ public:
ConstructorRunnable(WorkerPrivate* aWorkerPrivate,
const nsAString& aURL, URLProxy* aBaseProxy,
mozilla::ErrorResult& aRv)
: WorkerMainThreadRunnable(aWorkerPrivate)
: WorkerMainThreadRunnable(aWorkerPrivate,
NS_LITERAL_CSTRING("URL :: Constructor with BaseURL"))
, mURL(aURL)
, mBaseProxy(aBaseProxy)
, mRv(aRv)
@@ -326,7 +330,10 @@ public:
GetterRunnable(WorkerPrivate* aWorkerPrivate,
GetterType aType, nsAString& aValue,
URLProxy* aURLProxy)
: WorkerMainThreadRunnable(aWorkerPrivate)
: WorkerMainThreadRunnable(aWorkerPrivate,
// We can have telemetry keys for each getter when
// needed.
NS_LITERAL_CSTRING("URL :: getter"))
, mValue(aValue)
, mType(aType)
, mURLProxy(aURLProxy)
@@ -414,7 +421,10 @@ public:
SetterRunnable(WorkerPrivate* aWorkerPrivate,
SetterType aType, const nsAString& aValue,
URLProxy* aURLProxy)
: WorkerMainThreadRunnable(aWorkerPrivate)
: WorkerMainThreadRunnable(aWorkerPrivate,
// We can have telemetry keys for each setter when
// needed.
NS_LITERAL_CSTRING("URL :: setter"))
, mValue(aValue)
, mType(aType)
, mURLProxy(aURLProxy)
+2 -1
View File
@@ -109,7 +109,8 @@ class GetUserAgentRunnable final : public WorkerMainThreadRunnable
public:
GetUserAgentRunnable(WorkerPrivate* aWorkerPrivate, nsString& aUA)
: WorkerMainThreadRunnable(aWorkerPrivate)
: WorkerMainThreadRunnable(aWorkerPrivate,
NS_LITERAL_CSTRING("UserAgent getter"))
, mUA(aUA)
{
MOZ_ASSERT(aWorkerPrivate);
+25 -7
View File
@@ -15,6 +15,7 @@
#include "mozilla/DebugOnly.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/Telemetry.h"
#include "js/RootingAPI.h"
#include "js/Value.h"
@@ -466,19 +467,20 @@ MainThreadWorkerSyncRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate,
{
}
StopSyncLoopRunnable::StopSyncLoopRunnable(
MainThreadStopSyncLoopRunnable::MainThreadStopSyncLoopRunnable(
WorkerPrivate* aWorkerPrivate,
already_AddRefed<nsIEventTarget>&& aSyncLoopTarget,
bool aResult)
: WorkerSyncRunnable(aWorkerPrivate, Move(aSyncLoopTarget)), mResult(aResult)
{
AssertIsOnMainThread();
#ifdef DEBUG
mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget);
#endif
}
nsresult
StopSyncLoopRunnable::Cancel()
MainThreadStopSyncLoopRunnable::Cancel()
{
nsresult rv = Run();
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Run() failed");
@@ -490,8 +492,8 @@ StopSyncLoopRunnable::Cancel()
}
bool
StopSyncLoopRunnable::WorkerRun(JSContext* aCx,
WorkerPrivate* aWorkerPrivate)
MainThreadStopSyncLoopRunnable::WorkerRun(JSContext* aCx,
WorkerPrivate* aWorkerPrivate)
{
aWorkerPrivate->AssertIsOnWorkerThread();
MOZ_ASSERT(mSyncLoopTarget);
@@ -508,11 +510,11 @@ StopSyncLoopRunnable::WorkerRun(JSContext* aCx,
}
bool
StopSyncLoopRunnable::DispatchInternal()
MainThreadStopSyncLoopRunnable::DispatchInternal()
{
MOZ_ASSERT(mSyncLoopTarget);
RefPtr<StopSyncLoopRunnable> runnable(this);
RefPtr<MainThreadStopSyncLoopRunnable> runnable(this);
return NS_SUCCEEDED(mSyncLoopTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL));
}
@@ -572,8 +574,10 @@ MainThreadWorkerControlRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate,
NS_IMPL_ISUPPORTS_INHERITED0(WorkerControlRunnable, WorkerRunnable)
WorkerMainThreadRunnable::WorkerMainThreadRunnable(WorkerPrivate* aWorkerPrivate)
WorkerMainThreadRunnable::WorkerMainThreadRunnable(WorkerPrivate* aWorkerPrivate,
const nsACString& aTelemetryKey)
: mWorkerPrivate(aWorkerPrivate)
, mTelemetryKey(aTelemetryKey)
{
mWorkerPrivate->AssertIsOnWorkerThread();
}
@@ -583,6 +587,8 @@ WorkerMainThreadRunnable::Dispatch(ErrorResult& aRv)
{
mWorkerPrivate->AssertIsOnWorkerThread();
TimeStamp startTime = TimeStamp::NowLoRes();
AutoSyncLoopHolder syncLoop(mWorkerPrivate);
mSyncLoopTarget = syncLoop.EventTarget();
@@ -596,6 +602,10 @@ WorkerMainThreadRunnable::Dispatch(ErrorResult& aRv)
if (!syncLoop.Run()) {
aRv.ThrowUncatchableException();
}
Telemetry::Accumulate(Telemetry::SYNC_WORKER_OPERATION, mTelemetryKey,
static_cast<uint32_t>((TimeStamp::NowLoRes() - startTime)
.ToMilliseconds()));
}
NS_IMETHODIMP
@@ -615,6 +625,14 @@ WorkerMainThreadRunnable::Run()
return NS_OK;
}
WorkerCheckAPIExposureOnMainThreadRunnable::WorkerCheckAPIExposureOnMainThreadRunnable(WorkerPrivate* aWorkerPrivate):
WorkerMainThreadRunnable(aWorkerPrivate,
NS_LITERAL_CSTRING("WorkerCheckAPIExposureOnMainThread"))
{}
WorkerCheckAPIExposureOnMainThreadRunnable::~WorkerCheckAPIExposureOnMainThreadRunnable()
{}
bool
WorkerCheckAPIExposureOnMainThreadRunnable::Dispatch()
{
+24 -40
View File
@@ -264,18 +264,20 @@ private:
PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override;
};
// This runnable is used to stop a sync loop . As sync loops keep the busy count
// incremented as long as they run this runnable does not modify the busy count
// This runnable is used to stop a sync loop and it's meant to be used on the
// main-thread only. As sync loops keep the busy count incremented as long as
// they run this runnable does not modify the busy count
// in any way.
class StopSyncLoopRunnable : public WorkerSyncRunnable
class MainThreadStopSyncLoopRunnable : public WorkerSyncRunnable
{
bool mResult;
public:
// Passing null for aSyncLoopTarget is not allowed.
StopSyncLoopRunnable(WorkerPrivate* aWorkerPrivate,
already_AddRefed<nsIEventTarget>&& aSyncLoopTarget,
bool aResult);
MainThreadStopSyncLoopRunnable(
WorkerPrivate* aWorkerPrivate,
already_AddRefed<nsIEventTarget>&& aSyncLoopTarget,
bool aResult);
// By default StopSyncLoopRunnables cannot be canceled since they could leave
// a sync loop spinning forever.
@@ -283,7 +285,7 @@ public:
Cancel() override;
protected:
virtual ~StopSyncLoopRunnable()
virtual ~MainThreadStopSyncLoopRunnable()
{ }
// Called on the worker thread, in WorkerRun, right before stopping the
@@ -296,33 +298,6 @@ protected:
MaybeSetException()
{ }
private:
virtual bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
virtual bool
DispatchInternal() override final;
};
// This runnable is identical to StopSyncLoopRunnable except it is meant to be
// used on the main thread only.
class MainThreadStopSyncLoopRunnable : public StopSyncLoopRunnable
{
public:
// Passing null for aSyncLoopTarget is not allowed.
MainThreadStopSyncLoopRunnable(
WorkerPrivate* aWorkerPrivate,
already_AddRefed<nsIEventTarget>&& aSyncLoopTarget,
bool aResult)
: StopSyncLoopRunnable(aWorkerPrivate, Move(aSyncLoopTarget), aResult)
{
AssertIsOnMainThread();
}
protected:
virtual ~MainThreadStopSyncLoopRunnable()
{ }
private:
virtual bool
PreDispatch(WorkerPrivate* aWorkerPrivate) override final
@@ -333,6 +308,12 @@ private:
virtual void
PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override;
virtual bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
virtual bool
DispatchInternal() override final;
};
// This runnable is processed as soon as it is received by the worker,
@@ -429,8 +410,10 @@ class WorkerMainThreadRunnable : public Runnable
protected:
WorkerPrivate* mWorkerPrivate;
nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
const nsCString mTelemetryKey;
explicit WorkerMainThreadRunnable(WorkerPrivate* aWorkerPrivate);
explicit WorkerMainThreadRunnable(WorkerPrivate* aWorkerPrivate,
const nsACString& aTelemetryKey);
~WorkerMainThreadRunnable() {}
virtual bool MainThreadRun() = 0;
@@ -452,13 +435,14 @@ private:
// them happen while a worker is shutting down.
//
// Do NOT copy what this class is doing elsewhere. Just don't.
class WorkerCheckAPIExposureOnMainThreadRunnable : public WorkerMainThreadRunnable
class WorkerCheckAPIExposureOnMainThreadRunnable
: public WorkerMainThreadRunnable
{
public:
explicit WorkerCheckAPIExposureOnMainThreadRunnable(WorkerPrivate* aWorkerPrivate):
WorkerMainThreadRunnable(aWorkerPrivate)
{}
~WorkerCheckAPIExposureOnMainThreadRunnable() {}
explicit
WorkerCheckAPIExposureOnMainThreadRunnable(WorkerPrivate* aWorkerPrivate);
virtual
~WorkerCheckAPIExposureOnMainThreadRunnable();
// Returns whether the dispatch succeeded. If this returns false, the API
// should not be exposed.
+5
View File
@@ -914,6 +914,11 @@ static bool
ShouldPersistAttribute(Element* aElement, nsIAtom* aAttribute)
{
if (aElement->IsXULElement(nsGkAtoms::window)) {
// This is not an element of the top document, its owner is
// not an nsXULWindow. Persist it.
if (aElement->OwnerDoc()->GetParentDocument()) {
return true;
}
// The following attributes of xul:window should be handled in
// nsXULWindow::SavePersistentAttributes instead of here.
if (aAttribute == nsGkAtoms::screenX ||
+1 -1
View File
@@ -212,7 +212,7 @@ nsXULPrototypeCache::PutScript(nsIURI* aURI,
{
MOZ_ASSERT(aScriptObject, "Need a non-NULL script");
#ifdef DEBUG
#ifdef DEBUG_BUG_392650
if (mScriptTable.Get(aURI)) {
nsAutoCString scriptName;
aURI->GetSpec(scriptName);
+2 -1
View File
@@ -1502,7 +1502,8 @@ public:
int32_t feature,
nsACString& failureId,
int32_t* status)
: WorkerMainThreadRunnable(workerPrivate)
: WorkerMainThreadRunnable(workerPrivate,
NS_LITERAL_CSTRING("GFX :: GetFeatureStatus"))
, mGfxInfo(gfxInfo)
, mFeature(feature)
, mStatus(status)
+13
View File
@@ -0,0 +1,13 @@
// |jit-test| error: out of memory
if (!('oomTest' in this))
throw new Error("out of memory");
// jsfunfuzz-generated
fullcompartmentchecks(true);
// Adapted from randomly chosen test: js/src/jit-test/tests/debug/bug-1248162.js
var dbg = new Debugger;
dbg.onNewGlobalObject = function() {};
oomTest(function() {
newGlobal();
})
+17
View File
@@ -0,0 +1,17 @@
// |jit-test| error: boom
if (!('oomTest' in this))
throw new Error("boom");
evaluate(`
function ERROR(msg) {
throw new Error("boom");
}
for (var i = 0; i < 9; ++ i) {
var dbg = new Debugger;
dbg.onNewGlobalObject = ERROR;
}
oomTest(function() {
newGlobal();
})
`);
+15
View File
@@ -0,0 +1,15 @@
// |jit-test| error: out of memory
if (!('oomTest' in this))
throw new Error("out of memory");
var g = newGlobal();
var dbg = new Debugger(g);
dbg.onNewScript = function (s) {
log += dbg.findScripts({ source: s.source }).length;
}
log = "";
oomTest(() => {
var static = newGlobal();
g.eval("(function() {})()");
});
+1
View File
@@ -84,6 +84,7 @@ UNIFIED_SOURCES += [
'testStringBuffer.cpp',
'testStructuredClone.cpp',
'testSymbol.cpp',
'testThreadingConditionVariable.cpp',
'testThreadingExclusiveData.cpp',
'testThreadingMutex.cpp',
'testToIntWidth.cpp',
@@ -0,0 +1,230 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "jsapi-tests/tests.h"
#include "threading/ConditionVariable.h"
#include "threading/Mutex.h"
struct TestState {
js::Mutex mutex;
js::ConditionVariable condition;
bool flag;
PRThread* testThread;
explicit TestState(bool createThread = true)
: flag(false), testThread(nullptr)
{
if (createThread) {
testThread = PR_CreateThread(PR_USER_THREAD,
setFlag,
(void *)this,
PR_PRIORITY_NORMAL,
PR_GLOBAL_THREAD,
PR_JOINABLE_THREAD,
0);
MOZ_RELEASE_ASSERT(testThread);
}
}
static void setFlag(void* arg) {
auto& state = *static_cast<TestState*>(arg);
js::UniqueLock<js::Mutex> lock(state.mutex);
state.flag = true;
state.condition.notify_one();
}
void join() {
MOZ_RELEASE_ASSERT(testThread != nullptr);
PR_JoinThread(testThread);
testThread = nullptr;
}
};
BEGIN_TEST(testThreadingConditionVariable)
{
auto state = MakeUnique<TestState>();
{
js::UniqueLock<js::Mutex> lock(state->mutex);
while (!state->flag)
state->condition.wait(lock);
}
state->join();
CHECK(state->flag);
return true;
}
END_TEST(testThreadingConditionVariable)
BEGIN_TEST(testThreadingConditionVariablePredicate)
{
auto state = MakeUnique<TestState>();
{
js::UniqueLock<js::Mutex> lock(state->mutex);
state->condition.wait(lock, [&state]() {return state->flag;});
}
state->join();
CHECK(state->flag);
return true;
}
END_TEST(testThreadingConditionVariablePredicate)
BEGIN_TEST(testThreadingConditionVariableUntilOkay)
{
auto state = MakeUnique<TestState>();
{
js::UniqueLock<js::Mutex> lock(state->mutex);
while (!state->flag) {
auto to = mozilla::TimeStamp::Now() + mozilla::TimeDuration::FromSeconds(600);
js::CVStatus res = state->condition.wait_until(lock, to);
CHECK(res == js::CVStatus::NoTimeout);
}
}
state->join();
CHECK(state->flag);
return true;
}
END_TEST(testThreadingConditionVariableUntilOkay)
BEGIN_TEST(testThreadingConditionVariableUntilTimeout)
{
auto state = MakeUnique<TestState>(false);
{
js::UniqueLock<js::Mutex> lock(state->mutex);
while (!state->flag) {
auto to = mozilla::TimeStamp::Now() + mozilla::TimeDuration::FromMilliseconds(10);
js::CVStatus res = state->condition.wait_until(lock, to);
if (res == js::CVStatus::Timeout)
break;
}
}
CHECK(!state->flag);
// Timeout in the past should return with timeout immediately.
{
js::UniqueLock<js::Mutex> lock(state->mutex);
auto to = mozilla::TimeStamp::Now() - mozilla::TimeDuration::FromMilliseconds(10);
js::CVStatus res = state->condition.wait_until(lock, to);
CHECK(res == js::CVStatus::Timeout);
}
return true;
}
END_TEST(testThreadingConditionVariableUntilTimeout)
BEGIN_TEST(testThreadingConditionVariableUntilOkayPredicate)
{
auto state = MakeUnique<TestState>();
{
js::UniqueLock<js::Mutex> lock(state->mutex);
auto to = mozilla::TimeStamp::Now() + mozilla::TimeDuration::FromSeconds(600);
bool res = state->condition.wait_until(lock, to, [&state](){return state->flag;});
CHECK(res);
}
state->join();
CHECK(state->flag);
return true;
}
END_TEST(testThreadingConditionVariableUntilOkayPredicate)
BEGIN_TEST(testThreadingConditionVariableUntilTimeoutPredicate)
{
auto state = MakeUnique<TestState>(false);
{
js::UniqueLock<js::Mutex> lock(state->mutex);
auto to = mozilla::TimeStamp::Now() + mozilla::TimeDuration::FromMilliseconds(10);
bool res = state->condition.wait_until(lock, to, [&state](){return state->flag;});
CHECK(!res);
}
CHECK(!state->flag);
return true;
}
END_TEST(testThreadingConditionVariableUntilTimeoutPredicate)
BEGIN_TEST(testThreadingConditionVariableForOkay)
{
auto state = MakeUnique<TestState>();
{
js::UniqueLock<js::Mutex> lock(state->mutex);
while (!state->flag) {
auto duration = mozilla::TimeDuration::FromSeconds(600);
js::CVStatus res = state->condition.wait_for(lock, duration);
CHECK(res == js::CVStatus::NoTimeout);
}
}
state->join();
CHECK(state->flag);
return true;
}
END_TEST(testThreadingConditionVariableForOkay)
BEGIN_TEST(testThreadingConditionVariableForTimeout)
{
auto state = MakeUnique<TestState>(false);
{
js::UniqueLock<js::Mutex> lock(state->mutex);
while (!state->flag) {
auto duration = mozilla::TimeDuration::FromMilliseconds(10);
js::CVStatus res = state->condition.wait_for(lock, duration);
if (res == js::CVStatus::Timeout)
break;
}
}
CHECK(!state->flag);
// Timeout in the past should return with timeout immediately.
{
js::UniqueLock<js::Mutex> lock(state->mutex);
auto duration = mozilla::TimeDuration::FromMilliseconds(-10);
js::CVStatus res = state->condition.wait_for(lock, duration);
CHECK(res == js::CVStatus::Timeout);
}
return true;
}
END_TEST(testThreadingConditionVariableForTimeout)
BEGIN_TEST(testThreadingConditionVariableForOkayPredicate)
{
auto state = MakeUnique<TestState>();
{
js::UniqueLock<js::Mutex> lock(state->mutex);
auto duration = mozilla::TimeDuration::FromSeconds(600);
bool res = state->condition.wait_for(lock, duration, [&state](){return state->flag;});
CHECK(res);
}
state->join();
CHECK(state->flag);
return true;
}
END_TEST(testThreadingConditionVariableForOkayPredicate)
BEGIN_TEST(testThreadingConditionVariableForTimeoutPredicate)
{
auto state = MakeUnique<TestState>(false);
{
js::UniqueLock<js::Mutex> lock(state->mutex);
auto duration = mozilla::TimeDuration::FromMilliseconds(10);
bool res = state->condition.wait_for(lock, duration, [&state](){return state->flag;});
CHECK(!res);
}
CHECK(!state->flag);
return true;
}
END_TEST(testThreadingConditionVariableForTimeoutPredicate)
+7 -1
View File
@@ -305,7 +305,13 @@ js::ErrorFromException(JSContext* cx, HandleObject objArg)
if (!obj->is<ErrorObject>())
return nullptr;
return obj->as<ErrorObject>().getOrCreateErrorReport(cx);
JSErrorReport* report = obj->as<ErrorObject>().getOrCreateErrorReport(cx);
if (!report) {
MOZ_ASSERT(cx->isThrowingOutOfMemory());
cx->recoverFromOutOfMemory();
}
return report;
}
JS_PUBLIC_API(JSObject*)
+13
View File
@@ -266,6 +266,19 @@ ToInteger(JSContext* cx, HandleValue v, double* dp)
return true;
}
/* ECMA-262 draft (2016 Mar 19) 7.1.15 ToLength ( argument ) */
inline double
ToLength(double argument)
{
const double MAX_SAFE_INTEGER = 9007199254740991;
double len = JS::ToInteger(argument);
if (len <= 0)
return 0;
if (len > MAX_SAFE_INTEGER)
return MAX_SAFE_INTEGER;
return len;
}
/* ES6 7.1.15 ToLength, but clamped to the [0,2^32-2] range. If the
* return value is false then *overflow will be true iff the value was
* not clampable to uint32_t range.
+4
View File
@@ -558,6 +558,7 @@ elif CONFIG['JS_CODEGEN_MIPS32'] or CONFIG['JS_CODEGEN_MIPS64']:
if CONFIG['OS_ARCH'] == 'WINNT':
SOURCES += [
'jit/ExecutableAllocatorWin.cpp',
'threading/windows/ConditionVariable.cpp',
'threading/windows/Mutex.cpp',
]
# _CRT_RAND_S must be #defined before #including stdlib.h to get rand_s()
@@ -565,6 +566,7 @@ if CONFIG['OS_ARCH'] == 'WINNT':
else:
SOURCES += [
'jit/ExecutableAllocatorPosix.cpp',
'threading/posix/ConditionVariable.cpp',
'threading/posix/Mutex.cpp',
]
@@ -706,6 +708,8 @@ if CONFIG['OS_ARCH'] == 'SunOS':
'socket',
]
OS_LIBS += CONFIG['REALTIME_LIBS']
CFLAGS += CONFIG['MOZ_ICU_CFLAGS']
CXXFLAGS += CONFIG['MOZ_ICU_CFLAGS']
LOCAL_INCLUDES += CONFIG['MOZ_ICU_INCLUDES']
@@ -0,0 +1,20 @@
let badLengths = [
-1,
1.0001342,
0x100000000,
0x200000000,
0x400000000,
Infinity,
NaN
];
for (let n of badLengths)
assertThrowsInstanceOf(() => new ArrayBuffer(n), RangeError);
assertEq(new ArrayBuffer({valueOf: () => 123}).byteLength, 123);
assertEq(new ArrayBuffer(true).byteLength, 1);
assertEq(new ArrayBuffer([123]).byteLength, 123);
assertEq(new ArrayBuffer(["0x10"]).byteLength, 16);
if (typeof reportCompare === 'function')
reportCompare(0, 0, "ok");
@@ -1,7 +1,7 @@
// |reftest| skip-if(!xulRuntime.shell) -- needs detachArrayBuffer
for (var detachArg of ['change-data', 'same-data']) {
var buf = new ArrayBuffer([1,2]);
var buf = new ArrayBuffer(2);
var bufView = new DataView(buf);
detachArrayBuffer(buf, detachArg);
+95
View File
@@ -0,0 +1,95 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef threading_ConditionVariable_h
#define threading_ConditionVariable_h
#include "mozilla/Attributes.h"
#include "mozilla/Move.h"
#include "mozilla/TimeStamp.h"
#include <stdint.h>
#ifndef XP_WIN
# include <pthread.h>
#endif
#include "threading/LockGuard.h"
#include "threading/Mutex.h"
namespace js {
template <typename T> using UniqueLock = LockGuard<T>;
enum class CVStatus {
NoTimeout,
Timeout
};
// A poly-fill for std::condition_variable.
class ConditionVariable
{
public:
struct PlatformData;
ConditionVariable();
~ConditionVariable();
void notify_one();
void notify_all();
void wait(UniqueLock<Mutex>& lock);
template <typename Predicate>
void wait(UniqueLock<Mutex>& lock, Predicate pred) {
while (!pred()) {
wait(lock);
}
}
CVStatus wait_until(UniqueLock<Mutex>& lock,
const mozilla::TimeStamp& abs_time);
template <typename Predicate>
bool wait_until(UniqueLock<Mutex>& lock, const mozilla::TimeStamp& abs_time,
Predicate pred) {
while (!pred()) {
if (wait_until(lock, abs_time) == CVStatus::Timeout) {
return pred();
}
}
return true;
}
CVStatus wait_for(UniqueLock<Mutex>& lock,
const mozilla::TimeDuration& rel_time);
template <typename Predicate>
bool wait_for(UniqueLock<Mutex>& lock, const mozilla::TimeDuration& rel_time,
Predicate pred) {
return wait_until(lock, mozilla::TimeStamp::Now() + rel_time,
mozilla::Move(pred));
}
private:
ConditionVariable(const ConditionVariable&) = delete;
ConditionVariable& operator=(const ConditionVariable&) = delete;
PlatformData* platformData();
#ifndef XP_WIN
void* platformData_[sizeof(pthread_cond_t) / sizeof(void*)];
static_assert(sizeof(pthread_cond_t) / sizeof(void*) != 0 &&
sizeof(pthread_cond_t) % sizeof(void*) == 0,
"pthread_cond_t must have pointer alignment");
#else
void* platformData_[4];
#endif
};
} // namespace js
#endif // threading_ConditionVariable_h
+1
View File
@@ -15,6 +15,7 @@ template <typename Mutex>
class MOZ_RAII LockGuard
{
friend class UnlockGuard<Mutex>;
friend class ConditionVariable;
Mutex& lock;
public:
+1
View File
@@ -44,6 +44,7 @@ private:
Mutex(const Mutex&) = delete;
void operator=(const Mutex&) = delete;
friend class ConditionVariable;
PlatformData* platformData() {
MOZ_ASSERT(platformData_);
return platformData_;
@@ -0,0 +1,174 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/Assertions.h"
#include "mozilla/CheckedInt.h"
#include <errno.h>
#include <pthread.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include "threading/ConditionVariable.h"
#include "threading/Mutex.h"
#include "threading/posix/MutexPlatformData.h"
using mozilla::CheckedInt;
using mozilla::TimeDuration;
using mozilla::TimeStamp;
static const long NanoSecPerSec = 1000000000;
// Android has the clock functions, but not pthread_condattr_setclock.
#if defined(HAVE_CLOCK_MONOTONIC) && !defined(__ANDROID__)
# define USE_CLOCK_API
#endif
#ifdef USE_CLOCK_API
// The C++ specification defines std::condition_variable::wait_for in terms of
// std::chrono::steady_clock, which is closest to CLOCK_MONOTONIC.
static const clockid_t WhichClock = CLOCK_MONOTONIC;
// While timevaladd is widely available to work with timevals, the newer
// timespec structure is largely lacking such conveniences. Thankfully, the
// utilities available in MFBT make implementing our own quite easy.
static void
timespecadd(struct timespec* lhs, struct timespec* rhs, struct timespec* result)
{
// Add nanoseconds. This may wrap, but not above 2 billion.
MOZ_RELEASE_ASSERT(lhs->tv_nsec < NanoSecPerSec);
MOZ_RELEASE_ASSERT(rhs->tv_nsec < NanoSecPerSec);
result->tv_nsec = lhs->tv_nsec + rhs->tv_nsec;
// Add seconds, checking for overflow in the platform specific time_t type.
CheckedInt<time_t> sec = CheckedInt<time_t>(lhs->tv_sec) + rhs->tv_sec;
// If nanoseconds overflowed, carry the result over into seconds.
if (result->tv_nsec >= NanoSecPerSec) {
MOZ_RELEASE_ASSERT(result->tv_nsec < 2 * NanoSecPerSec);
result->tv_nsec -= NanoSecPerSec;
sec += 1;
}
// Extracting the value asserts that there was no overflow.
MOZ_RELEASE_ASSERT(sec.isValid());
result->tv_sec = sec.value();
}
#endif
struct js::ConditionVariable::PlatformData
{
pthread_cond_t ptCond;
};
js::ConditionVariable::ConditionVariable()
{
pthread_cond_t* ptCond = &platformData()->ptCond;
#ifdef USE_CLOCK_API
pthread_condattr_t attr;
int r0 = pthread_condattr_init(&attr);
MOZ_RELEASE_ASSERT(!r0);
int r1 = pthread_condattr_setclock(&attr, WhichClock);
MOZ_RELEASE_ASSERT(!r1);
int r2 = pthread_cond_init(ptCond, &attr);
MOZ_RELEASE_ASSERT(!r2);
int r3 = pthread_condattr_destroy(&attr);
MOZ_RELEASE_ASSERT(!r3);
#else
int r = pthread_cond_init(ptCond, NULL);
MOZ_RELEASE_ASSERT(!r);
#endif
}
js::ConditionVariable::~ConditionVariable()
{
int r = pthread_cond_destroy(&platformData()->ptCond);
MOZ_RELEASE_ASSERT(r == 0);
}
void
js::ConditionVariable::notify_one()
{
int r = pthread_cond_signal(&platformData()->ptCond);
MOZ_RELEASE_ASSERT(r == 0);
}
void
js::ConditionVariable::notify_all()
{
int r = pthread_cond_broadcast(&platformData()->ptCond);
MOZ_RELEASE_ASSERT(r == 0);
}
void
js::ConditionVariable::wait(UniqueLock<Mutex>& lock)
{
pthread_cond_t* ptCond = &platformData()->ptCond;
pthread_mutex_t* ptMutex = &lock.lock.platformData()->ptMutex;
int r = pthread_cond_wait(ptCond, ptMutex);
MOZ_RELEASE_ASSERT(r == 0);
}
js::CVStatus
js::ConditionVariable::wait_until(UniqueLock<Mutex>& lock,
const TimeStamp& abs_time)
{
return wait_for(lock, abs_time - TimeStamp::Now());
}
js::CVStatus
js::ConditionVariable::wait_for(UniqueLock<Mutex>& lock,
const TimeDuration& a_rel_time)
{
pthread_cond_t* ptCond = &platformData()->ptCond;
pthread_mutex_t* ptMutex = &lock.lock.platformData()->ptMutex;
int r;
// Clamp to 0, as time_t is unsigned.
TimeDuration rel_time = a_rel_time < TimeDuration::FromSeconds(0)
? TimeDuration::FromSeconds(0)
: a_rel_time;
// Convert the duration to a timespec.
struct timespec rel_ts;
rel_ts.tv_sec = static_cast<time_t>(rel_time.ToSeconds());
rel_ts.tv_nsec = static_cast<uint64_t>(rel_time.ToMicroseconds() * 1000.0) % NanoSecPerSec;
#ifdef USE_CLOCK_API
struct timespec now_ts;
r = clock_gettime(WhichClock, &now_ts);
MOZ_RELEASE_ASSERT(!r);
struct timespec abs_ts;
timespecadd(&now_ts, &rel_ts, &abs_ts);
r = pthread_cond_timedwait(ptCond, ptMutex, &abs_ts);
#else
// Our non-clock-supporting platforms, OS X and Android, do support waiting
// on a condition variable with a relative timeout.
r = pthread_cond_timedwait_relative_np(ptCond, ptMutex, &rel_ts);
#endif
if (r == 0) {
return CVStatus::NoTimeout;
}
MOZ_RELEASE_ASSERT(r == ETIMEDOUT);
return CVStatus::Timeout;
}
js::ConditionVariable::PlatformData*
js::ConditionVariable::platformData()
{
static_assert(sizeof platformData_ >= sizeof(PlatformData),
"platformData_ is too small");
return reinterpret_cast<PlatformData*>(platformData_);
}
@@ -0,0 +1,416 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/Assertions.h"
#include <float.h>
#include <intrin.h>
#include <stdlib.h>
#include <Windows.h>
#include "threading/ConditionVariable.h"
#include "threading/Mutex.h"
#include "threading/windows/MutexPlatformData.h"
// Some versions of the Windows SDK have a bug where some interlocked functions
// are not redefined as compiler intrinsics. Fix that for the interlocked
// functions that are used in this file.
#if defined(_MSC_VER) && !defined(InterlockedExchangeAdd)
#define InterlockedExchangeAdd(addend, value) \
_InterlockedExchangeAdd((volatile long*)(addend), (long)(value))
#endif
#if defined(_MSC_VER) && !defined(InterlockedIncrement)
#define InterlockedIncrement(addend) \
_InterlockedIncrement((volatile long*)(addend))
#endif
// Windows XP and Server 2003 don't support condition variables natively. The
// NativeImports class is responsible for detecting native support and
// retrieving the appropriate function pointers. It gets instantiated once,
// using a static initializer.
class ConditionVariableNativeImports
{
public:
ConditionVariableNativeImports() {
HMODULE kernel32_dll = GetModuleHandle("kernel32.dll");
MOZ_RELEASE_ASSERT(kernel32_dll != NULL);
#define LOAD_SYMBOL(symbol) loadSymbol(kernel32_dll, #symbol, symbol)
supported_ = LOAD_SYMBOL(InitializeConditionVariable) &&
LOAD_SYMBOL(WakeConditionVariable) &&
LOAD_SYMBOL(WakeAllConditionVariable) &&
LOAD_SYMBOL(SleepConditionVariableCS);
#undef LOAD_SYMBOL
}
inline bool supported() const {
return supported_;
}
void(WINAPI* InitializeConditionVariable)(CONDITION_VARIABLE* ConditionVariable);
void(WINAPI* WakeAllConditionVariable)(PCONDITION_VARIABLE ConditionVariable);
void(WINAPI* WakeConditionVariable)(CONDITION_VARIABLE* ConditionVariable);
BOOL(WINAPI* SleepConditionVariableCS)(CONDITION_VARIABLE* ConditionVariable,
CRITICAL_SECTION* CriticalSection,
DWORD dwMilliseconds);
private:
template <typename T>
inline bool loadSymbol(HMODULE module, const char* name, T& fn) {
void* ptr = GetProcAddress(module, name);
if (!ptr)
return false;
fn = reinterpret_cast<T>(ptr);
return true;
}
bool supported_;
};
static ConditionVariableNativeImports sNativeImports;
// Wrapper for native condition variable APIs.
struct ConditionVariableNative
{
inline void initialize() {
sNativeImports.InitializeConditionVariable(&cv_);
}
inline void destroy() {
// Native condition variables don't require cleanup.
}
inline void notify_one() { sNativeImports.WakeConditionVariable(&cv_); }
inline void notify_all() { sNativeImports.WakeAllConditionVariable(&cv_); }
inline bool wait(CRITICAL_SECTION* cs, DWORD msec) {
return sNativeImports.SleepConditionVariableCS(&cv_, cs, msec);
}
private:
CONDITION_VARIABLE cv_;
};
// Fallback condition variable support for Windows XP and Server 2003. Given the
// difficulty of testing on these antiquated platforms and their rapidly
// diminishing market share, this implementation trades performance for
// predictable behavior.
struct ConditionVariableFallback
{
static const uint32_t WAKEUP_MODE_NONE = 0;
static const uint32_t WAKEUP_MODE_ONE = 0x40000000;
static const uint32_t WAKEUP_MODE_ALL = 0x80000000;
static const uint32_t WAKEUP_MODE_MASK = WAKEUP_MODE_ONE | WAKEUP_MODE_ALL;
static const uint32_t SLEEPERS_COUNT_MASK = ~WAKEUP_MODE_MASK;
void initialize()
{
// Initialize the state variable to 0 sleepers, no wakeup.
sleepersCountAndWakeupMode_ = 0 | WAKEUP_MODE_NONE;
// Create a semaphore that prevents threads from entering sleep,
// or waking other threads while a wakeup is ongoing.
sleepWakeupSemaphore_ = CreateSemaphoreW(NULL, 1, 1, NULL);
MOZ_RELEASE_ASSERT(sleepWakeupSemaphore_);
// Use an auto-reset event for waking up a single sleeper.
wakeOneEvent_ = CreateEventW(NULL, FALSE, FALSE, NULL);
MOZ_RELEASE_ASSERT(wakeOneEvent_);
// Use a manual-reset event for waking up all sleepers.
wakeAllEvent_ = CreateEventW(NULL, TRUE, FALSE, NULL);
MOZ_RELEASE_ASSERT(wakeAllEvent_);
}
void destroy()
{
BOOL r;
MOZ_RELEASE_ASSERT(sleepersCountAndWakeupMode_ == (0 | WAKEUP_MODE_NONE));
r = CloseHandle(sleepWakeupSemaphore_);
MOZ_RELEASE_ASSERT(r);
r = CloseHandle(wakeOneEvent_);
MOZ_RELEASE_ASSERT(r);
r = CloseHandle(wakeAllEvent_);
MOZ_RELEASE_ASSERT(r);
}
private:
void wakeup(uint32_t wakeupMode, HANDLE wakeEvent)
{
// Ensure that only one thread at a time can wake up others.
BOOL result = WaitForSingleObject(sleepWakeupSemaphore_, INFINITE);
MOZ_RELEASE_ASSERT(result == WAIT_OBJECT_0);
// Atomically set the wakeup mode and retrieve the number of sleepers.
uint32_t wcwm = InterlockedExchangeAdd(&sleepersCountAndWakeupMode_,
wakeupMode);
uint32_t sleepersCount = wcwm & SLEEPERS_COUNT_MASK;
MOZ_RELEASE_ASSERT((wcwm & WAKEUP_MODE_MASK) == WAKEUP_MODE_NONE);
if (sleepersCount > 0) {
// If there are any sleepers, set the wake event. The (last) woken
// up thread is responsible for releasing the semaphore.
BOOL success = SetEvent(wakeEvent);
MOZ_RELEASE_ASSERT(success);
} else {
// If there are no sleepers, set the wakeup mode back to 'none'
// and release the semaphore ourselves.
sleepersCountAndWakeupMode_ = 0 | WAKEUP_MODE_NONE;
BOOL success = ReleaseSemaphore(sleepWakeupSemaphore_, 1, NULL);
MOZ_RELEASE_ASSERT(success);
}
}
public:
void notify_one() { wakeup(WAKEUP_MODE_ONE, wakeOneEvent_); }
void notify_all() { wakeup(WAKEUP_MODE_ALL, wakeAllEvent_); }
bool wait(CRITICAL_SECTION* userLock, DWORD msec)
{
// Make sure that we can't enter sleep when there are other threads
// that still need to wake up on either of the wake events being set.
DWORD result = WaitForSingleObject(sleepWakeupSemaphore_, INFINITE);
MOZ_RELEASE_ASSERT(result == WAIT_OBJECT_0);
// Register ourselves as a sleeper. Use an atomic operation, because
// if another thread times out at the same time, it will decrement the
// sleepers count without acquiring the semaphore.
uint32_t wcwm = InterlockedIncrement(&sleepersCountAndWakeupMode_);
MOZ_RELEASE_ASSERT((wcwm & WAKEUP_MODE_MASK) == WAKEUP_MODE_NONE);
// Now that that this thread has been enlisted as a sleeper, it is safe
// again for other threads to do a wakeup.
BOOL success = ReleaseSemaphore(sleepWakeupSemaphore_, 1, NULL);
MOZ_RELEASE_ASSERT(success);
// Release the caller's mutex.
LeaveCriticalSection(userLock);
// Wait for either event to become signaled, which happens when
// notify_one() or notify_all() is called, or for a timeout.
HANDLE handles[2] = { wakeOneEvent_, wakeAllEvent_ };
DWORD waitResult = WaitForMultipleObjects(2, handles, FALSE, msec);
MOZ_RELEASE_ASSERT(waitResult == WAIT_OBJECT_0 ||
waitResult == WAIT_OBJECT_0 + 1 ||
(waitResult == WAIT_TIMEOUT && msec != INFINITE));
// Atomically decrease the sleepers count and retrieve the wakeup mode
// and new sleepers count.
// If the wait returned because wakeOneEvent_ was set, we are certain
// that the wakeup mode will be WAKEUP_MODE_ONE. In that case,
// atomically reset the wakeup mode to 'none', because if another
// thread's sleep times out at same time and it finds that it was the
// last sleeper, it decides whether or not to reset the wakeOneEvent_
// based on the current wakeup mode.
uint32_t sub;
if (waitResult == WAIT_OBJECT_0)
sub = 1 | WAKEUP_MODE_ONE;
else
sub = 1;
// Note that InterlockedExchangeAdd returns the old value, but it's
// easier to work with the new value.
wcwm = InterlockedExchangeAdd(&sleepersCountAndWakeupMode_, -sub) - sub;
uint32_t wakeupMode = wcwm & WAKEUP_MODE_MASK;
uint32_t sleepersCount = wcwm & SLEEPERS_COUNT_MASK;
bool releaseSleepWakeupSemaphore = false;
if (waitResult == WAIT_OBJECT_0) {
// The wake-one event is an auto-reset event so if we're woken by
// it, it should already have been reset. We also already removed
// the WAKEUP_MODE_ONE bit so the wakeup mode should now be 'none'
// again.
MOZ_RELEASE_ASSERT(wakeupMode == WAKEUP_MODE_NONE);
// The signaling thread has acquired the enter-wakeup semaphore and
// expects the woken (this) thread to release it again.
releaseSleepWakeupSemaphore = true;
} else if (waitResult == WAIT_TIMEOUT && wakeupMode == WAKEUP_MODE_ONE &&
sleepersCount == 0) {
// In theory a race condition is possible where the last sleeper
// times out right at the moment that another thread signals it.
// If that just happened we now have a dangling signal event and
// mode, but no threads to be woken up by it, and we need to clean
// that up.
BOOL success = ResetEvent(wakeOneEvent_);
MOZ_RELEASE_ASSERT(success);
// This is safe - we are certain there are no other sleepers that
// could wake up right now, and the semaphore ensures that no
// non-sleeping threads are messing with
// sleepersCountAndWakeupMode_.
sleepersCountAndWakeupMode_ = 0 | WAKEUP_MODE_NONE;
// The signaling thread has acquired the sleep-wakeup semaphore and
// expects the woken thread to release it. But since there are no
// sleeping threads left this thread will do it instead.
releaseSleepWakeupSemaphore = true;
} else if (wakeupMode == WAKEUP_MODE_ALL && sleepersCount == 0) {
// If this was the last thread waking up in response to a
// notify_all, clear the wakeup mode and reset the wake-all event.
// A race condition similar to the case described above could
// occur, so waitResult could be WAIT_TIMEOUT, but that doesn't
// matter for the actions that need to be taken.
MOZ_RELEASE_ASSERT(waitResult = WAIT_OBJECT_0 + 1 ||
waitResult == WAIT_TIMEOUT);
BOOL success = ResetEvent(wakeAllEvent_);
MOZ_RELEASE_ASSERT(success);
sleepersCountAndWakeupMode_ = 0 | WAKEUP_MODE_NONE;
// The broadcasting thread has acquired the enter-wakeup semaphore
// and expects the last thread that wakes up to release it.
releaseSleepWakeupSemaphore = true;
} else if ((waitResult == WAIT_TIMEOUT && msec != INFINITE) ||
(waitResult == WAIT_OBJECT_0 + 1 &&
wakeupMode == WAKEUP_MODE_ALL)) {
// Either:
// * The wait timed out but found no active notify_one or notify_all
// the moment it decreased the wait count.
// * A notify_all woke up this thread but there are more threads
// that need to be woken up by the wake-all event.
// These are ordinary conditions in which we don't have to do
// anything.
} else {
MOZ_CRASH("invalid wakeup condition");
}
// Release the enter-wakeup semaphore if the wakeup condition requires
// us to do it.
if (releaseSleepWakeupSemaphore) {
BOOL success = ReleaseSemaphore(sleepWakeupSemaphore_, 1, NULL);
MOZ_RELEASE_ASSERT(success);
}
// Reacquire the user mutex.
EnterCriticalSection(userLock);
// Return true if woken up, false when timed out.
if (waitResult == WAIT_TIMEOUT) {
SetLastError(ERROR_TIMEOUT);
return false;
}
return true;
}
private:
uint32_t sleepersCountAndWakeupMode_;
HANDLE sleepWakeupSemaphore_;
HANDLE wakeOneEvent_;
HANDLE wakeAllEvent_;
};
struct js::ConditionVariable::PlatformData
{
union
{
ConditionVariableNative native;
ConditionVariableFallback fallback;
};
};
js::ConditionVariable::ConditionVariable()
{
if (sNativeImports.supported())
platformData()->native.initialize();
else
platformData()->fallback.initialize();
}
void
js::ConditionVariable::notify_one()
{
if (sNativeImports.supported())
platformData()->native.notify_one();
else
platformData()->fallback.notify_one();
}
void
js::ConditionVariable::notify_all()
{
if (sNativeImports.supported())
platformData()->native.notify_all();
else
platformData()->fallback.notify_all();
}
void
js::ConditionVariable::wait(UniqueLock<Mutex>& lock)
{
CRITICAL_SECTION* cs = &lock.lock.platformData()->criticalSection;
bool r;
if (sNativeImports.supported())
r = platformData()->native.wait(cs, INFINITE);
else
r = platformData()->fallback.wait(cs, INFINITE);
MOZ_RELEASE_ASSERT(r);
}
js::CVStatus
js::ConditionVariable::wait_until(UniqueLock<Mutex>& lock,
const mozilla::TimeStamp& abs_time)
{
return wait_for(lock, abs_time - mozilla::TimeStamp::Now());
}
js::CVStatus
js::ConditionVariable::wait_for(UniqueLock<Mutex>& lock,
const mozilla::TimeDuration& rel_time)
{
CRITICAL_SECTION* cs = &lock.lock.platformData()->criticalSection;
// Note that DWORD is unsigned, so we have to be careful to clamp at 0.
double msecd = rel_time.ToMilliseconds();
DWORD msec = msecd < 0.0
? 0
: msecd > DBL_MAX
? INFINITE
: static_cast<DWORD>(msecd);
BOOL r;
if (sNativeImports.supported())
r = platformData()->native.wait(cs, msec);
else
r = platformData()->fallback.wait(cs, msec);
if (r)
return CVStatus::NoTimeout;
MOZ_RELEASE_ASSERT(GetLastError() == ERROR_TIMEOUT);
return CVStatus::Timeout;
}
js::ConditionVariable::~ConditionVariable()
{
if (sNativeImports.supported())
platformData()->native.destroy();
else
platformData()->fallback.destroy();
}
inline js::ConditionVariable::PlatformData*
js::ConditionVariable::platformData()
{
static_assert(sizeof platformData_ >= sizeof(PlatformData),
"platformData_ is too small");
return reinterpret_cast<PlatformData*>(platformData_);
}
+15 -13
View File
@@ -202,37 +202,39 @@ ArrayBufferObject::fun_isView(JSContext* cx, unsigned argc, Value* vp)
return true;
}
/*
* new ArrayBuffer(byteLength)
*/
// new ArrayBuffer(byteLength) - ECMA-262 draft (2016 Mar 19) 24.1.2.1
bool
ArrayBufferObject::class_constructor(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
// Step 1.
if (!ThrowIfNotConstructing(cx, args, "ArrayBuffer"))
return false;
int32_t nbytes = 0;
if (argc > 0 && !ToInt32(cx, args[0], &nbytes))
return false;
// Step 2. ES6 specifies that `new ArrayBuffer()` without arguments should
// throw, but it's a bug.
double length = 0;
if (args.hasDefined(0)) {
if (!ToNumber(cx, args[0], &length))
return false;
}
if (nbytes < 0) {
/*
* We're just not going to support arrays that are bigger than what will fit
* as an integer value; if someone actually ever complains (validly), then we
* can fix.
*/
// Steps 3-4. Also refuse to allocate buffers 1GiB or larger.
double byteLength = ToLength(length);
const double SIZE_LIMIT = 1024.0 * 1024 * 1024;
if (length != byteLength || byteLength >= SIZE_LIMIT) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
return false;
}
// Step 5.
RootedObject proto(cx);
RootedObject newTarget(cx, &args.newTarget().toObject());
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
return false;
JSObject* bufobj = create(cx, uint32_t(nbytes), proto);
JSObject* bufobj = create(cx, uint32_t(byteLength), proto);
if (!bufobj)
return false;
args.rval().setObject(*bufobj);
+28 -7
View File
@@ -398,6 +398,14 @@ Debugger::slowPathCheckNoExecute(JSContext* cx, HandleScript script)
return EnterDebuggeeNoExecute::reportIfFoundInStack(cx, script);
}
static inline void
NukeDebuggerWrapper(NativeObject *wrapper)
{
// In some OOM failure cases, we need to destroy the edge to the referent,
// to avoid trying to trace it during untimely collections.
wrapper->setPrivate(nullptr);
}
/*** Breakpoints *********************************************************************************/
@@ -934,14 +942,19 @@ Debugger::wrapEnvironment(JSContext* cx, Handle<Env*> env, MutableHandleValue rv
return false;
envobj->setPrivateGCThing(env);
envobj->setReservedSlot(JSSLOT_DEBUGENV_OWNER, ObjectValue(*object));
if (!p.add(cx, environments, env, envobj))
if (!p.add(cx, environments, env, envobj)) {
NukeDebuggerWrapper(envobj);
return false;
}
CrossCompartmentKey key(CrossCompartmentKey::DebuggerEnvironment, object, env);
if (!object->compartment()->putWrapper(cx, key, ObjectValue(*envobj))) {
NukeDebuggerWrapper(envobj);
environments.remove(env);
return false;
}
}
rval.setObject(*envobj);
return true;
@@ -976,12 +989,15 @@ Debugger::wrapDebuggeeValue(JSContext* cx, MutableHandleValue vp)
dobj->setPrivateGCThing(obj);
dobj->setReservedSlot(JSSLOT_DEBUGOBJECT_OWNER, ObjectValue(*object));
if (!p.add(cx, objects, obj, dobj))
if (!p.add(cx, objects, obj, dobj)) {
NukeDebuggerWrapper(dobj);
return false;
}
if (obj->compartment() != object->compartment()) {
CrossCompartmentKey key(CrossCompartmentKey::DebuggerObject, object, obj);
if (!object->compartment()->putWrapper(cx, key, ObjectValue(*dobj))) {
NukeDebuggerWrapper(dobj);
objects.remove(obj);
ReportOutOfMemory(cx);
return false;
@@ -3856,7 +3872,8 @@ class MOZ_STACK_CLASS Debugger::ScriptQuery
if (!GetProperty(cx, query, query, cx->names().global, &global))
return false;
if (global.isUndefined()) {
matchAllDebuggeeGlobals();
if (!matchAllDebuggeeGlobals())
return false;
} else {
GlobalObject* globalObject = debugger->unwrapDebuggeeArgument(cx, global);
if (!globalObject)
@@ -5028,7 +5045,7 @@ class DebuggerScriptSetPrivateMatcher
ReturnType match(Handle<WasmModuleObject*> module) { obj_->setPrivateGCThing(module); }
};
JSObject*
NativeObject*
Debugger::newDebuggerScript(JSContext* cx, Handle<DebuggerScriptReferent> referent)
{
assertSameCompartment(cx, object.get());
@@ -5058,19 +5075,23 @@ Debugger::wrapVariantReferent(JSContext* cx, Map& map, CrossCompartmentKey::Kind
DependentAddPtr<Map> p(cx, map, untaggedReferent);
if (!p) {
JSObject* wrapper = newVariantWrapper(cx, referent);
NativeObject* wrapper = newVariantWrapper(cx, referent);
if (!wrapper)
return nullptr;
if (!p.add(cx, map, untaggedReferent, wrapper))
if (!p.add(cx, map, untaggedReferent, wrapper)) {
NukeDebuggerWrapper(wrapper);
return nullptr;
}
CrossCompartmentKey key(keyKind, object, untaggedReferent);
if (!object->compartment()->putWrapper(cx, key, ObjectValue(*wrapper))) {
NukeDebuggerWrapper(wrapper);
map.remove(untaggedReferent);
ReportOutOfMemory(cx);
return nullptr;
}
}
return p->value();
@@ -6300,7 +6321,7 @@ class SetDebuggerSourcePrivateMatcher
ReturnType match(Handle<WasmModuleObject*> module) { obj_->setPrivateGCThing(module); }
};
JSObject*
NativeObject*
Debugger::newDebuggerSource(JSContext* cx, Handle<DebuggerSourceReferent> referent)
{
assertSameCompartment(cx, object.get());
+4 -4
View File
@@ -690,10 +690,10 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
JSTrapStatus fireNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global, MutableHandleValue vp);
JSTrapStatus firePromiseHook(JSContext* cx, Hook hook, HandleObject promise, MutableHandleValue vp);
JSObject* newVariantWrapper(JSContext* cx, Handle<DebuggerScriptReferent> referent) {
NativeObject* newVariantWrapper(JSContext* cx, Handle<DebuggerScriptReferent> referent) {
return newDebuggerScript(cx, referent);
}
JSObject* newVariantWrapper(JSContext* cx, Handle<DebuggerSourceReferent> referent) {
NativeObject* newVariantWrapper(JSContext* cx, Handle<DebuggerSourceReferent> referent) {
return newDebuggerSource(cx, referent);
}
@@ -715,13 +715,13 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
* Allocate and initialize a Debugger.Script instance whose referent is
* |referent|.
*/
JSObject* newDebuggerScript(JSContext* cx, Handle<DebuggerScriptReferent> referent);
NativeObject* newDebuggerScript(JSContext* cx, Handle<DebuggerScriptReferent> referent);
/*
* Allocate and initialize a Debugger.Source instance whose referent is
* |referent|.
*/
JSObject* newDebuggerSource(JSContext* cx, Handle<DebuggerSourceReferent> referent);
NativeObject* newDebuggerSource(JSContext* cx, Handle<DebuggerSourceReferent> referent);
/*
* Receive a "new script" event from the engine. A new script was compiled
+21 -15
View File
@@ -509,25 +509,31 @@ UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group)
if (!nativeGroup)
return false;
// Propagate all property types from the old group to the new group.
if (layout.isArray()) {
if (!PropagatePropertyTypes(cx, JSID_VOID, group, nativeGroup))
return false;
} else {
for (size_t i = 0; i < layout.properties().length(); i++) {
const UnboxedLayout::Property& property = layout.properties()[i];
jsid id = NameToId(property.name);
if (!PropagatePropertyTypes(cx, id, group, nativeGroup))
// No sense propagating if we don't know what we started with.
if (!group->unknownProperties()) {
// Propagate all property types from the old group to the new group.
if (layout.isArray()) {
if (!PropagatePropertyTypes(cx, JSID_VOID, group, nativeGroup))
return false;
} else {
for (size_t i = 0; i < layout.properties().length(); i++) {
const UnboxedLayout::Property& property = layout.properties()[i];
jsid id = NameToId(property.name);
if (!PropagatePropertyTypes(cx, id, group, nativeGroup))
return false;
// If we are OOM we may not be able to propagate properties.
if (nativeGroup->unknownProperties())
break;
// If we are OOM we may not be able to propagate properties.
if (nativeGroup->unknownProperties())
break;
HeapTypeSet* nativeProperty = nativeGroup->maybeGetProperty(id);
if (nativeProperty && nativeProperty->canSetDefinite(i))
nativeProperty->setDefinite(i);
HeapTypeSet* nativeProperty = nativeGroup->maybeGetProperty(id);
if (nativeProperty && nativeProperty->canSetDefinite(i))
nativeProperty->setDefinite(i);
}
}
} else {
// If we skip, though, the new group had better agree.
MOZ_ASSERT(nativeGroup->unknownProperties());
}
layout.nativeGroup_ = nativeGroup;
+3 -2
View File
@@ -199,7 +199,7 @@ nsresult NrIceResolver::PendingResolution::OnLookupComplete(
ASSERT_ON_THREAD(thread_);
// First check if we've been canceled. This is single-threaded on the STS
// thread, but cancel() cannot guarantee this event isn't on the queue.
if (!canceled_) {
if (request_) {
nr_transport_addr *cb_addr = nullptr;
nr_transport_addr ta;
// TODO(jib@mozilla.com): Revisit when we do TURN.
@@ -212,6 +212,7 @@ nsresult NrIceResolver::PendingResolution::OnLookupComplete(
}
}
cb_(cb_arg_, cb_addr);
request_ = nullptr;
Release();
}
return NS_OK;
@@ -226,7 +227,7 @@ int NrIceResolver::cancel(void *obj, void *handle) {
int NrIceResolver::PendingResolution::cancel() {
request_->Cancel (NS_ERROR_ABORT);
canceled_ = true; // in case OnLookupComplete is already on event queue.
request_ = nullptr;
Release();
return 0;
}
+1 -3
View File
@@ -95,8 +95,7 @@ class NrIceResolver
thread_(thread),
port_(port),
transport_(transport),
cb_(cb), cb_arg_(cb_arg),
canceled_ (false) {}
cb_(cb), cb_arg_(cb_arg) {}
NS_IMETHOD OnLookupComplete(nsICancelable *request, nsIDNSRecord *record,
nsresult status) override;
int cancel();
@@ -110,7 +109,6 @@ class NrIceResolver
int transport_;
int (*cb_)(void *cb_arg, nr_transport_addr *addr);
void *cb_arg_;
bool canceled_;
};
nr_resolver_vtbl* vtbl_;
+18 -15
View File
@@ -386,8 +386,14 @@ int TestNrSocket::recvfrom(void *buf, size_t maxlen,
if (!r) {
PortMapping *port_mapping_used;
ingress_allowed = allow_ingress(*from, &port_mapping_used);
if (ingress_allowed && nat_->refresh_on_ingress_ && port_mapping_used) {
port_mapping_used->last_used_ = PR_IntervalNow();
if (ingress_allowed) {
r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s received from %s via %s",
internal_socket_->my_addr().as_string,
from->as_string,
port_mapping_used->external_socket_->my_addr().as_string);
if (nat_->refresh_on_ingress_) {
port_mapping_used->last_used_ = PR_IntervalNow();
}
}
}
} else {
@@ -402,9 +408,13 @@ int TestNrSocket::recvfrom(void *buf, size_t maxlen,
nat_->is_an_internal_tuple(*from));
if (!ingress_allowed) {
r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s denying ingress from %s: "
"Not behind the same NAT",
internal_socket_->my_addr().as_string,
from->as_string);
"Not behind the same NAT",
internal_socket_->my_addr().as_string,
from->as_string);
} else {
r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s received from %s",
internal_socket_->my_addr().as_string,
from->as_string);
}
}
}
@@ -425,12 +435,9 @@ int TestNrSocket::recvfrom(void *buf, size_t maxlen,
bool TestNrSocket::allow_ingress(const nr_transport_addr &from,
PortMapping **port_mapping_used) const {
*port_mapping_used = nullptr;
if (!nat_->enabled_)
return true;
if (nat_->is_an_internal_tuple(from))
return true;
// This is only called for traffic arriving at a port mapping
MOZ_ASSERT(nat_->enabled_);
MOZ_ASSERT(!nat_->is_an_internal_tuple(from));
*port_mapping_used = get_port_mapping(from, nat_->filtering_type_);
if (!(*port_mapping_used)) {
@@ -699,10 +706,6 @@ void TestNrSocket::on_socket_readable(NrSocketBase *real_socket) {
void TestNrSocket::fire_readable_callback() {
MOZ_ASSERT(poll_flags() & PR_POLL_READ);
// Stop listening on all real sockets; we will start listening again
// if the app starts listening to us again.
cancel_port_mapping_async_wait(NR_ASYNC_WAIT_READ);
internal_socket_->cancel(NR_ASYNC_WAIT_READ);
r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s ready for read",
internal_socket_->my_addr().as_string);
fire_callback(NR_ASYNC_WAIT_READ);
-36
View File
@@ -31,13 +31,7 @@
#undef LOG
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "OmxPlugin" , ## args)
#if !defined(MOZ_ANDROID_GB) && !defined(MOZ_ANDROID_HC)
#define MOZ_ANDROID_V4_OR_ABOVE
#endif
#if defined(MOZ_ANDROID_V4_OR_ABOVE)
#include <I420ColorConverter.h>
#endif
using namespace MPAPI;
@@ -200,11 +194,9 @@ OmxDecoder::~OmxDecoder()
mAudioSource->stop();
}
#ifndef MOZ_ANDROID_HC
if (mColorConverter) {
delete mColorConverter;
}
#endif
}
class AutoStopMediaSource {
@@ -268,14 +260,12 @@ static uint32_t GetVideoCreationFlags(PluginHost* aPluginHost)
int32_t flags = 0;
aPluginHost->GetIntPref("media.stagefright.omxcodec.flags", &flags);
if (flags != 0) {
#if !defined(MOZ_ANDROID_GB)
LOG("media.stagefright.omxcodec.flags=%d", flags);
if ((flags & OMXCodec::kHardwareCodecsOnly) != 0) {
LOG("FORCE HARDWARE DECODING");
} else if ((flags & OMXCodec::kSoftwareCodecsOnly) != 0) {
LOG("FORCE SOFTWARE DECODING");
}
#endif
}
flags |= GetDefaultStagefrightFlags(aPluginHost);
@@ -310,14 +300,11 @@ IsColorFormatSupported(OMX_COLOR_FORMATTYPE aColorFormat)
// These formats are okay if we can't find a better one; Android provides a
// software conversion to a sane colour format.
#if !defined(MOZ_ANDROID_HC)
if (ColorConverter(aColorFormat, OMX_COLOR_Format16bitRGB565).isValid()) {
LOG("Colour format %#x supported by Android ColorConverter.", aColorFormat);
return ColorFormatSupportOK;
}
#endif
#if defined(MOZ_ANDROID_V4_OR_ABOVE)
I420ColorConverter yuvConverter;
if (yuvConverter.isLoaded() &&
@@ -325,7 +312,6 @@ IsColorFormatSupported(OMX_COLOR_FORMATTYPE aColorFormat)
LOG("Colour format %#x supported by Android I420ColorConverter.", aColorFormat);
return ColorFormatSupportOK;
}
#endif
return ColorFormatNotSupported;
}
@@ -433,11 +419,7 @@ static sp<MediaSource> CreateVideoSource(PluginHost* aPluginHost,
// Throw away the videoSource and try again with new flags.
LOG("Falling back to software decoder");
videoSource.clear();
#if defined(MOZ_ANDROID_GB)
flags = DEFAULT_STAGEFRIGHT_FLAGS | OMXCodec::kPreferSoftwareCodecs;
#else
flags = DEFAULT_STAGEFRIGHT_FLAGS | OMXCodec::kSoftwareCodecsOnly;
#endif
}
MOZ_ASSERT(flags != DEFAULT_STAGEFRIGHT_FLAGS);
@@ -633,18 +615,14 @@ bool OmxDecoder::SetVideoFormat() {
}
// Gingerbread does not support the kKeyCropRect key
#if !defined(MOZ_ANDROID_GB)
if (!format->findRect(kKeyCropRect, &mVideoCropLeft, &mVideoCropTop,
&mVideoCropRight, &mVideoCropBottom)) {
#endif
mVideoCropLeft = 0;
mVideoCropTop = 0;
mVideoCropRight = mVideoStride - 1;
mVideoCropBottom = mVideoSliceHeight - 1;
LOG("crop rect not available, assuming no cropping");
#if !defined(MOZ_ANDROID_GB)
}
#endif
if (mVideoCropLeft < 0 || mVideoCropLeft >= mVideoCropRight || mVideoCropRight >= mVideoStride ||
mVideoCropTop < 0 || mVideoCropTop >= mVideoCropBottom || mVideoCropBottom >= mVideoSliceHeight) {
@@ -798,9 +776,6 @@ bool OmxDecoder::ToVideoFrame_RGB565(VideoFrame *aFrame, int64_t aTimeUs, void *
}
bool OmxDecoder::ToVideoFrame_ColorConverter(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame, BufferCallback *aBufferCallback) {
#ifdef MOZ_ANDROID_HC
return false;
#else
if (!mColorConverter) {
mColorConverter = new ColorConverter((OMX_COLOR_FORMATTYPE)mVideoColorFormat,
OMX_COLOR_Format16bitRGB565);
@@ -820,26 +795,18 @@ bool OmxDecoder::ToVideoFrame_ColorConverter(VideoFrame *aFrame, int64_t aTimeUs
aFrame->mSize = mVideoWidth * mVideoHeight * 2;
#if defined(MOZ_ANDROID_GB)
mColorConverter->convert(mVideoWidth, mVideoHeight,
aData, 0 /* srcSkip */,
buffer, mVideoWidth * 2);
#else
mColorConverter->convert(aData, mVideoStride, mVideoSliceHeight,
mVideoCropLeft, mVideoCropTop,
mVideoCropLeft + mVideoWidth - 1,
mVideoCropTop + mVideoHeight - 1,
buffer, mVideoWidth, mVideoHeight,
0, 0, mVideoWidth - 1, mVideoHeight - 1);
#endif
return true;
#endif
}
bool OmxDecoder::ToVideoFrame_I420ColorConverter(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame, BufferCallback *aBufferCallback)
{
#if defined(MOZ_ANDROID_V4_OR_ABOVE)
I420ColorConverter yuvConverter;
if (!yuvConverter.isLoaded()) {
@@ -866,9 +833,6 @@ bool OmxDecoder::ToVideoFrame_I420ColorConverter(VideoFrame *aFrame, int64_t aTi
}
return result == OK;
#else
return false;
#endif
}
bool OmxDecoder::ToVideoFrame(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame, BufferCallback *aBufferCallback) {
@@ -10291,5 +10291,15 @@
"expires_in_version": "55",
"kind": "boolean",
"description": "Result of call to SandboxBroker::Initialize"
},
"SYNC_WORKER_OPERATION": {
"alert_emails": ["amarchesini@mozilla.com", "khuey@mozilla.com" ],
"bug_numbers": [1267904],
"expires_in_version": "never",
"kind": "exponential",
"high": 5000,
"n_buckets": 20,
"keyed": true,
"description": "Tracking how long a Worker thread is blocked when a sync operation is executed on the main-thread."
}
}
-8
View File
@@ -66,15 +66,7 @@ if CONFIG['MOZ_OMX_PLUGIN']:
'/media/omx-plugin/lib/ics/libutils',
'/media/omx-plugin/lib/ics/libstagefright',
'/media/omx-plugin/lib/ics/libvideoeditorplayer',
'/media/omx-plugin/lib/gb/libutils',
'/media/omx-plugin/lib/gb/libstagefright',
'/media/omx-plugin/lib/gb/libstagefright_color_conversion',
'/media/omx-plugin/lib/gb235/libstagefright',
'/media/omx-plugin',
'/media/omx-plugin/gb',
'/media/omx-plugin/gb235',
'/media/omx-plugin/lib/hc/libstagefright',
'/media/omx-plugin/hc',
'/media/omx-plugin/kk',
]