/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 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/UiCompositorControllerChild.h" #include "mozilla/dom/ContentChild.h" #include "mozilla/layers/CompositorThread.h" #include "mozilla/layers/SynchronousTask.h" #include "mozilla/layers/UiCompositorControllerMessageTypes.h" #include "mozilla/layers/UiCompositorControllerParent.h" #include "mozilla/gfx/GPUProcessManager.h" #include "mozilla/ipc/Endpoint.h" #include "mozilla/StaticPtr.h" #include "nsBaseWidget.h" #include "nsProxyRelease.h" #include "nsThreadUtils.h" #if defined(MOZ_WIDGET_ANDROID) # include "mozilla/widget/AndroidUiThread.h" static RefPtr GetUiThread() { return mozilla::GetAndroidUiThread(); } #else static RefPtr GetUiThread() { MOZ_CRASH("Platform does not support UiCompositorController"); return nullptr; } #endif // defined(MOZ_WIDGET_ANDROID) namespace mozilla { namespace layers { // public: /* static */ RefPtr UiCompositorControllerChild::CreateForSameProcess( const LayersId& aRootLayerTreeId, nsBaseWidget* aWidget) { RefPtr child = new UiCompositorControllerChild(0, aWidget); child->mParent = new UiCompositorControllerParent(aRootLayerTreeId); GetUiThread()->Dispatch( NewRunnableMethod( "layers::UiCompositorControllerChild::OpenForSameProcess", child, &UiCompositorControllerChild::OpenForSameProcess), nsIThread::DISPATCH_NORMAL); return child; } /* static */ RefPtr UiCompositorControllerChild::CreateForGPUProcess( const uint64_t& aProcessToken, Endpoint&& aEndpoint, nsBaseWidget* aWidget) { RefPtr child = new UiCompositorControllerChild(aProcessToken, aWidget); RefPtr task = NewRunnableMethod&&>( "layers::UiCompositorControllerChild::OpenForGPUProcess", child, &UiCompositorControllerChild::OpenForGPUProcess, std::move(aEndpoint)); GetUiThread()->Dispatch(task.forget(), nsIThread::DISPATCH_NORMAL); return child; } bool UiCompositorControllerChild::Pause() { if (!mIsOpen) { return false; } return SendPause(); } bool UiCompositorControllerChild::Resume() { if (!mIsOpen) { return false; } bool resumed = false; return SendResume(&resumed) && resumed; } bool UiCompositorControllerChild::ResumeAndResize(const int32_t& aX, const int32_t& aY, const int32_t& aWidth, const int32_t& aHeight) { if (!mIsOpen) { mResize = Some(gfx::IntRect(aX, aY, aWidth, aHeight)); // Since we are caching these values, pretend the call succeeded. return true; } bool resumed = false; return SendResumeAndResize(aX, aY, aWidth, aHeight, &resumed) && resumed; } bool UiCompositorControllerChild::InvalidateAndRender() { if (!mIsOpen) { return false; } return SendInvalidateAndRender(); } bool UiCompositorControllerChild::SetMaxToolbarHeight(const int32_t& aHeight) { if (!mIsOpen) { mMaxToolbarHeight = Some(aHeight); // Since we are caching this value, pretend the call succeeded. return true; } return SendMaxToolbarHeight(aHeight); } bool UiCompositorControllerChild::SetFixedBottomOffset(int32_t aOffset) { return SendFixedBottomOffset(aOffset); } bool UiCompositorControllerChild::ToolbarAnimatorMessageFromUI( const int32_t& aMessage) { if (!mIsOpen) { return false; } if (aMessage == IS_COMPOSITOR_CONTROLLER_OPEN) { RecvToolbarAnimatorMessageFromCompositor(COMPOSITOR_CONTROLLER_OPEN); } return true; } bool UiCompositorControllerChild::SetDefaultClearColor(const uint32_t& aColor) { if (!mIsOpen) { mDefaultClearColor = Some(aColor); // Since we are caching this value, pretend the call succeeded. return true; } return SendDefaultClearColor(aColor); } bool UiCompositorControllerChild::RequestScreenPixels() { if (!mIsOpen) { return false; } return SendRequestScreenPixels(); } bool UiCompositorControllerChild::EnableLayerUpdateNotifications( const bool& aEnable) { if (!mIsOpen) { mLayerUpdateEnabled = Some(aEnable); // Since we are caching this value, pretend the call succeeded. return true; } return SendEnableLayerUpdateNotifications(aEnable); } void UiCompositorControllerChild::Destroy() { MOZ_ASSERT(NS_IsMainThread()); layers::SynchronousTask task("UiCompositorControllerChild::Destroy"); GetUiThread()->Dispatch(NS_NewRunnableFunction( "layers::UiCompositorControllerChild::Destroy", [&]() { MOZ_ASSERT(GetUiThread()->IsOnCurrentThread()); AutoCompleteTask complete(&task); // Clear the process token so that we don't notify the GPUProcessManager // about an abnormal shutdown, thereby tearing down the GPU process. mProcessToken = 0; if (mWidget) { // Dispatch mWidget to main thread to prevent it from being destructed // by the ui thread. RefPtr widget = std::move(mWidget); NS_ReleaseOnMainThread("UiCompositorControllerChild::mWidget", widget.forget()); } if (mIsOpen) { // Close the underlying IPC channel. PUiCompositorControllerChild::Close(); mIsOpen = false; } })); task.Wait(); } bool UiCompositorControllerChild::DeallocPixelBuffer(Shmem& aMem) { return DeallocShmem(aMem); } // protected: void UiCompositorControllerChild::ActorDestroy(ActorDestroyReason aWhy) { mIsOpen = false; mParent = nullptr; if (mProcessToken) { gfx::GPUProcessManager::Get()->NotifyRemoteActorDestroyed(mProcessToken); mProcessToken = 0; } } void UiCompositorControllerChild::ProcessingError(Result aCode, const char* aReason) { if (aCode != MsgDropped) { gfxDevCrash(gfx::LogReason::ProcessingError) << "Processing error in UiCompositorControllerChild: " << int(aCode); } } void UiCompositorControllerChild::HandleFatalError(const char* aMsg) { dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid()); } mozilla::ipc::IPCResult UiCompositorControllerChild::RecvToolbarAnimatorMessageFromCompositor( const int32_t& aMessage) { #if defined(MOZ_WIDGET_ANDROID) if (mWidget) { mWidget->RecvToolbarAnimatorMessageFromCompositor(aMessage); } #endif // defined(MOZ_WIDGET_ANDROID) return IPC_OK(); } mozilla::ipc::IPCResult UiCompositorControllerChild::RecvRootFrameMetrics( const ScreenPoint& aScrollOffset, const CSSToScreenScale& aZoom) { #if defined(MOZ_WIDGET_ANDROID) if (mWidget) { mWidget->UpdateRootFrameMetrics(aScrollOffset, aZoom); } #endif // defined(MOZ_WIDGET_ANDROID) return IPC_OK(); } mozilla::ipc::IPCResult UiCompositorControllerChild::RecvScreenPixels( ipc::Shmem&& aMem, const ScreenIntSize& aSize, bool aNeedsYFlip) { #if defined(MOZ_WIDGET_ANDROID) if (mWidget) { mWidget->RecvScreenPixels(std::move(aMem), aSize, aNeedsYFlip); } #endif // defined(MOZ_WIDGET_ANDROID) return IPC_OK(); } // private: UiCompositorControllerChild::UiCompositorControllerChild( const uint64_t& aProcessToken, nsBaseWidget* aWidget) : mIsOpen(false), mProcessToken(aProcessToken), mWidget(aWidget) {} UiCompositorControllerChild::~UiCompositorControllerChild() = default; void UiCompositorControllerChild::OpenForSameProcess() { MOZ_ASSERT(GetUiThread()->IsOnCurrentThread()); mIsOpen = Open(mParent, mozilla::layers::CompositorThread(), mozilla::ipc::ChildSide); if (!mIsOpen) { mParent = nullptr; return; } mParent->InitializeForSameProcess(); SendCachedValues(); // Let Ui thread know the connection is open; RecvToolbarAnimatorMessageFromCompositor(COMPOSITOR_CONTROLLER_OPEN); } void UiCompositorControllerChild::OpenForGPUProcess( Endpoint&& aEndpoint) { MOZ_ASSERT(GetUiThread()->IsOnCurrentThread()); mIsOpen = aEndpoint.Bind(this); if (!mIsOpen) { // The GPU Process Manager might be gone if we receive ActorDestroy very // late in shutdown. if (gfx::GPUProcessManager* gpm = gfx::GPUProcessManager::Get()) { gpm->NotifyRemoteActorDestroyed(mProcessToken); } return; } SendCachedValues(); // Let Ui thread know the connection is open; RecvToolbarAnimatorMessageFromCompositor(COMPOSITOR_CONTROLLER_OPEN); } void UiCompositorControllerChild::SendCachedValues() { MOZ_ASSERT(mIsOpen); if (mResize) { bool resumed; SendResumeAndResize(mResize.ref().x, mResize.ref().y, mResize.ref().width, mResize.ref().height, &resumed); mResize.reset(); } if (mMaxToolbarHeight) { SendMaxToolbarHeight(mMaxToolbarHeight.ref()); mMaxToolbarHeight.reset(); } if (mDefaultClearColor) { SendDefaultClearColor(mDefaultClearColor.ref()); mDefaultClearColor.reset(); } if (mLayerUpdateEnabled) { SendEnableLayerUpdateNotifications(mLayerUpdateEnabled.ref()); mLayerUpdateEnabled.reset(); } } #ifdef MOZ_WIDGET_ANDROID void UiCompositorControllerChild::SetCompositorSurfaceManager( java::CompositorSurfaceManager::Param aCompositorSurfaceManager) { MOZ_ASSERT(!mCompositorSurfaceManager, "SetCompositorSurfaceManager must only be called once."); MOZ_ASSERT(mProcessToken != 0, "SetCompositorSurfaceManager must only be called for GPU process " "controllers."); mCompositorSurfaceManager = aCompositorSurfaceManager; }; void UiCompositorControllerChild::OnCompositorSurfaceChanged( int32_t aWidgetId, java::sdk::Surface::Param aSurface) { // If mCompositorSurfaceManager is not set then there is no GPU process and // we do not need to do anything. if (mCompositorSurfaceManager == nullptr) { return; } nsresult result = mCompositorSurfaceManager->OnSurfaceChanged(aWidgetId, aSurface); // If our remote binder has died then notify the GPU process manager. if (NS_FAILED(result)) { if (mProcessToken) { gfx::GPUProcessManager::Get()->NotifyRemoteActorDestroyed(mProcessToken); mProcessToken = 0; } } } #endif } // namespace layers } // namespace mozilla