/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- * 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 "ImageBridgeChild.h" #include <vector> // for vector #include "ImageBridgeParent.h" // for ImageBridgeParent #include "ImageContainer.h" // for ImageContainer #include "Layers.h" // for Layer, etc #include "ShadowLayers.h" // for ShadowLayerForwarder #include "base/message_loop.h" // for MessageLoop #include "base/platform_thread.h" // for PlatformThread #include "base/process.h" // for ProcessId #include "base/task.h" // for NewRunnableFunction, etc #include "base/thread.h" // for Thread #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc #include "mozilla/Monitor.h" // for Monitor, MonitorAutoLock #include "mozilla/ReentrantMonitor.h" // for ReentrantMonitor, etc #include "mozilla/ipc/MessageChannel.h" // for MessageChannel, etc #include "mozilla/ipc/Transport.h" // for Transport #include "mozilla/gfx/Point.h" // for IntSize #include "mozilla/layers/AsyncCanvasRenderer.h" #include "mozilla/media/MediaSystemResourceManager.h" // for MediaSystemResourceManager #include "mozilla/media/MediaSystemResourceManagerChild.h" // for MediaSystemResourceManagerChild #include "mozilla/layers/CompositableChild.h" #include "mozilla/layers/CompositableClient.h" // for CompositableChild, etc #include "mozilla/layers/CompositorThread.h" #include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator #include "mozilla/layers/ImageClient.h" // for ImageClient #include "mozilla/layers/ImageContainerChild.h" #include "mozilla/layers/LayersMessages.h" // for CompositableOperation #include "mozilla/layers/PCompositableChild.h" // for PCompositableChild #include "mozilla/layers/TextureClient.h" // for TextureClient #include "mozilla/dom/ContentChild.h" #include "mozilla/mozalloc.h" // for operator new, etc #include "mtransport/runnable_utils.h" #include "nsContentUtils.h" #include "nsISupportsImpl.h" // for ImageContainer::AddRef, etc #include "nsTArray.h" // for AutoTArray, nsTArray, etc #include "nsTArrayForwardDeclare.h" // for AutoTArray #include "nsThreadUtils.h" // for NS_IsMainThread #include "nsXULAppAPI.h" // for XRE_GetIOMessageLoop #include "mozilla/StaticMutex.h" #include "mozilla/StaticPtr.h" // for StaticRefPtr #include "mozilla/layers/TextureClient.h" #include "SynchronousTask.h" namespace mozilla { namespace ipc { class Shmem; } // namespace ipc namespace layers { using base::Thread; using base::ProcessId; using namespace mozilla::ipc; using namespace mozilla::gfx; using namespace mozilla::media; typedef std::vector<CompositableOperation> OpVector; typedef nsTArray<OpDestroy> OpDestroyVector; namespace { class ImageBridgeThread : public Thread { public: ImageBridgeThread() : Thread("ImageBridgeChild") { } protected: MOZ_IS_CLASS_INIT void Init() { } void CleanUp() { } }; } struct CompositableTransaction { CompositableTransaction() : mSwapRequired(false) , mFinished(true) {} ~CompositableTransaction() { End(); } bool Finished() const { return mFinished; } void Begin() { MOZ_ASSERT(mFinished); mFinished = false; } void End() { mFinished = true; mSwapRequired = false; mOperations.clear(); mDestroyedActors.Clear(); } bool IsEmpty() const { return mOperations.empty() && mDestroyedActors.IsEmpty(); } void AddNoSwapEdit(const CompositableOperation& op) { MOZ_ASSERT(!Finished(), "forgot BeginTransaction?"); mOperations.push_back(op); } void AddEdit(const CompositableOperation& op) { AddNoSwapEdit(op); MarkSyncTransaction(); } void MarkSyncTransaction() { mSwapRequired = true; } OpVector mOperations; OpDestroyVector mDestroyedActors; bool mSwapRequired; bool mFinished; }; struct AutoEndTransaction { explicit AutoEndTransaction(CompositableTransaction* aTxn) : mTxn(aTxn) {} ~AutoEndTransaction() { mTxn->End(); } CompositableTransaction* mTxn; }; void ImageBridgeChild::UseTextures(CompositableClient* aCompositable, const nsTArray<TimedTextureClient>& aTextures) { MOZ_ASSERT(aCompositable); MOZ_ASSERT(aCompositable->GetIPDLActor()); MOZ_ASSERT(aCompositable->IsConnected()); AutoTArray<TimedTexture,4> textures; for (auto& t : aTextures) { MOZ_ASSERT(t.mTextureClient); MOZ_ASSERT(t.mTextureClient->GetIPDLActor()); if (!t.mTextureClient->IsSharedWithCompositor()) { return; } ReadLockDescriptor readLock; t.mTextureClient->SerializeReadLock(readLock); textures.AppendElement(TimedTexture(nullptr, t.mTextureClient->GetIPDLActor(), readLock, t.mTimeStamp, t.mPictureRect, t.mFrameID, t.mProducerID)); // Wait end of usage on host side if TextureFlags::RECYCLE is set or GrallocTextureData case HoldUntilCompositableRefReleasedIfNecessary(t.mTextureClient); } mTxn->AddNoSwapEdit(CompositableOperation(nullptr, aCompositable->GetIPDLActor(), OpUseTexture(textures))); } void ImageBridgeChild::UseComponentAlphaTextures(CompositableClient* aCompositable, TextureClient* aTextureOnBlack, TextureClient* aTextureOnWhite) { MOZ_ASSERT(aCompositable); MOZ_ASSERT(aTextureOnWhite); MOZ_ASSERT(aTextureOnBlack); MOZ_ASSERT(aCompositable->IsConnected()); MOZ_ASSERT(aTextureOnWhite->GetIPDLActor()); MOZ_ASSERT(aTextureOnBlack->GetIPDLActor()); MOZ_ASSERT(aTextureOnBlack->GetSize() == aTextureOnWhite->GetSize()); ReadLockDescriptor readLockW; ReadLockDescriptor readLockB; aTextureOnBlack->SerializeReadLock(readLockB); aTextureOnWhite->SerializeReadLock(readLockW); HoldUntilCompositableRefReleasedIfNecessary(aTextureOnBlack); HoldUntilCompositableRefReleasedIfNecessary(aTextureOnWhite); mTxn->AddNoSwapEdit( CompositableOperation( nullptr, aCompositable->GetIPDLActor(), OpUseComponentAlphaTextures( nullptr, aTextureOnBlack->GetIPDLActor(), nullptr, aTextureOnWhite->GetIPDLActor(), readLockB, readLockW ) ) ); } void ImageBridgeChild::HoldUntilCompositableRefReleasedIfNecessary(TextureClient* aClient) { // Wait ReleaseCompositableRef only when TextureFlags::RECYCLE is set on ImageBridge. if (!aClient || !(aClient->GetFlags() & TextureFlags::RECYCLE)) { return; } aClient->SetLastFwdTransactionId(GetFwdTransactionId()); mTexturesWaitingRecycled.Put(aClient->GetSerial(), aClient); } void ImageBridgeChild::NotifyNotUsed(uint64_t aTextureId, uint64_t aFwdTransactionId) { RefPtr<TextureClient> client = mTexturesWaitingRecycled.Get(aTextureId); if (!client) { return; } if (aFwdTransactionId < client->GetLastFwdTransactionId()) { // Released on host side, but client already requested newer use texture. return; } mTexturesWaitingRecycled.Remove(aTextureId); } void ImageBridgeChild::CancelWaitForRecycle(uint64_t aTextureId) { MOZ_ASSERT(InImageBridgeChildThread()); RefPtr<TextureClient> client = mTexturesWaitingRecycled.Get(aTextureId); if (!client) { return; } mTexturesWaitingRecycled.Remove(aTextureId); } // Singleton static StaticMutex sImageBridgeSingletonLock; static StaticRefPtr<ImageBridgeChild> sImageBridgeChildSingleton; static Thread *sImageBridgeChildThread = nullptr; // dispatched function void ImageBridgeChild::ShutdownStep1(SynchronousTask* aTask) { AutoCompleteTask complete(aTask); MOZ_ASSERT(InImageBridgeChildThread(), "Should be in ImageBridgeChild thread."); MediaSystemResourceManager::Shutdown(); // Force all managed protocols to shut themselves down cleanly InfallibleTArray<PCompositableChild*> compositables; ManagedPCompositableChild(compositables); for (int i = compositables.Length() - 1; i >= 0; --i) { auto compositable = CompositableClient::FromIPDLActor(compositables[i]); if (compositable) { compositable->Destroy(); } } InfallibleTArray<PTextureChild*> textures; ManagedPTextureChild(textures); for (int i = textures.Length() - 1; i >= 0; --i) { RefPtr<TextureClient> client = TextureClient::AsTextureClient(textures[i]); if (client) { client->Destroy(); } } if (mCanSend) { SendWillClose(); } MarkShutDown(); // From now on, no message can be sent through the image bridge from the // client side except the final Stop message. } // dispatched function void ImageBridgeChild::ShutdownStep2(SynchronousTask* aTask) { AutoCompleteTask complete(aTask); MOZ_ASSERT(InImageBridgeChildThread(), "Should be in ImageBridgeChild thread."); if (!mCalledClose) { Close(); mCalledClose = true; } } void ImageBridgeChild::ActorDestroy(ActorDestroyReason aWhy) { mCanSend = false; mCalledClose = true; } void ImageBridgeChild::DeallocPImageBridgeChild() { this->Release(); } void ImageBridgeChild::CreateImageClientSync(SynchronousTask* aTask, RefPtr<ImageClient>* result, CompositableType aType, ImageContainer* aImageContainer, ImageContainerChild* aContainerChild) { AutoCompleteTask complete(aTask); *result = CreateImageClientNow(aType, aImageContainer, aContainerChild); } // dispatched function void ImageBridgeChild::CreateCanvasClientSync(SynchronousTask* aTask, CanvasClient::CanvasClientType aType, TextureFlags aFlags, RefPtr<CanvasClient>* const outResult) { AutoCompleteTask complete(aTask); *outResult = CreateCanvasClientNow(aType, aFlags); } ImageBridgeChild::ImageBridgeChild() : mCanSend(false) , mCalledClose(false) , mFwdTransactionId(0) { MOZ_ASSERT(NS_IsMainThread()); mTxn = new CompositableTransaction(); } ImageBridgeChild::~ImageBridgeChild() { delete mTxn; } void ImageBridgeChild::MarkShutDown() { mTexturesWaitingRecycled.Clear(); mCanSend = false; } void ImageBridgeChild::Connect(CompositableClient* aCompositable, ImageContainer* aImageContainer) { MOZ_ASSERT(aCompositable); MOZ_ASSERT(InImageBridgeChildThread()); MOZ_ASSERT(CanSend()); uint64_t id = 0; PImageContainerChild* imageContainerChild = nullptr; if (aImageContainer) imageContainerChild = aImageContainer->GetPImageContainerChild(); PCompositableChild* child = SendPCompositableConstructor(aCompositable->GetTextureInfo(), imageContainerChild, &id); if (!child) { return; } aCompositable->InitIPDLActor(child, id); } PCompositableChild* ImageBridgeChild::AllocPCompositableChild(const TextureInfo& aInfo, PImageContainerChild* aChild, uint64_t* aID) { MOZ_ASSERT(CanSend()); return AsyncCompositableChild::CreateActor(); } bool ImageBridgeChild::DeallocPCompositableChild(PCompositableChild* aActor) { AsyncCompositableChild::DestroyActor(aActor); return true; } Thread* ImageBridgeChild::GetThread() const { return sImageBridgeChildThread; } /* static */ RefPtr<ImageBridgeChild> ImageBridgeChild::GetSingleton() { StaticMutexAutoLock lock(sImageBridgeSingletonLock); return sImageBridgeChildSingleton; } void ImageBridgeChild::ReleaseImageContainer(RefPtr<ImageContainerChild> aChild) { if (!aChild) { return; } if (!InImageBridgeChildThread()) { RefPtr<Runnable> runnable = WrapRunnable( RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::ReleaseImageContainer, aChild); GetMessageLoop()->PostTask(runnable.forget()); return; } aChild->SendAsyncDelete(); } void ImageBridgeChild::ReleaseTextureClientNow(TextureClient* aClient) { MOZ_ASSERT(InImageBridgeChildThread()); RELEASE_MANUALLY(aClient); } /* static */ void ImageBridgeChild::DispatchReleaseTextureClient(TextureClient* aClient) { if (!aClient) { return; } RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton(); if (!imageBridge) { // TextureClient::Release should normally happen in the ImageBridgeChild // thread because it usually generate some IPDL messages. // However, if we take this branch it means that the ImageBridgeChild // has already shut down, along with the TextureChild, which means no // message will be sent and it is safe to run this code from any thread. MOZ_ASSERT(aClient->GetIPDLActor() == nullptr); RELEASE_MANUALLY(aClient); return; } RefPtr<Runnable> runnable = WrapRunnable( imageBridge, &ImageBridgeChild::ReleaseTextureClientNow, aClient); imageBridge->GetMessageLoop()->PostTask(runnable.forget()); } void ImageBridgeChild::UpdateImageClient(RefPtr<ImageClient> aClient, RefPtr<ImageContainer> aContainer) { if (!aClient || !aContainer) { return; } if (!InImageBridgeChildThread()) { RefPtr<Runnable> runnable = WrapRunnable( RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::UpdateImageClient, aClient, aContainer); GetMessageLoop()->PostTask(runnable.forget()); return; } if (!CanSend()) { return; } // If the client has become disconnected before this event was dispatched, // early return now. if (!aClient->IsConnected()) { return; } BeginTransaction(); aClient->UpdateImage(aContainer, Layer::CONTENT_OPAQUE); EndTransaction(); } void ImageBridgeChild::UpdateAsyncCanvasRendererSync(SynchronousTask* aTask, AsyncCanvasRenderer* aWrapper) { AutoCompleteTask complete(aTask); UpdateAsyncCanvasRendererNow(aWrapper); } void ImageBridgeChild::UpdateAsyncCanvasRenderer(AsyncCanvasRenderer* aWrapper) { aWrapper->GetCanvasClient()->UpdateAsync(aWrapper); if (InImageBridgeChildThread()) { UpdateAsyncCanvasRendererNow(aWrapper); return; } SynchronousTask task("UpdateAsyncCanvasRenderer Lock"); RefPtr<Runnable> runnable = WrapRunnable( RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::UpdateAsyncCanvasRendererSync, &task, aWrapper); GetMessageLoop()->PostTask(runnable.forget()); task.Wait(); } void ImageBridgeChild::UpdateAsyncCanvasRendererNow(AsyncCanvasRenderer* aWrapper) { MOZ_ASSERT(aWrapper); if (!CanSend()) { return; } BeginTransaction(); aWrapper->GetCanvasClient()->Updated(); EndTransaction(); } void ImageBridgeChild::FlushAllImagesSync(SynchronousTask* aTask, ImageClient* aClient, ImageContainer* aContainer) { AutoCompleteTask complete(aTask); if (!CanSend()) { return; } MOZ_ASSERT(aClient); BeginTransaction(); if (aContainer) { aContainer->ClearImagesFromImageBridge(); } aClient->FlushAllImages(); EndTransaction(); } void ImageBridgeChild::FlushAllImages(ImageClient* aClient, ImageContainer* aContainer) { MOZ_ASSERT(aClient); MOZ_ASSERT(!InImageBridgeChildThread()); if (InImageBridgeChildThread()) { NS_ERROR("ImageBridgeChild::FlushAllImages() is called on ImageBridge thread."); return; } SynchronousTask task("FlushAllImages Lock"); // RefPtrs on arguments are not needed since this dispatches synchronously. RefPtr<Runnable> runnable = WrapRunnable( RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::FlushAllImagesSync, &task, aClient, aContainer); GetMessageLoop()->PostTask(runnable.forget()); task.Wait(); } void ImageBridgeChild::BeginTransaction() { MOZ_ASSERT(CanSend()); MOZ_ASSERT(mTxn->Finished(), "uncommitted txn?"); UpdateFwdTransactionId(); mTxn->Begin(); } void ImageBridgeChild::EndTransaction() { MOZ_ASSERT(CanSend()); MOZ_ASSERT(!mTxn->Finished(), "forgot BeginTransaction?"); AutoEndTransaction _(mTxn); if (mTxn->IsEmpty()) { return; } AutoTArray<CompositableOperation, 10> cset; cset.SetCapacity(mTxn->mOperations.size()); if (!mTxn->mOperations.empty()) { cset.AppendElements(&mTxn->mOperations.front(), mTxn->mOperations.size()); } if (!IsSameProcess()) { ShadowLayerForwarder::PlatformSyncBeforeUpdate(); } AutoTArray<EditReply, 10> replies; if (mTxn->mSwapRequired) { if (!SendUpdate(cset, mTxn->mDestroyedActors, GetFwdTransactionId(), &replies)) { NS_WARNING("could not send async texture transaction"); return; } } else { // If we don't require a swap we can call SendUpdateNoSwap which // assumes that aReplies is empty (DEBUG assertion) if (!SendUpdateNoSwap(cset, mTxn->mDestroyedActors, GetFwdTransactionId())) { NS_WARNING("could not send async texture transaction (no swap)"); return; } } for (nsTArray<EditReply>::size_type i = 0; i < replies.Length(); ++i) { NS_RUNTIMEABORT("not reached"); } } void ImageBridgeChild::SendImageBridgeThreadId() { } bool ImageBridgeChild::InitForContent(Endpoint<PImageBridgeChild>&& aEndpoint) { MOZ_ASSERT(NS_IsMainThread()); gfxPlatform::GetPlatform(); if (!sImageBridgeChildThread) { sImageBridgeChildThread = new ImageBridgeThread(); if (!sImageBridgeChildThread->Start()) { return false; } } RefPtr<ImageBridgeChild> child = new ImageBridgeChild(); RefPtr<Runnable> runnable = NewRunnableMethod<Endpoint<PImageBridgeChild>&&>( child, &ImageBridgeChild::Bind, Move(aEndpoint)); child->GetMessageLoop()->PostTask(runnable.forget()); // Assign this after so other threads can't post messages before we connect to IPDL. { StaticMutexAutoLock lock(sImageBridgeSingletonLock); sImageBridgeChildSingleton = child; } return true; } bool ImageBridgeChild::ReinitForContent(Endpoint<PImageBridgeChild>&& aEndpoint) { MOZ_ASSERT(NS_IsMainThread()); // Note that at this point, ActorDestroy may not have been called yet, // meaning mCanSend is still true. In this case we will try to send a // synchronous WillClose message to the parent, and will certainly get a // false result and a MsgDropped processing error. This is okay. ShutdownSingleton(); return InitForContent(Move(aEndpoint)); } void ImageBridgeChild::Bind(Endpoint<PImageBridgeChild>&& aEndpoint) { if (!aEndpoint.Bind(this)) { return; } // This reference is dropped in DeallocPImageBridgeChild. this->AddRef(); mCanSend = true; SendImageBridgeThreadId(); } void ImageBridgeChild::BindSameProcess(RefPtr<ImageBridgeParent> aParent) { MessageLoop *parentMsgLoop = aParent->GetMessageLoop(); ipc::MessageChannel *parentChannel = aParent->GetIPCChannel(); Open(parentChannel, parentMsgLoop, mozilla::ipc::ChildSide); // This reference is dropped in DeallocPImageBridgeChild. this->AddRef(); mCanSend = true; SendImageBridgeThreadId(); } /* static */ void ImageBridgeChild::ShutDown() { MOZ_ASSERT(NS_IsMainThread()); ShutdownSingleton(); delete sImageBridgeChildThread; sImageBridgeChildThread = nullptr; } /* static */ void ImageBridgeChild::ShutdownSingleton() { MOZ_ASSERT(NS_IsMainThread()); if (RefPtr<ImageBridgeChild> child = GetSingleton()) { child->WillShutdown(); StaticMutexAutoLock lock(sImageBridgeSingletonLock); sImageBridgeChildSingleton = nullptr; } } void ImageBridgeChild::WillShutdown() { { SynchronousTask task("ImageBridge ShutdownStep1 lock"); RefPtr<Runnable> runnable = WrapRunnable( RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::ShutdownStep1, &task); GetMessageLoop()->PostTask(runnable.forget()); task.Wait(); } { SynchronousTask task("ImageBridge ShutdownStep2 lock"); RefPtr<Runnable> runnable = WrapRunnable( RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::ShutdownStep2, &task); GetMessageLoop()->PostTask(runnable.forget()); task.Wait(); } } void ImageBridgeChild::InitSameProcess() { NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!"); MOZ_ASSERT(!sImageBridgeChildSingleton); MOZ_ASSERT(!sImageBridgeChildThread); sImageBridgeChildThread = new ImageBridgeThread(); if (!sImageBridgeChildThread->IsRunning()) { sImageBridgeChildThread->Start(); } RefPtr<ImageBridgeChild> child = new ImageBridgeChild(); RefPtr<ImageBridgeParent> parent = ImageBridgeParent::CreateSameProcess(); RefPtr<Runnable> runnable = WrapRunnable( child, &ImageBridgeChild::BindSameProcess, parent); child->GetMessageLoop()->PostTask(runnable.forget()); // Assign this after so other threads can't post messages before we connect to IPDL. { StaticMutexAutoLock lock(sImageBridgeSingletonLock); sImageBridgeChildSingleton = child; } } /* static */ void ImageBridgeChild::InitWithGPUProcess(Endpoint<PImageBridgeChild>&& aEndpoint) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!sImageBridgeChildSingleton); MOZ_ASSERT(!sImageBridgeChildThread); sImageBridgeChildThread = new ImageBridgeThread(); if (!sImageBridgeChildThread->IsRunning()) { sImageBridgeChildThread->Start(); } RefPtr<ImageBridgeChild> child = new ImageBridgeChild(); MessageLoop* loop = child->GetMessageLoop(); loop->PostTask(NewRunnableMethod<Endpoint<PImageBridgeChild>&&>( child, &ImageBridgeChild::Bind, Move(aEndpoint))); // Assign this after so other threads can't post messages before we connect to IPDL. { StaticMutexAutoLock lock(sImageBridgeSingletonLock); sImageBridgeChildSingleton = child; } } bool InImageBridgeChildThread() { return sImageBridgeChildThread && sImageBridgeChildThread->thread_id() == PlatformThread::CurrentId(); } MessageLoop * ImageBridgeChild::GetMessageLoop() const { return sImageBridgeChildThread ? sImageBridgeChildThread->message_loop() : nullptr; } /* static */ void ImageBridgeChild::IdentifyCompositorTextureHost(const TextureFactoryIdentifier& aIdentifier) { if (RefPtr<ImageBridgeChild> child = GetSingleton()) { child->IdentifyTextureHost(aIdentifier); } } RefPtr<ImageClient> ImageBridgeChild::CreateImageClient(CompositableType aType, ImageContainer* aImageContainer, ImageContainerChild* aContainerChild) { if (InImageBridgeChildThread()) { return CreateImageClientNow(aType, aImageContainer, aContainerChild); } SynchronousTask task("CreateImageClient Lock"); RefPtr<ImageClient> result = nullptr; RefPtr<Runnable> runnable = WrapRunnable( RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::CreateImageClientSync, &task, &result, aType, aImageContainer, aContainerChild); GetMessageLoop()->PostTask(runnable.forget()); task.Wait(); return result; } RefPtr<ImageClient> ImageBridgeChild::CreateImageClientNow(CompositableType aType, ImageContainer* aImageContainer, ImageContainerChild* aContainerChild) { MOZ_ASSERT(InImageBridgeChildThread()); if (!CanSend()) { return nullptr; } if (aImageContainer) { aContainerChild->RegisterWithIPDL(); if (!SendPImageContainerConstructor(aContainerChild)) { return nullptr; } } RefPtr<ImageClient> client = ImageClient::CreateImageClient(aType, this, TextureFlags::NO_FLAGS); MOZ_ASSERT(client, "failed to create ImageClient"); if (client) { client->Connect(aImageContainer); } return client; } already_AddRefed<CanvasClient> ImageBridgeChild::CreateCanvasClient(CanvasClient::CanvasClientType aType, TextureFlags aFlag) { if (InImageBridgeChildThread()) { return CreateCanvasClientNow(aType, aFlag); } SynchronousTask task("CreateCanvasClient Lock"); // RefPtrs on arguments are not needed since this dispatches synchronously. RefPtr<CanvasClient> result = nullptr; RefPtr<Runnable> runnable = WrapRunnable( RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::CreateCanvasClientSync, &task, aType, aFlag, &result); GetMessageLoop()->PostTask(runnable.forget()); task.Wait(); return result.forget(); } already_AddRefed<CanvasClient> ImageBridgeChild::CreateCanvasClientNow(CanvasClient::CanvasClientType aType, TextureFlags aFlag) { RefPtr<CanvasClient> client = CanvasClient::CreateCanvasClient(aType, this, aFlag); MOZ_ASSERT(client, "failed to create CanvasClient"); if (client) { client->Connect(); } return client.forget(); } bool ImageBridgeChild::AllocUnsafeShmem(size_t aSize, ipc::SharedMemory::SharedMemoryType aType, ipc::Shmem* aShmem) { if (!InImageBridgeChildThread()) { return DispatchAllocShmemInternal(aSize, aType, aShmem, true); // true: unsafe } if (!CanSend()) { return false; } return PImageBridgeChild::AllocUnsafeShmem(aSize, aType, aShmem); } bool ImageBridgeChild::AllocShmem(size_t aSize, ipc::SharedMemory::SharedMemoryType aType, ipc::Shmem* aShmem) { if (!InImageBridgeChildThread()) { return DispatchAllocShmemInternal(aSize, aType, aShmem, false); // false: unsafe } if (!CanSend()) { return false; } return PImageBridgeChild::AllocShmem(aSize, aType, aShmem); } // NewRunnableFunction accepts a limited number of parameters so we need a // struct here struct AllocShmemParams { size_t mSize; ipc::SharedMemory::SharedMemoryType mType; ipc::Shmem* mShmem; bool mUnsafe; bool mSuccess; }; void ImageBridgeChild::ProxyAllocShmemNow(SynchronousTask* aTask, AllocShmemParams* aParams) { AutoCompleteTask complete(aTask); if (!CanSend()) { return; } bool ok = false; if (aParams->mUnsafe) { ok = AllocUnsafeShmem(aParams->mSize, aParams->mType, aParams->mShmem); } else { ok = AllocShmem(aParams->mSize, aParams->mType, aParams->mShmem); } aParams->mSuccess = ok; } bool ImageBridgeChild::DispatchAllocShmemInternal(size_t aSize, SharedMemory::SharedMemoryType aType, ipc::Shmem* aShmem, bool aUnsafe) { SynchronousTask task("AllocatorProxy alloc"); AllocShmemParams params = { aSize, aType, aShmem, aUnsafe, false }; RefPtr<Runnable> runnable = WrapRunnable( RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::ProxyAllocShmemNow, &task, ¶ms); GetMessageLoop()->PostTask(runnable.forget()); task.Wait(); return params.mSuccess; } void ImageBridgeChild::ProxyDeallocShmemNow(SynchronousTask* aTask, ipc::Shmem* aShmem, bool* aResult) { AutoCompleteTask complete(aTask); if (!CanSend()) { return; } *aResult = DeallocShmem(*aShmem); } bool ImageBridgeChild::DeallocShmem(ipc::Shmem& aShmem) { if (InImageBridgeChildThread()) { if (!CanSend()) { return false; } return PImageBridgeChild::DeallocShmem(aShmem); } SynchronousTask task("AllocatorProxy Dealloc"); bool result = false; RefPtr<Runnable> runnable = WrapRunnable( RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::ProxyDeallocShmemNow, &task, &aShmem, &result); GetMessageLoop()->PostTask(runnable.forget()); task.Wait(); return result; } PTextureChild* ImageBridgeChild::AllocPTextureChild(const SurfaceDescriptor&, const LayersBackend&, const TextureFlags&, const uint64_t& aSerial) { MOZ_ASSERT(CanSend()); return TextureClient::CreateIPDLActor(); } bool ImageBridgeChild::DeallocPTextureChild(PTextureChild* actor) { return TextureClient::DestroyIPDLActor(actor); } PMediaSystemResourceManagerChild* ImageBridgeChild::AllocPMediaSystemResourceManagerChild() { MOZ_ASSERT(CanSend()); return new mozilla::media::MediaSystemResourceManagerChild(); } bool ImageBridgeChild::DeallocPMediaSystemResourceManagerChild(PMediaSystemResourceManagerChild* aActor) { MOZ_ASSERT(aActor); delete static_cast<mozilla::media::MediaSystemResourceManagerChild*>(aActor); return true; } PImageContainerChild* ImageBridgeChild::AllocPImageContainerChild() { // we always use the "power-user" ctor NS_RUNTIMEABORT("not reached"); return nullptr; } bool ImageBridgeChild::DeallocPImageContainerChild(PImageContainerChild* actor) { static_cast<ImageContainerChild*>(actor)->UnregisterFromIPDL(); return true; } bool ImageBridgeChild::RecvParentAsyncMessages(InfallibleTArray<AsyncParentMessageData>&& aMessages) { for (AsyncParentMessageArray::index_type i = 0; i < aMessages.Length(); ++i) { const AsyncParentMessageData& message = aMessages[i]; switch (message.type()) { case AsyncParentMessageData::TOpNotifyNotUsed: { const OpNotifyNotUsed& op = message.get_OpNotifyNotUsed(); NotifyNotUsed(op.TextureId(), op.fwdTransactionId()); break; } default: NS_ERROR("unknown AsyncParentMessageData type"); return false; } } return true; } bool ImageBridgeChild::RecvDidComposite(InfallibleTArray<ImageCompositeNotification>&& aNotifications) { for (auto& n : aNotifications) { ImageContainerChild* child = static_cast<ImageContainerChild*>(n.imageContainerChild()); if (child) { child->NotifyComposite(n); } } return true; } PTextureChild* ImageBridgeChild::CreateTexture(const SurfaceDescriptor& aSharedData, LayersBackend aLayersBackend, TextureFlags aFlags, uint64_t aSerial) { MOZ_ASSERT(CanSend()); return SendPTextureConstructor(aSharedData, aLayersBackend, aFlags, aSerial); } static bool IBCAddOpDestroy(CompositableTransaction* aTxn, const OpDestroy& op, bool synchronously) { if (aTxn->Finished()) { return false; } aTxn->mDestroyedActors.AppendElement(op); if (synchronously) { aTxn->MarkSyncTransaction(); } return true; } bool ImageBridgeChild::DestroyInTransaction(PTextureChild* aTexture, bool synchronously) { return IBCAddOpDestroy(mTxn, OpDestroy(aTexture), synchronously); } bool ImageBridgeChild::DestroyInTransaction(PCompositableChild* aCompositable, bool synchronously) { return IBCAddOpDestroy(mTxn, OpDestroy(aCompositable), synchronously); } void ImageBridgeChild::RemoveTextureFromCompositable(CompositableClient* aCompositable, TextureClient* aTexture) { MOZ_ASSERT(CanSend()); MOZ_ASSERT(aTexture); MOZ_ASSERT(aTexture->IsSharedWithCompositor()); MOZ_ASSERT(aCompositable->IsConnected()); if (!aTexture || !aTexture->IsSharedWithCompositor() || !aCompositable->IsConnected()) { return; } CompositableOperation op( nullptr, aCompositable->GetIPDLActor(), OpRemoveTexture(nullptr, aTexture->GetIPDLActor())); if (aTexture->GetFlags() & TextureFlags::DEALLOCATE_CLIENT) { mTxn->AddEdit(op); } else { mTxn->AddNoSwapEdit(op); } } bool ImageBridgeChild::IsSameProcess() const { return OtherPid() == base::GetCurrentProcId(); } void ImageBridgeChild::Destroy(CompositableChild* aCompositable) { if (!InImageBridgeChildThread()) { RefPtr<Runnable> runnable = WrapRunnable( RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::Destroy, RefPtr<CompositableChild>(aCompositable)); GetMessageLoop()->PostTask(runnable.forget()); return; } CompositableForwarder::Destroy(aCompositable); } bool ImageBridgeChild::CanSend() const { MOZ_ASSERT(InImageBridgeChildThread()); return mCanSend; } void ImageBridgeChild::HandleFatalError(const char* aName, const char* aMsg) const { dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aName, aMsg, OtherPid()); } } // namespace layers } // namespace mozilla