gecko/gfx/layers/composite/APZCTreeManager.cpp

338 lines
12 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "APZCTreeManager.h"
#include "Compositor.h"
namespace mozilla {
namespace layers {
APZCTreeManager::APZCTreeManager()
: mTreeLock("APZCTreeLock")
{
MOZ_ASSERT(NS_IsMainThread());
AsyncPanZoomController::InitializeGlobalState();
}
/* Flatten the tree of APZC instances into the given nsTArray */
static void
Collect(AsyncPanZoomController* aApzc, nsTArray< nsRefPtr<AsyncPanZoomController> >* aCollection)
{
if (aApzc) {
aCollection->AppendElement(aApzc);
Collect(aApzc->GetLastChild(), aCollection);
Collect(aApzc->GetPrevSibling(), aCollection);
}
}
void
APZCTreeManager::UpdatePanZoomControllerTree(CompositorParent* aCompositor, Layer* aRoot,
bool aIsFirstPaint, uint64_t aFirstPaintLayersId)
{
Compositor::AssertOnCompositorThread();
MonitorAutoLock lock(mTreeLock);
// We do this business with collecting the entire tree into an array because otherwise
// it's very hard to determine which APZC instances need to be destroyed. In the worst
// case, there are two scenarios: (a) a layer with an APZC is removed from the layer
// tree and (b) a layer with an APZC is moved in the layer tree from one place to a
// completely different place. In scenario (a) we would want to destroy the APZC while
// walking the layer tree and noticing that the layer/APZC is no longer there. But if
// we do that then we run into a problem in scenario (b) because we might encounter that
// layer later during the walk. To handle both of these we have to 'remember' that the
// layer was not found, and then do the destroy only at the end of the tree walk after
// we are sure that the layer was removed and not just transplanted elsewhere. Doing that
// as part of a recursive tree walk is hard and so maintaining a list and removing
// APZCs that are still alive is much simpler.
nsTArray< nsRefPtr<AsyncPanZoomController> > apzcsToDestroy;
Collect(mRootApzc, &apzcsToDestroy);
mRootApzc = nullptr;
if (aRoot) {
UpdatePanZoomControllerTree(aCompositor,
aRoot, CompositorParent::ROOT_LAYER_TREE_ID,
nullptr, nullptr,
aIsFirstPaint, aFirstPaintLayersId,
&apzcsToDestroy);
}
for (int i = apzcsToDestroy.Length() - 1; i >= 0; i--) {
apzcsToDestroy[i]->Destroy();
}
}
AsyncPanZoomController*
APZCTreeManager::UpdatePanZoomControllerTree(CompositorParent* aCompositor,
Layer* aLayer, uint64_t aLayersId,
AsyncPanZoomController* aParent,
AsyncPanZoomController* aNextSibling,
bool aIsFirstPaint, uint64_t aFirstPaintLayersId,
nsTArray< nsRefPtr<AsyncPanZoomController> >* aApzcsToDestroy)
{
ContainerLayer* container = aLayer->AsContainerLayer();
AsyncPanZoomController* controller = nullptr;
if (container) {
if (container->GetFrameMetrics().IsScrollable()) {
const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(aLayersId);
if (state && state->mController.get()) {
// If we get here, aLayer is a scrollable container layer and somebody
// has registered a GeckoContentController for it, so we need to ensure
// it has an APZC instance to manage its scrolling.
controller = container->GetAsyncPanZoomController();
if (!controller) {
controller = new AsyncPanZoomController(aLayersId, state->mController,
AsyncPanZoomController::USE_GESTURE_DETECTOR);
controller->SetCompositorParent(aCompositor);
} else {
// If there was already an APZC for the layer clear the tree pointers
// so that it doesn't continue pointing to APZCs that should no longer
// be in the tree. These pointers will get reset properly as we continue
// building the tree. Also remove it from the set of APZCs that are going
// to be destroyed, because it's going to remain active.
aApzcsToDestroy->RemoveElement(controller);
controller->SetPrevSibling(nullptr);
controller->SetLastChild(nullptr);
}
controller->NotifyLayersUpdated(container->GetFrameMetrics(),
aIsFirstPaint && (aLayersId == aFirstPaintLayersId));
// Bind the APZC instance into the tree of APZCs
if (aNextSibling) {
aNextSibling->SetPrevSibling(controller);
} else if (aParent) {
aParent->SetLastChild(controller);
} else {
mRootApzc = controller;
}
// Let this controller be the parent of other controllers when we recurse downwards
aParent = controller;
}
}
container->SetAsyncPanZoomController(controller);
}
uint64_t childLayersId = (aLayer->AsRefLayer() ? aLayer->AsRefLayer()->GetReferentId() : aLayersId);
AsyncPanZoomController* next = nullptr;
for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) {
next = UpdatePanZoomControllerTree(aCompositor, child, childLayersId, aParent, next,
aIsFirstPaint, aFirstPaintLayersId, aApzcsToDestroy);
}
// Return the APZC that should be the sibling of other APZCs as we continue
// moving towards the first child at this depth in the layer tree.
// If this layer doesn't have a controller, we promote any APZCs in the subtree
// upwards. Otherwise we fall back to the aNextSibling that was passed in.
if (controller) {
return controller;
}
if (next) {
return next;
}
return aNextSibling;
}
nsEventStatus
APZCTreeManager::ReceiveInputEvent(const InputData& aEvent)
{
nsRefPtr<AsyncPanZoomController> apzc;
switch (aEvent.mInputType) {
case MULTITOUCH_INPUT: {
const MultiTouchInput& multiTouchInput = aEvent.AsMultiTouchInput();
apzc = GetTargetAPZC(ScreenPoint(multiTouchInput.mTouches[0].mScreenPoint));
break;
} case PINCHGESTURE_INPUT: {
const PinchGestureInput& pinchInput = aEvent.AsPinchGestureInput();
apzc = GetTargetAPZC(pinchInput.mFocusPoint);
break;
} case TAPGESTURE_INPUT: {
const TapGestureInput& tapInput = aEvent.AsTapGestureInput();
apzc = GetTargetAPZC(ScreenPoint(tapInput.mPoint));
break;
} default: {
// leave apzc as nullptr
break;
}
}
if (apzc) {
return apzc->ReceiveInputEvent(aEvent);
}
return nsEventStatus_eIgnore;
}
nsEventStatus
APZCTreeManager::ReceiveInputEvent(const nsInputEvent& aEvent,
nsInputEvent* aOutEvent)
{
MOZ_ASSERT(NS_IsMainThread());
nsRefPtr<AsyncPanZoomController> apzc;
switch (aEvent.eventStructType) {
case NS_TOUCH_EVENT: {
const nsTouchEvent& touchEvent = static_cast<const nsTouchEvent&>(aEvent);
if (touchEvent.touches.Length() > 0) {
nsIntPoint point = touchEvent.touches[0]->mRefPoint;
apzc = GetTargetAPZC(ScreenPoint::FromUnknownPoint(gfx::Point(point.x, point.y)));
}
break;
} case NS_MOUSE_EVENT: {
const nsMouseEvent& mouseEvent = static_cast<const nsMouseEvent&>(aEvent);
apzc = GetTargetAPZC(ScreenPoint::FromUnknownPoint(gfx::Point(mouseEvent.refPoint.x,
mouseEvent.refPoint.y)));
break;
} default: {
// leave apzc as nullptr
break;
}
}
if (apzc) {
return apzc->ReceiveInputEvent(aEvent, aOutEvent);
}
return nsEventStatus_eIgnore;
}
void
APZCTreeManager::UpdateCompositionBounds(const ScrollableLayerGuid& aGuid,
const ScreenIntRect& aCompositionBounds)
{
nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
if (apzc) {
apzc->UpdateCompositionBounds(aCompositionBounds);
}
}
void
APZCTreeManager::CancelDefaultPanZoom(const ScrollableLayerGuid& aGuid)
{
nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
if (apzc) {
apzc->CancelDefaultPanZoom();
}
}
void
APZCTreeManager::DetectScrollableSubframe(const ScrollableLayerGuid& aGuid)
{
nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
if (apzc) {
apzc->DetectScrollableSubframe();
}
}
void
APZCTreeManager::ZoomToRect(const ScrollableLayerGuid& aGuid,
const CSSRect& aRect)
{
nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
if (apzc) {
apzc->ZoomToRect(aRect);
}
}
void
APZCTreeManager::ContentReceivedTouch(const ScrollableLayerGuid& aGuid,
bool aPreventDefault)
{
nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
if (apzc) {
apzc->ContentReceivedTouch(aPreventDefault);
}
}
void
APZCTreeManager::UpdateZoomConstraints(const ScrollableLayerGuid& aGuid,
bool aAllowZoom,
float aMinScale,
float aMaxScale)
{
nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
if (apzc) {
apzc->UpdateZoomConstraints(aAllowZoom, aMinScale, aMaxScale);
}
}
void
APZCTreeManager::UpdateScrollOffset(const ScrollableLayerGuid& aGuid,
const CSSPoint& aScrollOffset)
{
nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
if (apzc) {
apzc->UpdateScrollOffset(aScrollOffset);
}
}
void
APZCTreeManager::CancelAnimation(const ScrollableLayerGuid &aGuid)
{
nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
if (apzc) {
apzc->CancelAnimation();
}
}
void
APZCTreeManager::ClearTree()
{
MonitorAutoLock lock(mTreeLock);
// This can be done as part of a tree walk but it's easier to
// just re-use the Collect method that we need in other places.
// If this is too slow feel free to change it to a recursive walk.
nsTArray< nsRefPtr<AsyncPanZoomController> > apzcsToDestroy;
Collect(mRootApzc, &apzcsToDestroy);
for (int i = apzcsToDestroy.Length() - 1; i >= 0; i--) {
apzcsToDestroy[i]->Destroy();
}
mRootApzc = nullptr;
}
already_AddRefed<AsyncPanZoomController>
APZCTreeManager::GetTargetAPZC(const ScrollableLayerGuid& aGuid)
{
MonitorAutoLock lock(mTreeLock);
nsRefPtr<AsyncPanZoomController> target;
// The root may have siblings, check those too
for (AsyncPanZoomController* apzc = mRootApzc; apzc; apzc = apzc->GetPrevSibling()) {
target = FindTargetAPZC(apzc, aGuid);
if (target) {
break;
}
}
return target.forget();
}
already_AddRefed<AsyncPanZoomController>
APZCTreeManager::GetTargetAPZC(const ScreenPoint& aPoint)
{
MonitorAutoLock lock(mTreeLock);
// TODO: Do a hit test on the tree of
// APZC instances and return the right one.
return nullptr;
}
AsyncPanZoomController*
APZCTreeManager::FindTargetAPZC(AsyncPanZoomController* aApzc, const ScrollableLayerGuid& aGuid) {
// This walks the tree in depth-first, reverse order, so that it encounters
// APZCs front-to-back on the screen.
for (AsyncPanZoomController* child = aApzc->GetLastChild(); child; child = child->GetPrevSibling()) {
AsyncPanZoomController* match = FindTargetAPZC(child, aGuid);
if (match) {
return match;
}
}
if (aApzc->Matches(aGuid)) {
return aApzc;
}
return nullptr;
}
}
}