Source code

Revision control

Other Tools

/* -*- 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/. */
#ifndef mozilla_layers_APZUtils_h
#define mozilla_layers_APZUtils_h
// This file is for APZ-related utilities that are used by code in gfx/layers
// only. For APZ-related utilities used by the Rest of the World (widget/,
// layout/, dom/, IPDL protocols, etc.), use APZPublicUtils.h.
// Do not include this header from source files outside of gfx/layers.
#include <stdint.h> // for uint32_t
#include <type_traits>
#include "gfxTypes.h"
#include "FrameMetrics.h"
#include "LayersTypes.h"
#include "UnitTransforms.h"
#include "mozilla/gfx/CompositorHitTestInfo.h"
#include "mozilla/gfx/Point.h"
#include "mozilla/DefineEnum.h"
#include "mozilla/EnumSet.h"
#include "mozilla/FloatingPoint.h"
namespace mozilla {
namespace layers {
enum CancelAnimationFlags : uint32_t {
Default = 0x0, /* Cancel all animations */
ExcludeOverscroll = 0x1, /* Don't clear overscroll */
ScrollSnap = 0x2, /* Snap to snap points */
ExcludeWheel = 0x4, /* Don't stop wheel smooth-scroll animations */
TriggeredExternally = 0x8, /* Cancellation was not triggered by APZ in
response to an input event */
};
inline CancelAnimationFlags operator|(CancelAnimationFlags a,
CancelAnimationFlags b) {
return static_cast<CancelAnimationFlags>(static_cast<int>(a) |
static_cast<int>(b));
}
// clang-format off
enum class ScrollSource {
// Touch-screen.
Touchscreen,
// Touchpad with gesture support.
Touchpad,
// Mouse wheel.
Wheel,
// Keyboard
Keyboard,
};
// clang-format on
inline bool ScrollSourceRespectsDisregardedDirections(ScrollSource aSource) {
return aSource == ScrollSource::Wheel || aSource == ScrollSource::Touchpad;
}
inline bool ScrollSourceAllowsOverscroll(ScrollSource aSource) {
return aSource == ScrollSource::Touchpad ||
aSource == ScrollSource::Touchscreen;
}
// Epsilon to be used when comparing 'float' coordinate values
// with FuzzyEqualsAdditive. The rationale is that 'float' has 7 decimal
// digits of precision, and coordinate values should be no larger than in the
// ten thousands. Note also that the smallest legitimate difference in page
// coordinates is 1 app unit, which is 1/60 of a (CSS pixel), so this epsilon
// isn't too large.
const float COORDINATE_EPSILON = 0.02f;
template <typename Units>
static bool IsZero(const gfx::PointTyped<Units>& aPoint) {
return FuzzyEqualsAdditive(aPoint.x, 0.0f, COORDINATE_EPSILON) &&
FuzzyEqualsAdditive(aPoint.y, 0.0f, COORDINATE_EPSILON);
}
// Represents async transforms consisting of a scale and a translation.
struct AsyncTransform {
explicit AsyncTransform(
LayerToParentLayerScale aScale = LayerToParentLayerScale(),
ParentLayerPoint aTranslation = ParentLayerPoint())
: mScale(aScale), mTranslation(aTranslation) {}
operator AsyncTransformComponentMatrix() const {
return AsyncTransformComponentMatrix::Scaling(mScale.scale, mScale.scale, 1)
.PostTranslate(mTranslation.x, mTranslation.y, 0);
}
bool operator==(const AsyncTransform& rhs) const {
return mTranslation == rhs.mTranslation && mScale == rhs.mScale;
}
bool operator!=(const AsyncTransform& rhs) const { return !(*this == rhs); }
LayerToParentLayerScale mScale;
ParentLayerPoint mTranslation;
};
// Deem an AsyncTransformComponentMatrix (obtained by multiplying together
// one or more AsyncTransformComponentMatrix objects) as constituting a
// complete async transform.
inline AsyncTransformMatrix CompleteAsyncTransform(
const AsyncTransformComponentMatrix& aMatrix) {
return ViewAs<AsyncTransformMatrix>(
aMatrix, PixelCastJustification::MultipleAsyncTransforms);
}
struct TargetConfirmationFlags final {
explicit TargetConfirmationFlags(bool aTargetConfirmed)
: mTargetConfirmed(aTargetConfirmed),
mRequiresTargetConfirmation(false),
mHitScrollbar(false),
mHitScrollThumb(false),
mDispatchToContent(false) {}
explicit TargetConfirmationFlags(
const gfx::CompositorHitTestInfo& aHitTestInfo)
: mTargetConfirmed(
(aHitTestInfo != gfx::CompositorHitTestInvisibleToHit) &&
(aHitTestInfo & gfx::CompositorHitTestDispatchToContent).isEmpty()),
mRequiresTargetConfirmation(aHitTestInfo.contains(
gfx::CompositorHitTestFlags::eRequiresTargetConfirmation)),
mHitScrollbar(
aHitTestInfo.contains(gfx::CompositorHitTestFlags::eScrollbar)),
mHitScrollThumb(aHitTestInfo.contains(
gfx::CompositorHitTestFlags::eScrollbarThumb)),
mDispatchToContent(
!(aHitTestInfo & gfx::CompositorHitTestDispatchToContent)
.isEmpty()) {}
bool mTargetConfirmed : 1;
bool mRequiresTargetConfirmation : 1;
bool mHitScrollbar : 1;
bool mHitScrollThumb : 1;
bool mDispatchToContent : 1;
};
enum class AsyncTransformComponent { eLayout, eVisual };
using AsyncTransformComponents = EnumSet<AsyncTransformComponent>;
constexpr AsyncTransformComponents LayoutAndVisual(
AsyncTransformComponent::eLayout, AsyncTransformComponent::eVisual);
/**
* Metrics that GeckoView wants to know at every composite.
* These are the effective visual scroll offset and zoom level of
* the root content APZC at composition time.
*/
struct GeckoViewMetrics {
CSSPoint mVisualScrollOffset;
CSSToParentLayerScale mZoom;
};
namespace apz {
/**
* Is aAngle within the given threshold of the horizontal axis?
* @param aAngle an angle in radians in the range [0, pi]
* @param aThreshold an angle in radians in the range [0, pi/2]
*/
bool IsCloseToHorizontal(float aAngle, float aThreshold);
// As above, but for the vertical axis.
bool IsCloseToVertical(float aAngle, float aThreshold);
// Returns true if a sticky layer with async translation |aTranslation| is
// stuck with a bottom margin. The inner/outer ranges are produced by the main
// thread at the last paint, and so |aTranslation| only needs to be the
// async translation from the last paint.
bool IsStuckAtBottom(gfxFloat aTranslation,
const LayerRectAbsolute& aInnerRange,
const LayerRectAbsolute& aOuterRange);
// Returns true if a sticky layer with async translation |aTranslation| is
// stuck with a top margin.
bool IsStuckAtTop(gfxFloat aTranslation, const LayerRectAbsolute& aInnerRange,
const LayerRectAbsolute& aOuterRange);
/**
* Compute the translation that should be applied to a layer that's fixed
* at |eFixedSides|, to respect the fixed layer margins |aFixedMargins|.
*/
ScreenPoint ComputeFixedMarginsOffset(
const ScreenMargin& aCompositorFixedLayerMargins, SideBits aFixedSides,
const ScreenMargin& aGeckoFixedLayerMargins);
/**
* Takes the visible rect from the compositor metrics, adds a pref-based
* margin around it, and checks to see if it is contained inside the painted
* rect from the painted metrics. Returns true if it is contained, or false
* if not. Returning false means that a (relatively) small amount of async
* scrolling/zooming can result in the visible area going outside the painted
* area and resulting in visual checkerboarding.
* Note that this may return false positives for cases where the scrollframe
* in question is nested inside other scrollframes, as the composition bounds
* used to determine the visible rect may in fact be clipped by enclosing
* scrollframes, but that is not accounted for in this function.
*/
bool AboutToCheckerboard(const FrameMetrics& aPaintedMetrics,
const FrameMetrics& aCompositorMetrics);
/**
* Returns SideBits where the given |aOverscrollAmount| overscrolls.
*/
SideBits GetOverscrollSideBits(const ParentLayerPoint& aOverscrollAmount);
} // namespace apz
} // namespace layers
} // namespace mozilla
#endif // mozilla_layers_APZUtils_h