diff options
Diffstat (limited to 'gfx/layers/apz/util/ChromeProcessController.cpp')
-rw-r--r-- | gfx/layers/apz/util/ChromeProcessController.cpp | 361 |
1 files changed, 361 insertions, 0 deletions
diff --git a/gfx/layers/apz/util/ChromeProcessController.cpp b/gfx/layers/apz/util/ChromeProcessController.cpp new file mode 100644 index 0000000000..a836f0ecdc --- /dev/null +++ b/gfx/layers/apz/util/ChromeProcessController.cpp @@ -0,0 +1,361 @@ +/* -*- 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 "ChromeProcessController.h" + +#include "MainThreadUtils.h" // for NS_IsMainThread() +#include "base/task.h" +#include "mozilla/PresShell.h" +#include "mozilla/dom/Element.h" +#include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/layers/APZCCallbackHelper.h" +#include "mozilla/layers/APZEventState.h" +#include "mozilla/layers/APZThreadUtils.h" +#include "mozilla/layers/IAPZCTreeManager.h" +#include "mozilla/layers/InputAPZContext.h" +#include "mozilla/layers/DoubleTapToZoom.h" +#include "mozilla/layers/RepaintRequest.h" +#include "mozilla/dom/Document.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsLayoutUtils.h" +#include "nsView.h" + +static mozilla::LazyLogModule sApzChromeLog("apz.cc.chrome"); + +using namespace mozilla; +using namespace mozilla::layers; +using namespace mozilla::widget; + +ChromeProcessController::ChromeProcessController( + nsIWidget* aWidget, APZEventState* aAPZEventState, + IAPZCTreeManager* aAPZCTreeManager) + : mWidget(aWidget), + mAPZEventState(aAPZEventState), + mAPZCTreeManager(aAPZCTreeManager), + mUIThread(NS_GetCurrentThread()) { + // Otherwise we're initializing mUIThread incorrectly. + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aAPZEventState); + MOZ_ASSERT(aAPZCTreeManager); + + mUIThread->Dispatch( + NewRunnableMethod("layers::ChromeProcessController::InitializeRoot", this, + &ChromeProcessController::InitializeRoot)); +} + +ChromeProcessController::~ChromeProcessController() = default; + +void ChromeProcessController::InitializeRoot() { + APZCCallbackHelper::InitializeRootDisplayport(GetPresShell()); +} + +void ChromeProcessController::NotifyLayerTransforms( + nsTArray<MatrixMessage>&& aTransforms) { + if (!mUIThread->IsOnCurrentThread()) { + mUIThread->Dispatch( + NewRunnableMethod<StoreCopyPassByRRef<nsTArray<MatrixMessage>>>( + "layers::ChromeProcessController::NotifyLayerTransforms", this, + &ChromeProcessController::NotifyLayerTransforms, + std::move(aTransforms))); + return; + } + + APZCCallbackHelper::NotifyLayerTransforms(aTransforms); +} + +void ChromeProcessController::RequestContentRepaint( + const RepaintRequest& aRequest) { + MOZ_ASSERT(IsRepaintThread()); + + if (aRequest.IsRootContent()) { + APZCCallbackHelper::UpdateRootFrame(aRequest); + } else { + APZCCallbackHelper::UpdateSubFrame(aRequest); + } +} + +bool ChromeProcessController::IsRepaintThread() { return NS_IsMainThread(); } + +void ChromeProcessController::DispatchToRepaintThread( + already_AddRefed<Runnable> aTask) { + NS_DispatchToMainThread(std::move(aTask)); +} + +void ChromeProcessController::Destroy() { + if (!mUIThread->IsOnCurrentThread()) { + mUIThread->Dispatch( + NewRunnableMethod("layers::ChromeProcessController::Destroy", this, + &ChromeProcessController::Destroy)); + return; + } + + MOZ_ASSERT(mUIThread->IsOnCurrentThread()); + mWidget = nullptr; + if (mAPZEventState) { + mAPZEventState->Destroy(); + } + mAPZEventState = nullptr; +} + +PresShell* ChromeProcessController::GetPresShell() const { + if (!mWidget) { + return nullptr; + } + if (nsView* view = nsView::GetViewFor(mWidget)) { + return view->GetPresShell(); + } + return nullptr; +} + +dom::Document* ChromeProcessController::GetRootDocument() const { + if (PresShell* presShell = GetPresShell()) { + return presShell->GetDocument(); + } + return nullptr; +} + +dom::Document* ChromeProcessController::GetRootContentDocument( + const ScrollableLayerGuid::ViewID& aScrollId) const { + nsIContent* content = nsLayoutUtils::FindContentFor(aScrollId); + if (!content) { + return nullptr; + } + if (PresShell* presShell = + APZCCallbackHelper::GetRootContentDocumentPresShellForContent( + content)) { + return presShell->GetDocument(); + } + return nullptr; +} + +void ChromeProcessController::HandleDoubleTap( + const mozilla::CSSPoint& aPoint, Modifiers aModifiers, + const ScrollableLayerGuid& aGuid, + const DoubleTapToZoomMetrics& aDoubleTapToZoomMetrics) { + MOZ_LOG(sApzChromeLog, LogLevel::Debug, ("HandleDoubleTap\n")); + MOZ_ASSERT(mUIThread->IsOnCurrentThread()); + + RefPtr<dom::Document> document = GetRootContentDocument(aGuid.mScrollId); + if (!document.get()) { + return; + } + + ZoomTarget zoomTarget = + CalculateRectToZoomTo(document, aPoint, aDoubleTapToZoomMetrics); + + mAPZCTreeManager->ZoomToRect(aGuid, zoomTarget, + ZoomToRectBehavior::DEFAULT_BEHAVIOR); +} + +void ChromeProcessController::HandleTap( + TapType aType, const mozilla::LayoutDevicePoint& aPoint, + Modifiers aModifiers, const ScrollableLayerGuid& aGuid, + uint64_t aInputBlockId, + const Maybe<DoubleTapToZoomMetrics>& aDoubleTapToZoomMetrics) { + MOZ_LOG(sApzChromeLog, LogLevel::Debug, + ("HandleTap called with %d\n", (int)aType)); + if (!mUIThread->IsOnCurrentThread()) { + MOZ_LOG(sApzChromeLog, LogLevel::Debug, ("HandleTap redispatching\n")); + mUIThread->Dispatch( + NewRunnableMethod<TapType, mozilla::LayoutDevicePoint, Modifiers, + ScrollableLayerGuid, uint64_t, + Maybe<DoubleTapToZoomMetrics>>( + "layers::ChromeProcessController::HandleTap", this, + &ChromeProcessController::HandleTap, aType, aPoint, aModifiers, + aGuid, aInputBlockId, aDoubleTapToZoomMetrics)); + return; + } + + if (!mAPZEventState) { + return; + } + + RefPtr<PresShell> presShell = GetPresShell(); + if (!presShell) { + return; + } + if (!presShell->GetPresContext()) { + return; + } + CSSToLayoutDeviceScale scale( + presShell->GetPresContext()->CSSToDevPixelScale()); + + CSSPoint point = aPoint / scale; + + // Stash the guid in InputAPZContext so that when the visual-to-layout + // transform is applied to the event's coordinates, we use the right transform + // based on the scroll frame being targeted. + // The other values don't really matter. + InputAPZContext context(aGuid, aInputBlockId, nsEventStatus_eSentinel); + + switch (aType) { + case TapType::eSingleTap: { + RefPtr<APZEventState> eventState(mAPZEventState); + eventState->ProcessSingleTap(point, scale, aModifiers, 1, aInputBlockId); + break; + } + case TapType::eDoubleTap: + MOZ_ASSERT(aDoubleTapToZoomMetrics); + HandleDoubleTap(point, aModifiers, aGuid, *aDoubleTapToZoomMetrics); + break; + case TapType::eSecondTap: { + RefPtr<APZEventState> eventState(mAPZEventState); + eventState->ProcessSingleTap(point, scale, aModifiers, 2, aInputBlockId); + break; + } + case TapType::eLongTap: { + RefPtr<APZEventState> eventState(mAPZEventState); + eventState->ProcessLongTap(presShell, point, scale, aModifiers, + aInputBlockId); + break; + } + case TapType::eLongTapUp: { + RefPtr<APZEventState> eventState(mAPZEventState); + eventState->ProcessLongTapUp(presShell, point, scale, aModifiers); + break; + } + } +} + +void ChromeProcessController::NotifyPinchGesture( + PinchGestureInput::PinchGestureType aType, const ScrollableLayerGuid& aGuid, + const LayoutDevicePoint& aFocusPoint, LayoutDeviceCoord aSpanChange, + Modifiers aModifiers) { + if (!mUIThread->IsOnCurrentThread()) { + mUIThread->Dispatch( + NewRunnableMethod<PinchGestureInput::PinchGestureType, + ScrollableLayerGuid, LayoutDevicePoint, + LayoutDeviceCoord, Modifiers>( + "layers::ChromeProcessController::NotifyPinchGesture", this, + &ChromeProcessController::NotifyPinchGesture, aType, aGuid, + aFocusPoint, aSpanChange, aModifiers)); + return; + } + + if (mWidget) { + // Dispatch the call to APZCCallbackHelper::NotifyPinchGesture to the main + // thread so that it runs asynchronously from the current call. This is + // because the call can run arbitrary JS code, which can also spin the event + // loop and cause undesirable re-entrancy in APZ. + mUIThread->Dispatch(NewRunnableFunction( + "layers::ChromeProcessController::NotifyPinchGestureAsync", + &APZCCallbackHelper::NotifyPinchGesture, aType, aFocusPoint, + aSpanChange, aModifiers, mWidget)); + } +} + +void ChromeProcessController::NotifyAPZStateChange( + const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg, + Maybe<uint64_t> aInputBlockId) { + if (!mUIThread->IsOnCurrentThread()) { + mUIThread->Dispatch(NewRunnableMethod<ScrollableLayerGuid, APZStateChange, + int, Maybe<uint64_t>>( + "layers::ChromeProcessController::NotifyAPZStateChange", this, + &ChromeProcessController::NotifyAPZStateChange, aGuid, aChange, aArg, + aInputBlockId)); + return; + } + + if (!mAPZEventState) { + return; + } + + mAPZEventState->ProcessAPZStateChange(aGuid.mScrollId, aChange, aArg, + aInputBlockId); +} + +void ChromeProcessController::NotifyMozMouseScrollEvent( + const ScrollableLayerGuid::ViewID& aScrollId, const nsString& aEvent) { + if (!mUIThread->IsOnCurrentThread()) { + mUIThread->Dispatch( + NewRunnableMethod<ScrollableLayerGuid::ViewID, nsString>( + "layers::ChromeProcessController::NotifyMozMouseScrollEvent", this, + &ChromeProcessController::NotifyMozMouseScrollEvent, aScrollId, + aEvent)); + return; + } + + APZCCallbackHelper::NotifyMozMouseScrollEvent(aScrollId, aEvent); +} + +void ChromeProcessController::NotifyFlushComplete() { + MOZ_ASSERT(IsRepaintThread()); + + APZCCallbackHelper::NotifyFlushComplete(GetPresShell()); +} + +void ChromeProcessController::NotifyAsyncScrollbarDragInitiated( + uint64_t aDragBlockId, const ScrollableLayerGuid::ViewID& aScrollId, + ScrollDirection aDirection) { + if (!mUIThread->IsOnCurrentThread()) { + mUIThread->Dispatch(NewRunnableMethod<uint64_t, ScrollableLayerGuid::ViewID, + ScrollDirection>( + "layers::ChromeProcessController::NotifyAsyncScrollbarDragInitiated", + this, &ChromeProcessController::NotifyAsyncScrollbarDragInitiated, + aDragBlockId, aScrollId, aDirection)); + return; + } + + APZCCallbackHelper::NotifyAsyncScrollbarDragInitiated(aDragBlockId, aScrollId, + aDirection); +} + +void ChromeProcessController::NotifyAsyncScrollbarDragRejected( + const ScrollableLayerGuid::ViewID& aScrollId) { + if (!mUIThread->IsOnCurrentThread()) { + mUIThread->Dispatch(NewRunnableMethod<ScrollableLayerGuid::ViewID>( + "layers::ChromeProcessController::NotifyAsyncScrollbarDragRejected", + this, &ChromeProcessController::NotifyAsyncScrollbarDragRejected, + aScrollId)); + return; + } + + APZCCallbackHelper::NotifyAsyncScrollbarDragRejected(aScrollId); +} + +void ChromeProcessController::NotifyAsyncAutoscrollRejected( + const ScrollableLayerGuid::ViewID& aScrollId) { + if (!mUIThread->IsOnCurrentThread()) { + mUIThread->Dispatch(NewRunnableMethod<ScrollableLayerGuid::ViewID>( + "layers::ChromeProcessController::NotifyAsyncAutoscrollRejected", this, + &ChromeProcessController::NotifyAsyncAutoscrollRejected, aScrollId)); + return; + } + + APZCCallbackHelper::NotifyAsyncAutoscrollRejected(aScrollId); +} + +void ChromeProcessController::CancelAutoscroll( + const ScrollableLayerGuid& aGuid) { + if (!mUIThread->IsOnCurrentThread()) { + mUIThread->Dispatch(NewRunnableMethod<ScrollableLayerGuid>( + "layers::ChromeProcessController::CancelAutoscroll", this, + &ChromeProcessController::CancelAutoscroll, aGuid)); + return; + } + + APZCCallbackHelper::CancelAutoscroll(aGuid.mScrollId); +} + +void ChromeProcessController::NotifyScaleGestureComplete( + const ScrollableLayerGuid& aGuid, float aScale) { + if (!mUIThread->IsOnCurrentThread()) { + mUIThread->Dispatch(NewRunnableMethod<ScrollableLayerGuid, float>( + "layers::ChromeProcessController::NotifyScaleGestureComplete", this, + &ChromeProcessController::NotifyScaleGestureComplete, aGuid, aScale)); + return; + } + + if (mWidget) { + // Dispatch the call to APZCCallbackHelper::NotifyScaleGestureComplete + // to the main thread so that it runs asynchronously from the current call. + // This is because the call can run arbitrary JS code, which can also spin + // the event loop and cause undesirable re-entrancy in APZ. + mUIThread->Dispatch(NewRunnableFunction( + "layers::ChromeProcessController::NotifyScaleGestureComplete", + &APZCCallbackHelper::NotifyScaleGestureComplete, mWidget, aScale)); + } +} |