Bug 1398556 - IPCBlobInputStream should call OnInputStreamReady() on the current thread if ::AsyncWait() is called without passing nsIEventTarget, r=smaug

This commit is contained in:
Andrea Marchesini 2017-09-13 15:29:39 +02:00
Родитель 6775e78ff5
Коммит e4901e78cc
3 изменённых файлов: 38 добавлений и 10 удалений

Просмотреть файл

@ -25,20 +25,24 @@ static NS_DEFINE_CID(kStreamTransportServiceCID, NS_STREAMTRANSPORTSERVICE_CID);
class InputStreamCallbackRunnable final : public CancelableRunnable
{
public:
// Note that the execution can be synchronous in case the event target is
// null.
static void
Execute(nsIInputStreamCallback* aCallback,
nsIEventTarget* aEventTarget,
IPCBlobInputStream* aStream)
{
MOZ_ASSERT(aCallback);
RefPtr<InputStreamCallbackRunnable> runnable =
new InputStreamCallbackRunnable(aCallback, aStream);
nsCOMPtr<nsIEventTarget> target = aEventTarget;
if (!target) {
target = NS_GetCurrentThread();
if (aEventTarget) {
target->Dispatch(runnable, NS_DISPATCH_NORMAL);
} else {
runnable->Run();
}
target->Dispatch(runnable, NS_DISPATCH_NORMAL);
}
NS_IMETHOD
@ -73,14 +77,13 @@ public:
nsIEventTarget* aEventTarget,
IPCBlobInputStream* aStream)
{
MOZ_ASSERT(aCallback);
MOZ_ASSERT(aEventTarget);
RefPtr<FileMetadataCallbackRunnable> runnable =
new FileMetadataCallbackRunnable(aCallback, aStream);
nsCOMPtr<nsIEventTarget> target = aEventTarget;
if (!target) {
target = NS_GetCurrentThread();
}
target->Dispatch(runnable, NS_DISPATCH_NORMAL);
}
@ -509,6 +512,8 @@ IPCBlobInputStream::OnInputStreamReady(nsIAsyncInputStream* aStream)
nsCOMPtr<nsIEventTarget> callbackEventTarget;
callbackEventTarget.swap(mInputStreamCallbackEventTarget);
// This must be the last operation because the execution of the callback can
// be synchronous.
InputStreamCallbackRunnable::Execute(callback, callbackEventTarget, this);
return NS_OK;
}
@ -547,6 +552,13 @@ NS_IMETHODIMP
IPCBlobInputStream::AsyncWait(nsIFileMetadataCallback* aCallback,
nsIEventTarget* aEventTarget)
{
MOZ_ASSERT(!!aCallback == !!aEventTarget);
// If we have the callback, we must have the event target.
if (NS_WARN_IF(!!aCallback != !!aEventTarget)) {
return NS_ERROR_FAILURE;
}
// See IPCBlobInputStream.h for more information about this state machine.
switch (mState) {

Просмотреть файл

@ -277,7 +277,7 @@ IPCBlobInputStreamChild::StreamNeeded(IPCBlobInputStream* aStream,
PendingOperation* opt = mPendingOperations.AppendElement();
opt->mStream = aStream;
opt->mEventTarget = aEventTarget ? aEventTarget : NS_GetCurrentThread();
opt->mEventTarget = aEventTarget;
if (mState == eActiveMigrating || mState == eInactiveMigrating) {
// This operation will be continued when the migration is completed.
@ -316,7 +316,16 @@ IPCBlobInputStreamChild::RecvStreamReady(const OptionalIPCStream& aStream)
RefPtr<StreamReadyRunnable> runnable =
new StreamReadyRunnable(pendingStream, stream);
eventTarget->Dispatch(runnable, NS_DISPATCH_NORMAL);
// If IPCBlobInputStream::AsyncWait() has been executed without passing an
// event target, we run the callback synchronous because any thread could be
// result to be the wrong one. See more in nsIAsyncInputStream::asyncWait
// documentation.
if (eventTarget) {
eventTarget->Dispatch(runnable, NS_DISPATCH_NORMAL);
} else {
runnable->Run();
}
return IPC_OK();
}

Просмотреть файл

@ -222,6 +222,13 @@ interface nsIAsyncFileMetadata : nsIFileMetadata
{
/**
* Asynchronously wait for the object to be ready.
*
* @param aCallback The callback will be used when the stream is ready to
* return File metadata. Use a nullptr to cancel a
* previous operation.
*
* @param aEventTarget The event target where aCallback will be executed.
* If aCallback is passed, aEventTarget cannot be null.
*/
void asyncWait(in nsIFileMetadataCallback aCallback,
in nsIEventTarget aEventTarget);