Files
tubestation/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
Kartikaya Gupta 1bcda612ce Bug 1350638 - Remove sync GetCompositorOptions call in TabChild::InitRenderingState. r=dvander
The goal of this patch is to remove the call to the sync IPC
GetCompositorOptions message from TabChild::InitRenderingState. In order
to this, we have InitRenderingState take the CompositorOptions as an
argument instead, and propagate that backwards through the call sites.
Eventually we can propagate it back to a set of already-sync IPC
messages in PCompositorBridge that are used during layers id
registration (NotifyChildCreated, NotifyChildRecreated, etc.). Therefore
this patch effectively piggybacks the CompositorOptions sync IPC onto
these pre-existing sync IPC messages.

The one exception is when we propagate it back to the AdoptChild call.
If this message were sync we could just use it like the others and have
it return a CompositorOptions. However, it is async, so instead we add
another call to GetCompositorOptions here temporarily. This will be
removed in the next patch.

MozReview-Commit-ID: AtdYOuXmHu4
2017-04-09 17:30:27 -04:00

633 lines
21 KiB
C++

/* -*- 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 <stdint.h> // 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/AnimationHelper.h" // for CompositorAnimationStorage
#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<uint64_t, CompositorBridgeParent::LayerTreeState> LayerTreeMap;
extern LayerTreeMap sIndirectLayerTrees;
extern StaticAutoPtr<mozilla::Monitor> 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<LayersBackend>&,
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<LayerTransactionParent*>(aLayers);
EraseLayerState(slp->GetId());
static_cast<LayerTransactionParent*>(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<APZCTreeManagerParent*>(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<RemoteContentController*>(aActor);
controller->Release();
return true;
}
PWebRenderBridgeParent*
CrossProcessCompositorBridgeParent::AllocPWebRenderBridgeParent(const wr::PipelineId& aPipelineId,
const LayoutDeviceIntSize& aSize,
TextureFactoryIdentifier* aTextureFactoryIdentifier,
uint32_t *aIdNamespace)
{
#ifndef MOZ_BUILD_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<wr::WebRenderAPI> api = root->GetWebRenderAPI();
RefPtr<WebRenderCompositableHolder> holder = root->CompositableHolder();
parent = new WebRenderBridgeParent(this, aPipelineId, nullptr, root->CompositorScheduler(), Move(api), Move(holder));
parent->AddRef(); // IPDL reference
sIndirectLayerTrees[pipelineHandle].mCrossProcessParent = this;
sIndirectLayerTrees[pipelineHandle].mWrBridge = parent;
*aTextureFactoryIdentifier = parent->GetTextureFactoryIdentifier();
*aIdNamespace = parent->GetIdNameSpace();
return parent;
}
bool
CrossProcessCompositorBridgeParent::DeallocPWebRenderBridgeParent(PWebRenderBridgeParent* aActor)
{
#ifndef MOZ_BUILD_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<WebRenderBridgeParent*>(aActor);
EraseLayerState(parent->PipelineId().mHandle);
parent->Release(); // IPDL reference
return true;
}
mozilla::ipc::IPCResult
CrossProcessCompositorBridgeParent::RecvNotifyChildCreated(const uint64_t& child,
CompositorOptions* aOptions)
{
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);
*aOptions = lts->mParent->GetOptions();
return IPC_OK();
}
}
return IPC_FAIL_NO_REASON(this);
}
mozilla::ipc::IPCResult
CrossProcessCompositorBridgeParent::RecvMapAndNotifyChildCreated(const uint64_t& child,
const base::ProcessId& pid,
CompositorOptions* aOptions)
{
// This can only be called from the browser process, as the mapping
// ensures proper window ownership of layer trees.
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->FlushPendingTransactionIds(), aCompositeStart, aCompositeEnd);
}
}
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);
}
CompositorAnimationStorage*
CrossProcessCompositorBridgeParent::GetAnimationStorage(
const uint64_t& aId)
{
MOZ_ASSERT(aId != 0);
const CompositorBridgeParent::LayerTreeState* state =
CompositorBridgeParent::GetIndirectShadowTree(aId);
if (!state) {
return nullptr;
}
MOZ_ASSERT(state->mParent);
// GetAnimationStorage in CompositorBridgeParent expects id as 0
return state->mParent->GetAnimationStorage(0);
}
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<ScrollableLayerGuid>& 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