summaryrefslogtreecommitdiffstats
path: root/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp')
-rw-r--r--gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp578
1 files changed, 578 insertions, 0 deletions
diff --git a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
new file mode 100644
index 000000000..1bb6d046b
--- /dev/null
+++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
@@ -0,0 +1,578 @@
+/* -*- 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 <stdio.h> // for fprintf, stdout
+#include <stdint.h> // for uint64_t
+#include <map> // for _Rb_tree_iterator, etc
+#include <utility> // for pair
+#include "LayerTransactionParent.h" // for LayerTransactionParent
+#include "RenderTrace.h" // for RenderTraceLayers
+#include "base/message_loop.h" // for MessageLoop
+#include "base/process.h" // for ProcessId
+#include "base/task.h" // for CancelableTask, etc
+#include "base/thread.h" // for Thread
+#include "gfxContext.h" // for gfxContext
+#include "gfxPlatform.h" // for gfxPlatform
+#include "TreeTraversal.h" // for ForEachNode
+#ifdef MOZ_WIDGET_GTK
+#include "gfxPlatformGtk.h" // for gfxPlatform
+#endif
+#include "gfxPrefs.h" // for gfxPrefs
+#include "mozilla/AutoRestore.h" // for AutoRestore
+#include "mozilla/ClearOnShutdown.h" // for ClearOnShutdown
+#include "mozilla/DebugOnly.h" // for DebugOnly
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/TabParent.h"
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Rect.h" // for IntSize
+#include "VRManager.h" // for VRManager
+#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/BasicCompositor.h" // for BasicCompositor
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/CompositorOGL.h" // for CompositorOGL
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/FrameUniformityData.h"
+#include "mozilla/layers/ImageBridgeParent.h"
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/layers/LayerTreeOwnerTracker.h"
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/layers/PLayerTransactionParent.h"
+#include "mozilla/layers/RemoteContentController.h"
+#include "mozilla/layout/RenderFrameParent.h"
+#include "mozilla/media/MediaSystemResourceService.h" // for MediaSystemResourceService
+#include "mozilla/mozalloc.h" // for operator new, etc
+#include "mozilla/Telemetry.h"
+#ifdef MOZ_WIDGET_GTK
+#include "basic/X11BasicCompositor.h" // for X11BasicCompositor
+#endif
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION, etc
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsIWidget.h" // for nsIWidget
+#include "nsTArray.h" // for nsTArray
+#include "nsThreadUtils.h" // for NS_IsMainThread
+#include "nsXULAppAPI.h" // for XRE_GetIOMessageLoop
+#ifdef XP_WIN
+#include "mozilla/layers/CompositorD3D11.h"
+#include "mozilla/layers/CompositorD3D9.h"
+#endif
+#include "GeckoProfiler.h"
+#include "mozilla/ipc/ProtocolTypes.h"
+#include "mozilla/Unused.h"
+#include "mozilla/Hal.h"
+#include "mozilla/HalTypes.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Telemetry.h"
+#ifdef MOZ_ENABLE_PROFILER_SPS
+#include "ProfilerMarkers.h"
+#endif
+#include "mozilla/VsyncDispatcher.h"
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+#include "VsyncSource.h"
+#endif
+#include "mozilla/widget/CompositorWidget.h"
+#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING
+# include "mozilla/widget/CompositorWidgetParent.h"
+#endif
+
+#include "LayerScope.h"
+
+namespace mozilla {
+
+namespace layers {
+
+// defined in CompositorBridgeParent.cpp
+typedef map<uint64_t, CompositorBridgeParent::LayerTreeState> LayerTreeMap;
+extern LayerTreeMap sIndirectLayerTrees;
+extern StaticAutoPtr<mozilla::Monitor> sIndirectLayerTreesLock;
+
+bool
+CrossProcessCompositorBridgeParent::RecvRequestNotifyAfterRemotePaint()
+{
+ mNotifyAfterRemotePaint = true;
+ return true;
+}
+
+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;
+ LayerManagerComposite* lm = state->mLayerManager;
+ *aTextureFactoryIdentifier = lm->GetCompositor()->GetTextureFactoryIdentifier();
+ *aSuccess = true;
+ LayerTransactionParent* p = new LayerTransactionParent(lm, this, aId);
+ p->AddIPDLReference();
+ sIndirectLayerTrees[aId].mLayerTree = p;
+ p->SetPendingCompositorUpdates(state->mPendingCompositorUpdates);
+ 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;
+}
+
+bool
+CrossProcessCompositorBridgeParent::RecvAsyncPanZoomEnabled(const uint64_t& aLayersId, bool* aHasAPZ)
+{
+ // 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 RecvAsyncPanZoomEnabled; dropping message...");
+ return false;
+ }
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ CompositorBridgeParent::LayerTreeState& state = sIndirectLayerTrees[aLayersId];
+
+ *aHasAPZ = state.mParent ? state.mParent->AsyncPanZoomEnabled() : false;
+ return true;
+}
+
+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;
+}
+
+bool
+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 true;
+ }
+ }
+ return false;
+}
+
+void
+CrossProcessCompositorBridgeParent::ShadowLayersUpdated(
+ LayerTransactionParent* aLayerTree,
+ const uint64_t& aTransactionId,
+ const TargetConfig& aTargetConfig,
+ const InfallibleTArray<PluginWindowData>& aPlugins,
+ bool aIsFirstPaint,
+ bool aScheduleComposite,
+ uint32_t aPaintSequenceNumber,
+ bool aIsRepeatTransaction,
+ int32_t /*aPaintSyncId: unused*/,
+ 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(aTargetConfig, aIsFirstPaint);
+
+ Layer* shadowRoot = aLayerTree->GetRoot();
+ if (shadowRoot) {
+ CompositorBridgeParent::SetShadowProperties(shadowRoot);
+ }
+ UpdateIndirectTree(id, shadowRoot, aTargetConfig);
+
+ // Cache the plugin data for this remote layer tree
+ state->mPluginData = aPlugins;
+ state->mUpdatedPluginDataAvailable = true;
+
+ state->mParent->NotifyShadowTreeTransaction(id, aIsFirstPaint, aScheduleComposite,
+ aPaintSequenceNumber, aIsRepeatTransaction, 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(aTransactionId);
+}
+
+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);
+ }
+}
+
+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<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);
+}
+
+bool
+CrossProcessCompositorBridgeParent::RecvAcknowledgeCompositorUpdate(const uint64_t& aLayersId)
+{
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ CompositorBridgeParent::LayerTreeState& state = sIndirectLayerTrees[aLayersId];
+
+ if (LayerTransactionParent* ltp = state.mLayerTree) {
+ ltp->AcknowledgeCompositorUpdate();
+ }
+ MOZ_ASSERT(state.mPendingCompositorUpdates > 0);
+ state.mPendingCompositorUpdates--;
+ return true;
+}
+
+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->mPendingCompositorUpdates) {
+ // 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();
+}
+
+bool
+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 true;
+}
+
+bool
+CrossProcessCompositorBridgeParent::RecvNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
+ const CSSIntRegion& aRegion)
+{
+ CompositorBridgeParent* parent;
+ { // scope lock
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ parent = sIndirectLayerTrees[aGuid.mLayersId].mParent;
+ }
+ if (parent) {
+ return parent->RecvNotifyApproximatelyVisibleRegion(aGuid, aRegion);
+ }
+ return true;
+}
+
+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);
+}
+
+} // namespace layers
+} // namespace mozilla