/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set sw=2 ts=2 et 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/layers/CrossProcessCompositorBridgeParent.h" #include // for uint64_t #include "LayerTransactionParent.h" // for LayerTransactionParent #include "base/message_loop.h" // for MessageLoop #include "base/task.h" // for CancelableTask, etc #include "base/thread.h" // for Thread #include "mozilla/ipc/Transport.h" // for Transport #include "mozilla/layers/APZCTreeManager.h" // for APZCTreeManager #include "mozilla/layers/APZCTreeManagerParent.h" // for APZCTreeManagerParent #include "mozilla/layers/APZThreadUtils.h" // for APZCTreeManager #include "mozilla/layers/AsyncCompositionManager.h" #include "mozilla/layers/CompositorOptions.h" #include "mozilla/layers/CompositorThread.h" #include "mozilla/layers/LayerManagerComposite.h" #include "mozilla/layers/LayerTreeOwnerTracker.h" #include "mozilla/layers/PLayerTransactionParent.h" #include "mozilla/layers/RemoteContentController.h" #include "mozilla/layers/WebRenderBridgeParent.h" #include "mozilla/layers/WebRenderCompositableHolder.h" #include "mozilla/mozalloc.h" // for operator new, etc #include "nsDebug.h" // for NS_ASSERTION, etc #include "nsTArray.h" // for nsTArray #include "nsXULAppAPI.h" // for XRE_GetIOMessageLoop #include "mozilla/Unused.h" #include "mozilla/StaticPtr.h" using namespace std; namespace mozilla { namespace layers { // defined in CompositorBridgeParent.cpp typedef map LayerTreeMap; extern LayerTreeMap sIndirectLayerTrees; extern StaticAutoPtr sIndirectLayerTreesLock; void UpdateIndirectTree(uint64_t aId, Layer* aRoot, const TargetConfig& aTargetConfig); void EraseLayerState(uint64_t aId); mozilla::ipc::IPCResult CrossProcessCompositorBridgeParent::RecvRequestNotifyAfterRemotePaint() { mNotifyAfterRemotePaint = true; return IPC_OK(); } void CrossProcessCompositorBridgeParent::ActorDestroy(ActorDestroyReason aWhy) { // We must keep this object alive untill the code handling message // reception is finished on this thread. MessageLoop::current()->PostTask(NewRunnableMethod(this, &CrossProcessCompositorBridgeParent::DeferredDestroy)); } PLayerTransactionParent* CrossProcessCompositorBridgeParent::AllocPLayerTransactionParent( const nsTArray&, const uint64_t& aId, TextureFactoryIdentifier* aTextureFactoryIdentifier, bool *aSuccess) { MOZ_ASSERT(aId != 0); // Check to see if this child process has access to this layer tree. if (!LayerTreeOwnerTracker::Get()->IsMapped(aId, OtherPid())) { NS_ERROR("Unexpected layers id in AllocPLayerTransactionParent; dropping message..."); return nullptr; } MonitorAutoLock lock(*sIndirectLayerTreesLock); CompositorBridgeParent::LayerTreeState* state = nullptr; LayerTreeMap::iterator itr = sIndirectLayerTrees.find(aId); if (sIndirectLayerTrees.end() != itr) { state = &itr->second; } if (state && state->mLayerManager) { state->mCrossProcessParent = this; HostLayerManager* lm = state->mLayerManager; *aTextureFactoryIdentifier = lm->GetCompositor()->GetTextureFactoryIdentifier(); *aSuccess = true; LayerTransactionParent* p = new LayerTransactionParent(lm, this, aId); p->AddIPDLReference(); sIndirectLayerTrees[aId].mLayerTree = p; if (state->mPendingCompositorUpdate) { p->SetPendingCompositorUpdate(state->mPendingCompositorUpdate.value()); } return p; } NS_WARNING("Created child without a matching parent?"); *aSuccess = false; LayerTransactionParent* p = new LayerTransactionParent(nullptr, this, aId); p->AddIPDLReference(); return p; } bool CrossProcessCompositorBridgeParent::DeallocPLayerTransactionParent(PLayerTransactionParent* aLayers) { LayerTransactionParent* slp = static_cast(aLayers); EraseLayerState(slp->GetId()); static_cast(aLayers)->ReleaseIPDLReference(); return true; } mozilla::ipc::IPCResult CrossProcessCompositorBridgeParent::RecvGetCompositorOptions(const uint64_t& aLayersId, CompositorOptions* aOptions) { // Check to see if this child process has access to this layer tree. if (!LayerTreeOwnerTracker::Get()->IsMapped(aLayersId, OtherPid())) { NS_ERROR("Unexpected layers id in RecvGetCompositorOptions; dropping message..."); return IPC_FAIL_NO_REASON(this); } MonitorAutoLock lock(*sIndirectLayerTreesLock); CompositorBridgeParent::LayerTreeState& state = sIndirectLayerTrees[aLayersId]; if (state.mParent) { *aOptions = state.mParent->GetOptions(); } return IPC_OK(); } PAPZCTreeManagerParent* CrossProcessCompositorBridgeParent::AllocPAPZCTreeManagerParent(const uint64_t& aLayersId) { // Check to see if this child process has access to this layer tree. if (!LayerTreeOwnerTracker::Get()->IsMapped(aLayersId, OtherPid())) { NS_ERROR("Unexpected layers id in AllocPAPZCTreeManagerParent; dropping message..."); return nullptr; } MonitorAutoLock lock(*sIndirectLayerTreesLock); CompositorBridgeParent::LayerTreeState& state = sIndirectLayerTrees[aLayersId]; MOZ_ASSERT(state.mParent); MOZ_ASSERT(!state.mApzcTreeManagerParent); state.mApzcTreeManagerParent = new APZCTreeManagerParent(aLayersId, state.mParent->GetAPZCTreeManager()); return state.mApzcTreeManagerParent; } bool CrossProcessCompositorBridgeParent::DeallocPAPZCTreeManagerParent(PAPZCTreeManagerParent* aActor) { APZCTreeManagerParent* parent = static_cast(aActor); MonitorAutoLock lock(*sIndirectLayerTreesLock); auto iter = sIndirectLayerTrees.find(parent->LayersId()); if (iter != sIndirectLayerTrees.end()) { CompositorBridgeParent::LayerTreeState& state = iter->second; MOZ_ASSERT(state.mApzcTreeManagerParent == parent); state.mApzcTreeManagerParent = nullptr; } delete parent; return true; } PAPZParent* CrossProcessCompositorBridgeParent::AllocPAPZParent(const uint64_t& aLayersId) { // Check to see if this child process has access to this layer tree. if (!LayerTreeOwnerTracker::Get()->IsMapped(aLayersId, OtherPid())) { NS_ERROR("Unexpected layers id in AllocPAPZParent; dropping message..."); return nullptr; } RemoteContentController* controller = new RemoteContentController(); // Increment the controller's refcount before we return it. This will keep the // controller alive until it is released by IPDL in DeallocPAPZParent. controller->AddRef(); MonitorAutoLock lock(*sIndirectLayerTreesLock); CompositorBridgeParent::LayerTreeState& state = sIndirectLayerTrees[aLayersId]; MOZ_ASSERT(!state.mController); state.mController = controller; return controller; } bool CrossProcessCompositorBridgeParent::DeallocPAPZParent(PAPZParent* aActor) { RemoteContentController* controller = static_cast(aActor); controller->Release(); return true; } PWebRenderBridgeParent* CrossProcessCompositorBridgeParent::AllocPWebRenderBridgeParent(const wr::PipelineId& aPipelineId, TextureFactoryIdentifier* aTextureFactoryIdentifier, uint32_t *aIdNamespace) { #ifndef MOZ_ENABLE_WEBRENDER // Extra guard since this in the parent process and we don't want a malicious // child process invoking this codepath before it's ready MOZ_RELEASE_ASSERT(false); #endif // Check to see if this child process has access to this layer tree. if (!LayerTreeOwnerTracker::Get()->IsMapped(aPipelineId.mHandle, OtherPid())) { NS_ERROR("Unexpected layers id in AllocPAPZCTreeManagerParent; dropping message..."); return nullptr; } auto pipelineHandle = aPipelineId.mHandle; MonitorAutoLock lock(*sIndirectLayerTreesLock); MOZ_ASSERT(sIndirectLayerTrees.find(pipelineHandle) != sIndirectLayerTrees.end()); MOZ_ASSERT(sIndirectLayerTrees[pipelineHandle].mWrBridge == nullptr); CompositorBridgeParent* cbp = sIndirectLayerTrees[pipelineHandle].mParent; WebRenderBridgeParent* root = sIndirectLayerTrees[cbp->RootLayerTreeId()].mWrBridge.get(); WebRenderBridgeParent* parent = nullptr; RefPtr api = root->GetWebRenderAPI(); RefPtr holder = root->CompositableHolder(); parent = new WebRenderBridgeParent(this, aPipelineId, nullptr, Move(api), Move(holder)); parent->AddRef(); // IPDL reference sIndirectLayerTrees[pipelineHandle].mCrossProcessParent = this; sIndirectLayerTrees[pipelineHandle].mWrBridge = parent; *aTextureFactoryIdentifier = parent->GetTextureFactoryIdentifier(); *aIdNamespace = api->GetNamespace().mHandle; return parent; } bool CrossProcessCompositorBridgeParent::DeallocPWebRenderBridgeParent(PWebRenderBridgeParent* aActor) { #ifndef MOZ_ENABLE_WEBRENDER // Extra guard since this in the parent process and we don't want a malicious // child process invoking this codepath before it's ready MOZ_RELEASE_ASSERT(false); #endif WebRenderBridgeParent* parent = static_cast(aActor); EraseLayerState(parent->PipelineId().mHandle); parent->Release(); // IPDL reference return true; } mozilla::ipc::IPCResult CrossProcessCompositorBridgeParent::RecvNotifyChildCreated(const uint64_t& child) { MonitorAutoLock lock(*sIndirectLayerTreesLock); for (LayerTreeMap::iterator it = sIndirectLayerTrees.begin(); it != sIndirectLayerTrees.end(); it++) { CompositorBridgeParent::LayerTreeState* lts = &it->second; if (lts->mParent && lts->mCrossProcessParent == this) { lts->mParent->NotifyChildCreated(child); return IPC_OK(); } } return IPC_FAIL_NO_REASON(this); } void CrossProcessCompositorBridgeParent::ShadowLayersUpdated( LayerTransactionParent* aLayerTree, const TransactionInfo& aInfo, bool aHitTestUpdate) { uint64_t id = aLayerTree->GetId(); MOZ_ASSERT(id != 0); CompositorBridgeParent::LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(id); if (!state) { return; } MOZ_ASSERT(state->mParent); state->mParent->ScheduleRotationOnCompositorThread( aInfo.targetConfig(), aInfo.isFirstPaint()); Layer* shadowRoot = aLayerTree->GetRoot(); if (shadowRoot) { CompositorBridgeParent::SetShadowProperties(shadowRoot); } UpdateIndirectTree(id, shadowRoot, aInfo.targetConfig()); // Cache the plugin data for this remote layer tree state->mPluginData = aInfo.plugins(); state->mUpdatedPluginDataAvailable = true; state->mParent->NotifyShadowTreeTransaction( id, aInfo.isFirstPaint(), aInfo.scheduleComposite(), aInfo.paintSequenceNumber(), aInfo.isRepeatTransaction(), aHitTestUpdate); // Send the 'remote paint ready' message to the content thread if it has already asked. if(mNotifyAfterRemotePaint) { Unused << SendRemotePaintIsReady(); mNotifyAfterRemotePaint = false; } if (aLayerTree->ShouldParentObserveEpoch()) { // Note that we send this through the window compositor, since this needs // to reach the widget owning the tab. Unused << state->mParent->SendObserveLayerUpdate(id, aLayerTree->GetChildEpoch(), true); } aLayerTree->SetPendingTransactionId(aInfo.id()); } void CrossProcessCompositorBridgeParent::DidComposite( uint64_t aId, TimeStamp& aCompositeStart, TimeStamp& aCompositeEnd) { sIndirectLayerTreesLock->AssertCurrentThreadOwns(); if (LayerTransactionParent *layerTree = sIndirectLayerTrees[aId].mLayerTree) { Unused << SendDidComposite(aId, layerTree->GetPendingTransactionId(), aCompositeStart, aCompositeEnd); layerTree->SetPendingTransactionId(0); } else if (WebRenderBridgeParent* wrbridge = sIndirectLayerTrees[aId].mWrBridge) { Unused << SendDidComposite(aId, wrbridge->GetPendingTransactionId(), aCompositeStart, aCompositeEnd); wrbridge->SetPendingTransactionId(0); } } void CrossProcessCompositorBridgeParent::ForceComposite(LayerTransactionParent* aLayerTree) { uint64_t id = aLayerTree->GetId(); MOZ_ASSERT(id != 0); CompositorBridgeParent* parent; { // scope lock MonitorAutoLock lock(*sIndirectLayerTreesLock); parent = sIndirectLayerTrees[id].mParent; } if (parent) { parent->ForceComposite(aLayerTree); } } void CrossProcessCompositorBridgeParent::NotifyClearCachedResources(LayerTransactionParent* aLayerTree) { uint64_t id = aLayerTree->GetId(); MOZ_ASSERT(id != 0); const CompositorBridgeParent::LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(id); if (state && state->mParent) { // Note that we send this through the window compositor, since this needs // to reach the widget owning the tab. Unused << state->mParent->SendObserveLayerUpdate(id, aLayerTree->GetChildEpoch(), false); } } bool CrossProcessCompositorBridgeParent::SetTestSampleTime( LayerTransactionParent* aLayerTree, const TimeStamp& aTime) { uint64_t id = aLayerTree->GetId(); MOZ_ASSERT(id != 0); const CompositorBridgeParent::LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(id); if (!state) { return false; } MOZ_ASSERT(state->mParent); return state->mParent->SetTestSampleTime(aLayerTree, aTime); } void CrossProcessCompositorBridgeParent::LeaveTestMode(LayerTransactionParent* aLayerTree) { uint64_t id = aLayerTree->GetId(); MOZ_ASSERT(id != 0); const CompositorBridgeParent::LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(id); if (!state) { return; } MOZ_ASSERT(state->mParent); state->mParent->LeaveTestMode(aLayerTree); } void CrossProcessCompositorBridgeParent::ApplyAsyncProperties( LayerTransactionParent* aLayerTree) { uint64_t id = aLayerTree->GetId(); MOZ_ASSERT(id != 0); const CompositorBridgeParent::LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(id); if (!state) { return; } MOZ_ASSERT(state->mParent); state->mParent->ApplyAsyncProperties(aLayerTree); } void CrossProcessCompositorBridgeParent::FlushApzRepaints(const LayerTransactionParent* aLayerTree) { uint64_t id = aLayerTree->GetId(); MOZ_ASSERT(id != 0); const CompositorBridgeParent::LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(id); if (!state) { return; } MOZ_ASSERT(state->mParent); state->mParent->FlushApzRepaints(aLayerTree); } void CrossProcessCompositorBridgeParent::GetAPZTestData( const LayerTransactionParent* aLayerTree, APZTestData* aOutData) { uint64_t id = aLayerTree->GetId(); MOZ_ASSERT(id != 0); MonitorAutoLock lock(*sIndirectLayerTreesLock); *aOutData = sIndirectLayerTrees[id].mApzTestData; } void CrossProcessCompositorBridgeParent::SetConfirmedTargetAPZC( const LayerTransactionParent* aLayerTree, const uint64_t& aInputBlockId, const nsTArray& aTargets) { uint64_t id = aLayerTree->GetId(); MOZ_ASSERT(id != 0); const CompositorBridgeParent::LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(id); if (!state || !state->mParent) { return; } state->mParent->SetConfirmedTargetAPZC(aLayerTree, aInputBlockId, aTargets); } AsyncCompositionManager* CrossProcessCompositorBridgeParent::GetCompositionManager(LayerTransactionParent* aLayerTree) { uint64_t id = aLayerTree->GetId(); const CompositorBridgeParent::LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(id); if (!state) { return nullptr; } MOZ_ASSERT(state->mParent); return state->mParent->GetCompositionManager(aLayerTree); } mozilla::ipc::IPCResult CrossProcessCompositorBridgeParent::RecvAcknowledgeCompositorUpdate(const uint64_t& aLayersId, const uint64_t& aSeqNo) { MonitorAutoLock lock(*sIndirectLayerTreesLock); CompositorBridgeParent::LayerTreeState& state = sIndirectLayerTrees[aLayersId]; if (LayerTransactionParent* ltp = state.mLayerTree) { ltp->AcknowledgeCompositorUpdate(aSeqNo); } if (state.mPendingCompositorUpdate == Some(aSeqNo)) { state.mPendingCompositorUpdate = Nothing(); } return IPC_OK(); } void CrossProcessCompositorBridgeParent::DeferredDestroy() { mCompositorThreadHolder = nullptr; mSelfRef = nullptr; } CrossProcessCompositorBridgeParent::~CrossProcessCompositorBridgeParent() { MOZ_ASSERT(XRE_GetIOMessageLoop()); MOZ_ASSERT(IToplevelProtocol::GetTransport()); } PTextureParent* CrossProcessCompositorBridgeParent::AllocPTextureParent(const SurfaceDescriptor& aSharedData, const LayersBackend& aLayersBackend, const TextureFlags& aFlags, const uint64_t& aId, const uint64_t& aSerial) { CompositorBridgeParent::LayerTreeState* state = nullptr; LayerTreeMap::iterator itr = sIndirectLayerTrees.find(aId); if (sIndirectLayerTrees.end() != itr) { state = &itr->second; } TextureFlags flags = aFlags; if (!state || state->mPendingCompositorUpdate) { // The compositor was recreated, and we're receiving layers updates for a // a layer manager that will soon be discarded or invalidated. We can't // return null because this will mess up deserialization later and we'll // kill the content process. Instead, we signal that the underlying // TextureHost should not attempt to access the compositor. flags |= TextureFlags::INVALID_COMPOSITOR; } else if (state->mLayerManager && state->mLayerManager->GetCompositor() && aLayersBackend != state->mLayerManager->GetCompositor()->GetBackendType()) { gfxDevCrash(gfx::LogReason::PAllocTextureBackendMismatch) << "Texture backend is wrong"; } return TextureHost::CreateIPDLActor(this, aSharedData, aLayersBackend, aFlags, aSerial); } bool CrossProcessCompositorBridgeParent::DeallocPTextureParent(PTextureParent* actor) { return TextureHost::DestroyIPDLActor(actor); } bool CrossProcessCompositorBridgeParent::IsSameProcess() const { return OtherPid() == base::GetCurrentProcId(); } mozilla::ipc::IPCResult CrossProcessCompositorBridgeParent::RecvClearApproximatelyVisibleRegions(const uint64_t& aLayersId, const uint32_t& aPresShellId) { CompositorBridgeParent* parent; { // scope lock MonitorAutoLock lock(*sIndirectLayerTreesLock); parent = sIndirectLayerTrees[aLayersId].mParent; } if (parent) { parent->ClearApproximatelyVisibleRegions(aLayersId, Some(aPresShellId)); } return IPC_OK(); } mozilla::ipc::IPCResult CrossProcessCompositorBridgeParent::RecvNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid, const CSSIntRegion& aRegion) { CompositorBridgeParent* parent; { // scope lock MonitorAutoLock lock(*sIndirectLayerTreesLock); parent = sIndirectLayerTrees[aGuid.mLayersId].mParent; } if (parent) { if (!parent->RecvNotifyApproximatelyVisibleRegion(aGuid, aRegion)) { return IPC_FAIL_NO_REASON(this); } return IPC_OK();; } return IPC_OK(); } void CrossProcessCompositorBridgeParent::UpdatePaintTime(LayerTransactionParent* aLayerTree, const TimeDuration& aPaintTime) { uint64_t id = aLayerTree->GetId(); MOZ_ASSERT(id != 0); CompositorBridgeParent::LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(id); if (!state || !state->mParent) { return; } state->mParent->UpdatePaintTime(aLayerTree, aPaintTime); } void CrossProcessCompositorBridgeParent::ObserveLayerUpdate(uint64_t aLayersId, uint64_t aEpoch, bool aActive) { MOZ_ASSERT(aLayersId != 0); CompositorBridgeParent::LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aLayersId); if (!state || !state->mParent) { return; } Unused << state->mParent->SendObserveLayerUpdate(aLayersId, aEpoch, aActive); } } // namespace layers } // namespace mozilla