mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
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:
+10
-22
@@ -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
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
{ }
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 ||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
})
|
||||
@@ -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();
|
||||
})
|
||||
`);
|
||||
@@ -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() {})()");
|
||||
});
|
||||
@@ -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
@@ -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*)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -15,6 +15,7 @@ template <typename Mutex>
|
||||
class MOZ_RAII LockGuard
|
||||
{
|
||||
friend class UnlockGuard<Mutex>;
|
||||
friend class ConditionVariable;
|
||||
Mutex& lock;
|
||||
|
||||
public:
|
||||
|
||||
@@ -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_);
|
||||
}
|
||||
@@ -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
@@ -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());
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user