From 4751a4e7325d1bc5ffbcd11e0182e27d4baeb98c Mon Sep 17 00:00:00 2001 From: roytam1 Date: Fri, 30 Aug 2024 21:17:13 +0800 Subject: [PATCH] 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) --- dom/base/File.cpp | 32 +- dom/base/WebSocket.cpp | 17 +- dom/broadcastchannel/BroadcastChannel.cpp | 3 +- dom/canvas/ImageBitmap.cpp | 6 +- dom/fetch/Fetch.cpp | 3 +- dom/fetch/Request.cpp | 3 +- dom/network/NetworkStatsManager.js | 4 +- dom/network/TCPSocket.cpp | 47 +- dom/network/TCPSocket.h | 2 + .../test_tcpsocket_enabled_with_perm.html | 3 + dom/network/tests/test_tcpsocket_legacy.html | 11 +- dom/notification/Notification.cpp | 9 +- .../base/nsPluginStreamListenerPeer.cpp | 121 +++-- dom/plugins/base/nsPluginTags.cpp | 1 + dom/plugins/base/nsPluginsDirWin.cpp | 2 +- dom/workers/ServiceWorkerRegistration.cpp | 3 +- dom/workers/URL.cpp | 22 +- dom/workers/WorkerNavigator.cpp | 3 +- dom/workers/WorkerRunnable.cpp | 32 +- dom/workers/WorkerRunnable.h | 64 +-- dom/xul/XULDocument.cpp | 5 + dom/xul/nsXULPrototypeCache.cpp | 2 +- gfx/thebes/gfxUtils.cpp | 3 +- js/src/jit-test/tests/debug/bug1251919.js | 13 + js/src/jit-test/tests/debug/bug1254123.js | 17 + js/src/jit-test/tests/debug/bug1254190.js | 15 + js/src/jsapi-tests/moz.build | 1 + .../testThreadingConditionVariable.cpp | 230 ++++++++++ js/src/jsexn.cpp | 8 +- js/src/jsnum.h | 13 + js/src/moz.build | 4 + .../ArrayBuffer/constructorTypeCheck.js | 20 + .../DataView/detach-after-construction.js | 2 +- js/src/threading/ConditionVariable.h | 95 ++++ js/src/threading/LockGuard.h | 1 + js/src/threading/Mutex.h | 1 + js/src/threading/posix/ConditionVariable.cpp | 174 ++++++++ .../threading/windows/ConditionVariable.cpp | 416 ++++++++++++++++++ js/src/vm/ArrayBufferObject.cpp | 28 +- js/src/vm/Debugger.cpp | 35 +- js/src/vm/Debugger.h | 8 +- js/src/vm/UnboxedObject.cpp | 36 +- media/mtransport/nriceresolver.cpp | 5 +- media/mtransport/nriceresolver.h | 4 +- media/mtransport/test_nr_socket.cpp | 33 +- media/omx-plugin/OmxPlugin.cpp | 36 -- toolkit/components/telemetry/Histograms.json | 10 + toolkit/toolkit.mozbuild | 8 - 48 files changed, 1365 insertions(+), 246 deletions(-) create mode 100644 js/src/jit-test/tests/debug/bug1251919.js create mode 100644 js/src/jit-test/tests/debug/bug1254123.js create mode 100644 js/src/jit-test/tests/debug/bug1254190.js create mode 100644 js/src/jsapi-tests/testThreadingConditionVariable.cpp create mode 100644 js/src/tests/ecma_6/ArrayBuffer/constructorTypeCheck.js create mode 100644 js/src/threading/ConditionVariable.h create mode 100644 js/src/threading/posix/ConditionVariable.cpp create mode 100644 js/src/threading/windows/ConditionVariable.cpp diff --git a/dom/base/File.cpp b/dom/base/File.cpp index ad9d2a1798..69e9a6339d 100644 --- a/dom/base/File.cpp +++ b/dom/base/File.cpp @@ -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 runnable = - new MainThreadStopSyncLoopRunnable(mWorkerPrivate, - mSyncLoopTarget.forget(), true); - NS_WARN_IF(!runnable->Dispatch()); - return NS_OK; + return true; } private: ~GetTypeRunnable() {} - WorkerPrivate* mWorkerPrivate; - nsCOMPtr mSyncLoopTarget; RefPtr mBlobImpl; }; @@ -925,14 +915,12 @@ BlobImplFile::GetType(nsAString& aType) return; } - AutoSyncLoopHolder syncLoop(workerPrivate); - RefPtr 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; } diff --git a/dom/base/WebSocket.cpp b/dom/base/WebSocket.cpp index 6f41912a50..3dca8ffe9c 100644 --- a/dom/base/WebSocket.cpp +++ b/dom/base/WebSocket.cpp @@ -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) { diff --git a/dom/broadcastchannel/BroadcastChannel.cpp b/dom/broadcastchannel/BroadcastChannel.cpp index 18688b9244..de79d60d7b 100644 --- a/dom/broadcastchannel/BroadcastChannel.cpp +++ b/dom/broadcastchannel/BroadcastChannel.cpp @@ -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) diff --git a/dom/canvas/ImageBitmap.cpp b/dom/canvas/ImageBitmap.cpp index 1aef404eb8..153034cb26 100644 --- a/dom/canvas/ImageBitmap.cpp +++ b/dom/canvas/ImageBitmap.cpp @@ -269,7 +269,8 @@ public: const gfx::IntSize& aSize, const Maybe& 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& aCropRect, layers::Image** aImage) - : WorkerMainThreadRunnable(aWorkerPrivate) + : WorkerMainThreadRunnable(aWorkerPrivate, + NS_LITERAL_CSTRING("ImageBitmap :: Create Image from Blob")) , mBlob(aBlob) , mCropRect(aCropRect) , mImage(aImage) diff --git a/dom/fetch/Fetch.cpp b/dom/fetch/Fetch.cpp index 76c08f3cde..a9c7d940b3 100644 --- a/dom/fetch/Fetch.cpp +++ b/dom/fetch/Fetch.cpp @@ -718,7 +718,8 @@ class CancelPumpRunnable final : public WorkerMainThreadRunnable FetchBody* mBody; public: explicit CancelPumpRunnable(FetchBody* aBody) - : WorkerMainThreadRunnable(aBody->mWorkerPrivate) + : WorkerMainThreadRunnable(aBody->mWorkerPrivate, + NS_LITERAL_CSTRING("Fetch :: Cancel Pump")) , mBody(aBody) { } diff --git a/dom/fetch/Request.cpp b/dom/fetch/Request.cpp index 6ff2b99526..5ab56ce3d4 100644 --- a/dom/fetch/Request.cpp +++ b/dom/fetch/Request.cpp @@ -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) { diff --git a/dom/network/NetworkStatsManager.js b/dom/network/NetworkStatsManager.js index 1c3d5e3791..b963aba2b5 100644 --- a/dom/network/NetworkStatsManager.js +++ b/dom/network/NetworkStatsManager.js @@ -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"); diff --git a/dom/network/TCPSocket.cpp b/dom/network/TCPSocket.cpp index d2637f2dcb..f59cf6b6aa 100644 --- a/dom/network/TCPSocket.cpp +++ b/dom/network/TCPSocket.cpp @@ -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 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 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 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; diff --git a/dom/network/TCPSocket.h b/dom/network/TCPSocket.h index 64faebb42f..d1e828753a 100644 --- a/dom/network/TCPSocket.h +++ b/dom/network/TCPSocket.h @@ -239,6 +239,8 @@ private: // The buffered data awaiting the TLS upgrade to finish. nsTArray> mPendingDataAfterStartTLS; + bool mObserversActive; + #ifdef MOZ_WIDGET_GONK // Number of bytes sent. uint32_t mTxBytes; diff --git a/dom/network/tests/test_tcpsocket_enabled_with_perm.html b/dom/network/tests/test_tcpsocket_enabled_with_perm.html index 1660a0eafe..65dcfe339c 100644 --- a/dom/network/tests/test_tcpsocket_enabled_with_perm.html +++ b/dom/network/tests/test_tcpsocket_enabled_with_perm.html @@ -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(); } diff --git a/dom/network/tests/test_tcpsocket_legacy.html b/dom/network/tests/test_tcpsocket_legacy.html index 9efcf72a4c..a75cbe3d79 100644 --- a/dom/network/tests/test_tcpsocket_legacy.html +++ b/dom/network/tests/test_tcpsocket_legacy.html @@ -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); } } diff --git a/dom/notification/Notification.cpp b/dom/notification/Notification.cpp index 04fd542fc7..563e7b6f0e 100644 --- a/dom/notification/Notification.cpp +++ b/dom/notification/Notification.cpp @@ -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) { } diff --git a/dom/plugins/base/nsPluginStreamListenerPeer.cpp b/dom/plugins/base/nsPluginStreamListenerPeer.cpp index 30393a9c98..52ca4142b6 100644 --- a/dom/plugins/base/nsPluginStreamListenerPeer.cpp +++ b/dom/plugins/base/nsPluginStreamListenerPeer.cpp @@ -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 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 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 mListener; + nsCOMPtr 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 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 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 = + 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 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 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; } diff --git a/dom/plugins/base/nsPluginTags.cpp b/dom/plugins/base/nsPluginTags.cpp index d9e9bfd02b..1d5d159890 100644 --- a/dom/plugins/base/nsPluginTags.cpp +++ b/dom/plugins/base/nsPluginTags.cpp @@ -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), diff --git a/dom/plugins/base/nsPluginsDirWin.cpp b/dom/plugins/base/nsPluginsDirWin.cpp index 8d3c0b76ea..eb771af5cf 100644 --- a/dom/plugins/base/nsPluginsDirWin.cpp +++ b/dom/plugins/base/nsPluginsDirWin.cpp @@ -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; diff --git a/dom/workers/ServiceWorkerRegistration.cpp b/dom/workers/ServiceWorkerRegistration.cpp index f87e63d10c..8a8e6671eb 100644 --- a/dom/workers/ServiceWorkerRegistration.cpp +++ b/dom/workers/ServiceWorkerRegistration.cpp @@ -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) {} diff --git a/dom/workers/URL.cpp b/dom/workers/URL.cpp index 13f2f481ed..8c2b05f206 100644 --- a/dom/workers/URL.cpp +++ b/dom/workers/URL.cpp @@ -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& 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) diff --git a/dom/workers/WorkerNavigator.cpp b/dom/workers/WorkerNavigator.cpp index ef84e10d2b..5fa81b9c8f 100644 --- a/dom/workers/WorkerNavigator.cpp +++ b/dom/workers/WorkerNavigator.cpp @@ -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); diff --git a/dom/workers/WorkerRunnable.cpp b/dom/workers/WorkerRunnable.cpp index 8a00e62eeb..97f0e76614 100644 --- a/dom/workers/WorkerRunnable.cpp +++ b/dom/workers/WorkerRunnable.cpp @@ -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&& 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 runnable(this); + RefPtr 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((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() { diff --git a/dom/workers/WorkerRunnable.h b/dom/workers/WorkerRunnable.h index 412aeb4b2a..17699cc42f 100644 --- a/dom/workers/WorkerRunnable.h +++ b/dom/workers/WorkerRunnable.h @@ -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&& aSyncLoopTarget, - bool aResult); + MainThreadStopSyncLoopRunnable( + WorkerPrivate* aWorkerPrivate, + already_AddRefed&& 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&& 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 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. diff --git a/dom/xul/XULDocument.cpp b/dom/xul/XULDocument.cpp index 839ee6acae..80e4e603ce 100644 --- a/dom/xul/XULDocument.cpp +++ b/dom/xul/XULDocument.cpp @@ -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 || diff --git a/dom/xul/nsXULPrototypeCache.cpp b/dom/xul/nsXULPrototypeCache.cpp index 20522a044d..36438be092 100644 --- a/dom/xul/nsXULPrototypeCache.cpp +++ b/dom/xul/nsXULPrototypeCache.cpp @@ -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); diff --git a/gfx/thebes/gfxUtils.cpp b/gfx/thebes/gfxUtils.cpp index d7c2670f38..5f17220ef9 100644 --- a/gfx/thebes/gfxUtils.cpp +++ b/gfx/thebes/gfxUtils.cpp @@ -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) diff --git a/js/src/jit-test/tests/debug/bug1251919.js b/js/src/jit-test/tests/debug/bug1251919.js new file mode 100644 index 0000000000..188bfa3932 --- /dev/null +++ b/js/src/jit-test/tests/debug/bug1251919.js @@ -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(); +}) diff --git a/js/src/jit-test/tests/debug/bug1254123.js b/js/src/jit-test/tests/debug/bug1254123.js new file mode 100644 index 0000000000..51b2c8940d --- /dev/null +++ b/js/src/jit-test/tests/debug/bug1254123.js @@ -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(); +}) +`); diff --git a/js/src/jit-test/tests/debug/bug1254190.js b/js/src/jit-test/tests/debug/bug1254190.js new file mode 100644 index 0000000000..d4bd11758d --- /dev/null +++ b/js/src/jit-test/tests/debug/bug1254190.js @@ -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() {})()"); +}); diff --git a/js/src/jsapi-tests/moz.build b/js/src/jsapi-tests/moz.build index 66da9230ab..22979d9d03 100644 --- a/js/src/jsapi-tests/moz.build +++ b/js/src/jsapi-tests/moz.build @@ -84,6 +84,7 @@ UNIFIED_SOURCES += [ 'testStringBuffer.cpp', 'testStructuredClone.cpp', 'testSymbol.cpp', + 'testThreadingConditionVariable.cpp', 'testThreadingExclusiveData.cpp', 'testThreadingMutex.cpp', 'testToIntWidth.cpp', diff --git a/js/src/jsapi-tests/testThreadingConditionVariable.cpp b/js/src/jsapi-tests/testThreadingConditionVariable.cpp new file mode 100644 index 0000000000..34b599be34 --- /dev/null +++ b/js/src/jsapi-tests/testThreadingConditionVariable.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(arg); + js::UniqueLock 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(); + { + js::UniqueLock 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(); + { + js::UniqueLock 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(); + { + js::UniqueLock 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(false); + { + js::UniqueLock 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 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(); + { + js::UniqueLock 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(false); + { + js::UniqueLock 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(); + { + js::UniqueLock 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(false); + { + js::UniqueLock 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 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(); + { + js::UniqueLock 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(false); + { + js::UniqueLock 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) diff --git a/js/src/jsexn.cpp b/js/src/jsexn.cpp index a8b8af2677..b8b42382f3 100644 --- a/js/src/jsexn.cpp +++ b/js/src/jsexn.cpp @@ -305,7 +305,13 @@ js::ErrorFromException(JSContext* cx, HandleObject objArg) if (!obj->is()) return nullptr; - return obj->as().getOrCreateErrorReport(cx); + JSErrorReport* report = obj->as().getOrCreateErrorReport(cx); + if (!report) { + MOZ_ASSERT(cx->isThrowingOutOfMemory()); + cx->recoverFromOutOfMemory(); + } + + return report; } JS_PUBLIC_API(JSObject*) diff --git a/js/src/jsnum.h b/js/src/jsnum.h index a6e078c192..710d0f289c 100644 --- a/js/src/jsnum.h +++ b/js/src/jsnum.h @@ -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. diff --git a/js/src/moz.build b/js/src/moz.build index 164e50175c..2a88050d1d 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -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'] diff --git a/js/src/tests/ecma_6/ArrayBuffer/constructorTypeCheck.js b/js/src/tests/ecma_6/ArrayBuffer/constructorTypeCheck.js new file mode 100644 index 0000000000..10cccb4413 --- /dev/null +++ b/js/src/tests/ecma_6/ArrayBuffer/constructorTypeCheck.js @@ -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"); diff --git a/js/src/tests/ecma_6/DataView/detach-after-construction.js b/js/src/tests/ecma_6/DataView/detach-after-construction.js index a2c66a5808..d626281485 100644 --- a/js/src/tests/ecma_6/DataView/detach-after-construction.js +++ b/js/src/tests/ecma_6/DataView/detach-after-construction.js @@ -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); diff --git a/js/src/threading/ConditionVariable.h b/js/src/threading/ConditionVariable.h new file mode 100644 index 0000000000..8ffd88ac72 --- /dev/null +++ b/js/src/threading/ConditionVariable.h @@ -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 +#ifndef XP_WIN +# include +#endif + +#include "threading/LockGuard.h" +#include "threading/Mutex.h" + +namespace js { + +template using UniqueLock = LockGuard; + +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& lock); + + template + void wait(UniqueLock& lock, Predicate pred) { + while (!pred()) { + wait(lock); + } + } + + CVStatus wait_until(UniqueLock& lock, + const mozilla::TimeStamp& abs_time); + + template + bool wait_until(UniqueLock& 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& lock, + const mozilla::TimeDuration& rel_time); + + template + bool wait_for(UniqueLock& 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 diff --git a/js/src/threading/LockGuard.h b/js/src/threading/LockGuard.h index 34ebfbccf0..45b023a90a 100644 --- a/js/src/threading/LockGuard.h +++ b/js/src/threading/LockGuard.h @@ -15,6 +15,7 @@ template class MOZ_RAII LockGuard { friend class UnlockGuard; + friend class ConditionVariable; Mutex& lock; public: diff --git a/js/src/threading/Mutex.h b/js/src/threading/Mutex.h index 8db25c060a..6cb4e0ea04 100644 --- a/js/src/threading/Mutex.h +++ b/js/src/threading/Mutex.h @@ -44,6 +44,7 @@ private: Mutex(const Mutex&) = delete; void operator=(const Mutex&) = delete; + friend class ConditionVariable; PlatformData* platformData() { MOZ_ASSERT(platformData_); return platformData_; diff --git a/js/src/threading/posix/ConditionVariable.cpp b/js/src/threading/posix/ConditionVariable.cpp new file mode 100644 index 0000000000..6a2e4a2467 --- /dev/null +++ b/js/src/threading/posix/ConditionVariable.cpp @@ -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 +#include +#include +#include +#include + +#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 sec = CheckedInt(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& 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& lock, + const TimeStamp& abs_time) +{ + return wait_for(lock, abs_time - TimeStamp::Now()); +} + +js::CVStatus +js::ConditionVariable::wait_for(UniqueLock& 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(rel_time.ToSeconds()); + rel_ts.tv_nsec = static_cast(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_); +} diff --git a/js/src/threading/windows/ConditionVariable.cpp b/js/src/threading/windows/ConditionVariable.cpp new file mode 100644 index 0000000000..2f62368ae8 --- /dev/null +++ b/js/src/threading/windows/ConditionVariable.cpp @@ -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 +#include +#include +#include + +#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 + inline bool loadSymbol(HMODULE module, const char* name, T& fn) { + void* ptr = GetProcAddress(module, name); + if (!ptr) + return false; + + fn = reinterpret_cast(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& 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& lock, + const mozilla::TimeStamp& abs_time) +{ + return wait_for(lock, abs_time - mozilla::TimeStamp::Now()); +} + +js::CVStatus +js::ConditionVariable::wait_for(UniqueLock& 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(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_); +} diff --git a/js/src/vm/ArrayBufferObject.cpp b/js/src/vm/ArrayBufferObject.cpp index b2b66e78d2..4a4d7f66b6 100644 --- a/js/src/vm/ArrayBufferObject.cpp +++ b/js/src/vm/ArrayBufferObject.cpp @@ -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); diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 1ca2cc9fdb..57d62caf18 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -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, 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 module) { obj_->setPrivateGCThing(module); } }; -JSObject* +NativeObject* Debugger::newDebuggerScript(JSContext* cx, Handle referent) { assertSameCompartment(cx, object.get()); @@ -5058,19 +5075,23 @@ Debugger::wrapVariantReferent(JSContext* cx, Map& map, CrossCompartmentKey::Kind DependentAddPtr 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 module) { obj_->setPrivateGCThing(module); } }; -JSObject* +NativeObject* Debugger::newDebuggerSource(JSContext* cx, Handle referent) { assertSameCompartment(cx, object.get()); diff --git a/js/src/vm/Debugger.h b/js/src/vm/Debugger.h index e833e084bb..bcc5e6742b 100644 --- a/js/src/vm/Debugger.h +++ b/js/src/vm/Debugger.h @@ -690,10 +690,10 @@ class Debugger : private mozilla::LinkedListElement JSTrapStatus fireNewGlobalObject(JSContext* cx, Handle global, MutableHandleValue vp); JSTrapStatus firePromiseHook(JSContext* cx, Hook hook, HandleObject promise, MutableHandleValue vp); - JSObject* newVariantWrapper(JSContext* cx, Handle referent) { + NativeObject* newVariantWrapper(JSContext* cx, Handle referent) { return newDebuggerScript(cx, referent); } - JSObject* newVariantWrapper(JSContext* cx, Handle referent) { + NativeObject* newVariantWrapper(JSContext* cx, Handle referent) { return newDebuggerSource(cx, referent); } @@ -715,13 +715,13 @@ class Debugger : private mozilla::LinkedListElement * Allocate and initialize a Debugger.Script instance whose referent is * |referent|. */ - JSObject* newDebuggerScript(JSContext* cx, Handle referent); + NativeObject* newDebuggerScript(JSContext* cx, Handle referent); /* * Allocate and initialize a Debugger.Source instance whose referent is * |referent|. */ - JSObject* newDebuggerSource(JSContext* cx, Handle referent); + NativeObject* newDebuggerSource(JSContext* cx, Handle referent); /* * Receive a "new script" event from the engine. A new script was compiled diff --git a/js/src/vm/UnboxedObject.cpp b/js/src/vm/UnboxedObject.cpp index 374869aedc..bf6e492589 100644 --- a/js/src/vm/UnboxedObject.cpp +++ b/js/src/vm/UnboxedObject.cpp @@ -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; diff --git a/media/mtransport/nriceresolver.cpp b/media/mtransport/nriceresolver.cpp index 1ef619112f..2298d6fafc 100644 --- a/media/mtransport/nriceresolver.cpp +++ b/media/mtransport/nriceresolver.cpp @@ -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; } diff --git a/media/mtransport/nriceresolver.h b/media/mtransport/nriceresolver.h index c3a379e716..612c860ae2 100644 --- a/media/mtransport/nriceresolver.h +++ b/media/mtransport/nriceresolver.h @@ -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_; diff --git a/media/mtransport/test_nr_socket.cpp b/media/mtransport/test_nr_socket.cpp index 79d85f0ec5..ea61787c9a 100644 --- a/media/mtransport/test_nr_socket.cpp +++ b/media/mtransport/test_nr_socket.cpp @@ -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); diff --git a/media/omx-plugin/OmxPlugin.cpp b/media/omx-plugin/OmxPlugin.cpp index 4dae18bc02..4b6641c46e 100644 --- a/media/omx-plugin/OmxPlugin.cpp +++ b/media/omx-plugin/OmxPlugin.cpp @@ -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 -#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 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) { diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index d5c90d4077..28e83a6f65 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -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." } } diff --git a/toolkit/toolkit.mozbuild b/toolkit/toolkit.mozbuild index 71e8058d3a..0fb0980965 100644 --- a/toolkit/toolkit.mozbuild +++ b/toolkit/toolkit.mozbuild @@ -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', ]