mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:30:27 +00:00
729a46113b
- Bug 1159056. Don't store a value in CSS pixels in a variable with type nscoord in ScrollFrameHelper::SetCoordAttribute. r=dholbert (432f06158) - Bug 1138442 - Disable reftests on Mulet in parity with B2G/B2G Desktop. r=ahal (8b5c55da7) - Don't clip in ComputeFrameMetrics if the scrollframe won't be layerized. (bug 1148582 part 2, r=tn) (088b6ef29) - Bug 1157579 - Make sure the visual scroll port size includes the area of scrollbars when used in TryLayout. r=tn (c35244374) - Bug 1139306: Fix margin sides for right-to-left scrollbars depending on the actual position of the scrollbar as determined by layout.scrollbar.side, r=tn (34328312d) - Fix some full zoom FrameMetrics changes not propagating to APZ. (bug 1147648, r=botond) (91f009ac4) - Bug 1159405 - Fix "Found a non-root APZ with no handoff parent". r=botond (09b0ddf07) - Bug 1161040 - Don't set a scrollparent on the root content document in the child process that points to itself. r=tn (d0dfbffb0) - Bug 1161040 - Add some assertions to guard against infinite loops in the handoff chains. r=botond (e6bae82e8) - Bug 1153539 patch 1 - Improve comments for nsLayoutUtils::ComputeSuitableScaleForAnimation. r=roc (1b2bc00cd) - Bug 1153539 patch 2 - Compute scale for rasterizing off-main-thread transform animations based on ratio to display size in addition to maximum and minimum scale. r=roc (97d632d0f) - Bug 1160250 - Extract a helper method from the triplicated comp-bounds function to determine if we need to deflate the scrollbars. r=botond,tn (8d79c845f) - Bug 1160250 - Extract a helper function from the triplicated composition-bounds-updating code. r=botond,tn (5b3f71329) - Bug 1160250 - Collapse the code from CalculateRootCompositionSize into UpdateCompositionBounds. r=botond,tn (db0f081d6) - Bug 1160250 - Put in a missing transfomToAncestor scaling factor. r=botond,tn (14837aa3a) - Bug 1160250 - Fix up the rect being used for the composition size calculation. r=tn (0ba58021f) - Bug 1139575 - Reset velocity on axes when going from a pan to a pinch. r=botond (2e03d04ab) - Bug 1136971 - Draw layer borders correctly in the presence of multi-FrameMetrics. r=kats (e72f5b171) - Bug 1148871 - Only apply checkerboarding background color to opaque layers. r=botond (20e070b84) - Bug 1157327 - Don't cull out container layers, because their prepared data might be null and cause crashes. r=nical (3d06c925f) - Bug 1157327 follow-up to add a comment. r=me and DONTBUILD (4cd74c0a1) - Bug 1160566 - Make FrameMetrics.mCompositionBounds private and add a getter/setter for it. r=botond (277dd9afd) - Bug 1160566 - Change some return values to be reference-to-const. r=botond (2ce17b63f) - Bug 1158933 - Prevent scrollbars from jumping while zoomed in the B2G browser. r=botond (eca8aae7d) - Bug 1158933 - Replace the compositedHeight/scrollableHeight (and width) term with the adjusted scrollThumbRatio. r=botond (236af4c4a) - Bug 1156401 - Fix painting when building fennec with apzc. r=kats, r=botond (be18764b9) - Bug 1159305 - patch 1 - Provide logical accessors for nsStylePosition and nsStyleSides fields. r=dbaron (7a90c519d) - Bug 1159305 - patch 2 - Clean up layout code by using the new accessors. r=smontagu (926eb7dd5) - Bug 1164406 - Expose scrollbar-area deflation in nsLayoutUtils. r=kats (42e5e8784) - Bug 1139895 - Add fuzz for 1062792-1.html. r=tn (e38d0ac73) - Bug 1139893 - Disable reftest bugs/1062108-1.html on mulet. r=jfkthame (930590d73) - Move the APZ scroll frame clip onto FrameMetrics. (bug 1148582 part 3, r=mstange,tn) (c8e05f8d7) - Fix PostprocessRetainedLayers to not test occlusion with asynchronous clips. (bug 1148582 part 5, r=tn) (cc71cafe2) - Bug 1146626 - Refresh the reftest snapshot after adjusting the async scroll offsets. r=roc (1f041955d) - Bug 1160642. Add reftest-async-zoom to apply an async zoom before taking snapshot. r=dbaron (1e8e54d8d) - Bug 1160285 - Add a staticruntime version of mfbt and link some stuff against it. r=glandium (c733c49c7) - Bug 786520 - Install things to $(DIST)/branding from moz.build instead of manual rules in Makefile.ins. r=mshal (42d38eb26) - Bug 1155776 - move USE_EXTENSION_MANIFEST to moz.build; r=mshal (a87ecc2ca) - Bug 1155816 - part 0 - remove NO_JS_MANIFEST from testing/mochitest/Makefile.in; r=mshal (af90a362c) - Bug 1155816 - part 1 - move NO_JS_MANIFEST to moz.build; r=mshal (3a55e99a0) - Bug 978591 - Remove per-directory uses of MOZ_CHROME_FILE_FORMAT. r=glandium (5084d62b5) - Bug 991983 - Set GARBAGE for GeneratedSources in the recursivemake backend. r=gps (b8ae57c14) - Bug 991983 - Emit absolute paths for UnifiedSources. r=gps (4100640d0) - Bug 991983 - Emit absolute paths for other sources. r=gps (f30ddd773) - Bug 991983 - Remove commented code in gyp_reader.py. r=gps (0685eca6a) - Bug 1167474 - Remove ELFHACK_BUILD hack. r=mshal (b9d56c0b2) - Bug 1097804 - Part 1 - Create a library containing nsISocketTransportService and nsIDNS that can be used to support standalone WebRTC. r=mcmanus (d2f83eb1c) - Bug 1097804 - Part 2 - Added xpcomrt build of library containing unicode util functions. r=gps (fa9306f34) - Bug 1162852 - Remove EXTRA_COMPILE_FLAGS. r=gps (340312bc6) - Bug 1162852 - Remove EXTRA_ASSEMBLER_FLAGS. r=gps (f124c902c) - Bug 1162852 - Remove the VARIABLES method of GypContext now it is doing nothing. r=gps (b6c1c0246) - accidental commit (a1bbb1ada) - Bug 991983 - Define SOURCES as SourcePath. r=gps (678065bce) - Bug 991983 - Use objdir-relative SOURCES instead of GENERATED_SOURCES. r=gps (b54db0304) - Bug 991983 - Make TEST_HARNESS_FILES use the *Path classes instead of a separate set of methods to resolve paths. r=gps (025fe1ca9) - Bug 991983 - Add a deprecation hint for GENERATED_SOURCES. r=gps (67c75f4ab) - Bug 1172800 - Fixup after bug 991983. r=gps (ce6e8c7a3) - Bug 1164218 - Allow running individual mochitests and reftests in chaos mode. r=roc,froydnj (f2ec1d9d8) - Bug 1154231 - Part 1. Use LRU to manage remote layers. r=mattwoodrow (1fe47e64f) - Bug 1128037 - Minimalist support for wildcards in jar manifests. r=gps (1b07a6ffc) - Bug 1162569 - default engine files should be in the omni.ja file, r=markh,glandium. (b07e54881) - Bug 1157279. Escaping CSS identifiers should use lowercase letters for hex digits, not uppercase ones. r=dbaron (6f220936e)
1153 lines
48 KiB
C++
1153 lines
48 KiB
C++
/* -*- 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/AsyncCompositionManager.h"
|
|
#include <stdint.h> // for uint32_t
|
|
#include "apz/src/AsyncPanZoomController.h"
|
|
#include "FrameMetrics.h" // for FrameMetrics
|
|
#include "LayerManagerComposite.h" // for LayerManagerComposite, etc
|
|
#include "Layers.h" // for Layer, ContainerLayer, etc
|
|
#include "gfxPoint.h" // for gfxPoint, gfxSize
|
|
#include "mozilla/StyleAnimationValue.h" // for StyleAnimationValue, etc
|
|
#include "mozilla/WidgetUtils.h" // for ComputeTransformForRotation
|
|
#include "mozilla/dom/KeyframeEffect.h" // for KeyframeEffectReadOnly
|
|
#include "mozilla/gfx/BaseRect.h" // for BaseRect
|
|
#include "mozilla/gfx/Point.h" // for RoundedToInt, PointTyped
|
|
#include "mozilla/gfx/Rect.h" // for RoundedToInt, RectTyped
|
|
#include "mozilla/gfx/ScaleFactor.h" // for ScaleFactor
|
|
#include "mozilla/layers/Compositor.h" // for Compositor
|
|
#include "mozilla/layers/CompositorParent.h" // for CompositorParent, etc
|
|
#include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
|
|
#include "nsCoord.h" // for NSAppUnitsToFloatPixels, etc
|
|
#include "nsDebug.h" // for NS_ASSERTION, etc
|
|
#include "nsDeviceContext.h" // for nsDeviceContext
|
|
#include "nsDisplayList.h" // for nsDisplayTransform, etc
|
|
#include "nsMathUtils.h" // for NS_round
|
|
#include "nsPoint.h" // for nsPoint
|
|
#include "nsRect.h" // for mozilla::gfx::IntRect
|
|
#include "nsRegion.h" // for nsIntRegion
|
|
#include "nsTArray.h" // for nsTArray, nsTArray_Impl, etc
|
|
#include "nsTArrayForwardDeclare.h" // for InfallibleTArray
|
|
#include "UnitTransforms.h" // for TransformTo
|
|
#if defined(MOZ_WIDGET_ANDROID)
|
|
# include <android/log.h>
|
|
# include "AndroidBridge.h"
|
|
#endif
|
|
#include "GeckoProfiler.h"
|
|
|
|
struct nsCSSValueSharedList;
|
|
|
|
namespace mozilla {
|
|
namespace layers {
|
|
|
|
using namespace mozilla::gfx;
|
|
|
|
enum Op { Resolve, Detach };
|
|
|
|
static bool
|
|
IsSameDimension(dom::ScreenOrientation o1, dom::ScreenOrientation o2)
|
|
{
|
|
bool isO1portrait = (o1 == dom::eScreenOrientation_PortraitPrimary || o1 == dom::eScreenOrientation_PortraitSecondary);
|
|
bool isO2portrait = (o2 == dom::eScreenOrientation_PortraitPrimary || o2 == dom::eScreenOrientation_PortraitSecondary);
|
|
return !(isO1portrait ^ isO2portrait);
|
|
}
|
|
|
|
static bool
|
|
ContentMightReflowOnOrientationChange(const IntRect& rect)
|
|
{
|
|
return rect.width != rect.height;
|
|
}
|
|
|
|
template<Op OP>
|
|
static void
|
|
WalkTheTree(Layer* aLayer,
|
|
bool& aReady,
|
|
const TargetConfig& aTargetConfig)
|
|
{
|
|
if (RefLayer* ref = aLayer->AsRefLayer()) {
|
|
if (const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(ref->GetReferentId())) {
|
|
if (Layer* referent = state->mRoot) {
|
|
if (!ref->GetVisibleRegion().IsEmpty()) {
|
|
dom::ScreenOrientation chromeOrientation = aTargetConfig.orientation();
|
|
dom::ScreenOrientation contentOrientation = state->mTargetConfig.orientation();
|
|
if (!IsSameDimension(chromeOrientation, contentOrientation) &&
|
|
ContentMightReflowOnOrientationChange(aTargetConfig.naturalBounds())) {
|
|
aReady = false;
|
|
}
|
|
}
|
|
|
|
if (OP == Resolve) {
|
|
ref->ConnectReferentLayer(referent);
|
|
} else {
|
|
ref->DetachReferentLayer(referent);
|
|
WalkTheTree<OP>(referent, aReady, aTargetConfig);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (Layer* child = aLayer->GetFirstChild();
|
|
child; child = child->GetNextSibling()) {
|
|
WalkTheTree<OP>(child, aReady, aTargetConfig);
|
|
}
|
|
}
|
|
|
|
void
|
|
AsyncCompositionManager::ResolveRefLayers()
|
|
{
|
|
if (!mLayerManager->GetRoot()) {
|
|
return;
|
|
}
|
|
|
|
mReadyForCompose = true;
|
|
WalkTheTree<Resolve>(mLayerManager->GetRoot(),
|
|
mReadyForCompose,
|
|
mTargetConfig);
|
|
}
|
|
|
|
void
|
|
AsyncCompositionManager::DetachRefLayers()
|
|
{
|
|
if (!mLayerManager->GetRoot()) {
|
|
return;
|
|
}
|
|
WalkTheTree<Detach>(mLayerManager->GetRoot(),
|
|
mReadyForCompose,
|
|
mTargetConfig);
|
|
}
|
|
|
|
void
|
|
AsyncCompositionManager::ComputeRotation()
|
|
{
|
|
if (!mTargetConfig.naturalBounds().IsEmpty()) {
|
|
mWorldTransform =
|
|
ComputeTransformForRotation(mTargetConfig.naturalBounds(),
|
|
mTargetConfig.rotation());
|
|
}
|
|
}
|
|
|
|
static bool
|
|
GetBaseTransform2D(Layer* aLayer, Matrix* aTransform)
|
|
{
|
|
// Start with the animated transform if there is one
|
|
return (aLayer->AsLayerComposite()->GetShadowTransformSetByAnimation() ?
|
|
aLayer->GetLocalTransform() : aLayer->GetTransform()).Is2D(aTransform);
|
|
}
|
|
|
|
static void
|
|
TransformClipRect(Layer* aLayer,
|
|
const Matrix4x4& aTransform)
|
|
{
|
|
const Maybe<ParentLayerIntRect>& clipRect = aLayer->AsLayerComposite()->GetShadowClipRect();
|
|
if (clipRect) {
|
|
ParentLayerIntRect transformed = TransformTo<ParentLayerPixel>(aTransform, *clipRect);
|
|
aLayer->AsLayerComposite()->SetShadowClipRect(Some(transformed));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set the given transform as the shadow transform on the layer, assuming
|
|
* that the given transform already has the pre- and post-scales applied.
|
|
* That is, this function cancels out the pre- and post-scales from aTransform
|
|
* before setting it as the shadow transform on the layer, so that when
|
|
* the layer's effective transform is computed, the pre- and post-scales will
|
|
* only be applied once.
|
|
*/
|
|
static void
|
|
SetShadowTransform(Layer* aLayer, Matrix4x4 aTransform)
|
|
{
|
|
if (ContainerLayer* c = aLayer->AsContainerLayer()) {
|
|
aTransform.PreScale(1.0f / c->GetPreXScale(),
|
|
1.0f / c->GetPreYScale(),
|
|
1);
|
|
}
|
|
aTransform.PostScale(1.0f / aLayer->GetPostXScale(),
|
|
1.0f / aLayer->GetPostYScale(),
|
|
1);
|
|
aLayer->AsLayerComposite()->SetShadowTransform(aTransform);
|
|
}
|
|
|
|
static void
|
|
TranslateShadowLayer2D(Layer* aLayer,
|
|
const gfxPoint& aTranslation,
|
|
bool aAdjustClipRect)
|
|
{
|
|
// This layer might also be a scrollable layer and have an async transform.
|
|
// To make sure we don't clobber that, we start with the shadow transform.
|
|
// (i.e. GetLocalTransform() instead of GetTransform()).
|
|
// Note that the shadow transform is reset on every frame of composition so
|
|
// we don't have to worry about the adjustments compounding over successive
|
|
// frames.
|
|
Matrix layerTransform;
|
|
if (!aLayer->GetLocalTransform().Is2D(&layerTransform)) {
|
|
return;
|
|
}
|
|
|
|
// Apply the 2D translation to the layer transform.
|
|
layerTransform._31 += aTranslation.x;
|
|
layerTransform._32 += aTranslation.y;
|
|
|
|
SetShadowTransform(aLayer, Matrix4x4::From2D(layerTransform));
|
|
aLayer->AsLayerComposite()->SetShadowTransformSetByAnimation(false);
|
|
|
|
if (aAdjustClipRect) {
|
|
TransformClipRect(aLayer, Matrix4x4::Translation(aTranslation.x, aTranslation.y, 0));
|
|
}
|
|
}
|
|
|
|
static bool
|
|
AccumulateLayerTransforms2D(Layer* aLayer,
|
|
Layer* aAncestor,
|
|
Matrix& aMatrix)
|
|
{
|
|
// Accumulate the transforms between this layer and the subtree root layer.
|
|
for (Layer* l = aLayer; l && l != aAncestor; l = l->GetParent()) {
|
|
Matrix l2D;
|
|
if (!GetBaseTransform2D(l, &l2D)) {
|
|
return false;
|
|
}
|
|
aMatrix *= l2D;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static LayerPoint
|
|
GetLayerFixedMarginsOffset(Layer* aLayer,
|
|
const LayerMargin& aFixedLayerMargins)
|
|
{
|
|
// Work out the necessary translation, in root scrollable layer space.
|
|
// Because fixed layer margins are stored relative to the root scrollable
|
|
// layer, we can just take the difference between these values.
|
|
LayerPoint translation;
|
|
const LayerPoint& anchor = aLayer->GetFixedPositionAnchor();
|
|
const LayerMargin& fixedMargins = aLayer->GetFixedPositionMargins();
|
|
|
|
if (fixedMargins.left >= 0) {
|
|
if (anchor.x > 0) {
|
|
translation.x -= aFixedLayerMargins.right - fixedMargins.right;
|
|
} else {
|
|
translation.x += aFixedLayerMargins.left - fixedMargins.left;
|
|
}
|
|
}
|
|
|
|
if (fixedMargins.top >= 0) {
|
|
if (anchor.y > 0) {
|
|
translation.y -= aFixedLayerMargins.bottom - fixedMargins.bottom;
|
|
} else {
|
|
translation.y += aFixedLayerMargins.top - fixedMargins.top;
|
|
}
|
|
}
|
|
|
|
return translation;
|
|
}
|
|
|
|
static gfxFloat
|
|
IntervalOverlap(gfxFloat aTranslation, gfxFloat aMin, gfxFloat aMax)
|
|
{
|
|
// Determine the amount of overlap between the 1D vector |aTranslation|
|
|
// and the interval [aMin, aMax].
|
|
if (aTranslation > 0) {
|
|
return std::max(0.0, std::min(aMax, aTranslation) - std::max(aMin, 0.0));
|
|
} else {
|
|
return std::min(0.0, std::max(aMin, aTranslation) - std::min(aMax, 0.0));
|
|
}
|
|
}
|
|
|
|
void
|
|
AsyncCompositionManager::AlignFixedAndStickyLayers(Layer* aLayer,
|
|
Layer* aTransformedSubtreeRoot,
|
|
FrameMetrics::ViewID aTransformScrollId,
|
|
const Matrix4x4& aPreviousTransformForRoot,
|
|
const Matrix4x4& aCurrentTransformForRoot,
|
|
const LayerMargin& aFixedLayerMargins)
|
|
{
|
|
// If aLayer == aTransformedSubtreeRoot, then treat aLayer as fixed relative
|
|
// to the ancestor scrollable layer rather than relative to itself.
|
|
bool isRootFixed = aLayer->GetIsFixedPosition() &&
|
|
aLayer != aTransformedSubtreeRoot &&
|
|
!aLayer->GetParent()->GetIsFixedPosition();
|
|
bool isStickyForSubtree = aLayer->GetIsStickyPosition() &&
|
|
aLayer->GetStickyScrollContainerId() == aTransformScrollId;
|
|
bool isFixedOrSticky = (isRootFixed || isStickyForSubtree);
|
|
|
|
// We want to process all the fixed and sticky children of
|
|
// aTransformedSubtreeRoot. Also, once we do encounter such a child, we don't
|
|
// need to recurse any deeper because the fixed layers are relative to their
|
|
// nearest scrollable layer.
|
|
if (!isFixedOrSticky) {
|
|
// ApplyAsyncContentTransformToTree will call this function again for
|
|
// nested scrollable layers, so we don't need to recurse if the layer is
|
|
// scrollable.
|
|
if (aLayer == aTransformedSubtreeRoot || !aLayer->HasScrollableFrameMetrics()) {
|
|
for (Layer* child = aLayer->GetFirstChild(); child; child = child->GetNextSibling()) {
|
|
AlignFixedAndStickyLayers(child, aTransformedSubtreeRoot, aTransformScrollId,
|
|
aPreviousTransformForRoot,
|
|
aCurrentTransformForRoot, aFixedLayerMargins);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Insert a translation so that the position of the anchor point is the same
|
|
// before and after the change to the transform of aTransformedSubtreeRoot.
|
|
// This currently only works for fixed layers with 2D transforms.
|
|
|
|
// Accumulate the transforms between this layer and the subtree root layer.
|
|
Matrix ancestorTransform;
|
|
if (!AccumulateLayerTransforms2D(aLayer->GetParent(), aTransformedSubtreeRoot,
|
|
ancestorTransform)) {
|
|
return;
|
|
}
|
|
|
|
Matrix oldRootTransform;
|
|
Matrix newRootTransform;
|
|
if (!aPreviousTransformForRoot.Is2D(&oldRootTransform) ||
|
|
!aCurrentTransformForRoot.Is2D(&newRootTransform)) {
|
|
return;
|
|
}
|
|
|
|
// Calculate the cumulative transforms between the subtree root with the
|
|
// old transform and the current transform.
|
|
Matrix oldCumulativeTransform = ancestorTransform * oldRootTransform;
|
|
Matrix newCumulativeTransform = ancestorTransform * newRootTransform;
|
|
if (newCumulativeTransform.IsSingular()) {
|
|
return;
|
|
}
|
|
Matrix newCumulativeTransformInverse = newCumulativeTransform.Inverse();
|
|
|
|
// Now work out the translation necessary to make sure the layer doesn't
|
|
// move given the new sub-tree root transform.
|
|
Matrix layerTransform;
|
|
if (!GetBaseTransform2D(aLayer, &layerTransform)) {
|
|
return;
|
|
}
|
|
|
|
// Calculate any offset necessary, in previous transform sub-tree root
|
|
// space. This is used to make sure fixed position content respects
|
|
// content document fixed position margins.
|
|
LayerPoint offsetInOldSubtreeLayerSpace = GetLayerFixedMarginsOffset(aLayer, aFixedLayerMargins);
|
|
|
|
// Add the above offset to the anchor point so we can offset the layer by
|
|
// and amount that's specified in old subtree layer space.
|
|
const LayerPoint& anchorInOldSubtreeLayerSpace = aLayer->GetFixedPositionAnchor();
|
|
LayerPoint offsetAnchorInOldSubtreeLayerSpace = anchorInOldSubtreeLayerSpace + offsetInOldSubtreeLayerSpace;
|
|
|
|
// Add the local layer transform to the two points to make the equation
|
|
// below this section more convenient.
|
|
Point anchor(anchorInOldSubtreeLayerSpace.x, anchorInOldSubtreeLayerSpace.y);
|
|
Point offsetAnchor(offsetAnchorInOldSubtreeLayerSpace.x, offsetAnchorInOldSubtreeLayerSpace.y);
|
|
Point locallyTransformedAnchor = layerTransform * anchor;
|
|
Point locallyTransformedOffsetAnchor = layerTransform * offsetAnchor;
|
|
|
|
// Transforming the locallyTransformedAnchor by oldCumulativeTransform
|
|
// returns the layer's anchor point relative to the parent of
|
|
// aTransformedSubtreeRoot, before the new transform was applied.
|
|
// Then, applying newCumulativeTransformInverse maps that point relative
|
|
// to the layer's parent, which is the same coordinate space as
|
|
// locallyTransformedAnchor again, allowing us to subtract them and find
|
|
// out the offset necessary to make sure the layer stays stationary.
|
|
Point oldAnchorPositionInNewSpace =
|
|
newCumulativeTransformInverse * (oldCumulativeTransform * locallyTransformedOffsetAnchor);
|
|
Point translation = oldAnchorPositionInNewSpace - locallyTransformedAnchor;
|
|
|
|
if (aLayer->GetIsStickyPosition()) {
|
|
// For sticky positioned layers, the difference between the two rectangles
|
|
// defines a pair of translation intervals in each dimension through which
|
|
// the layer should not move relative to the scroll container. To
|
|
// accomplish this, we limit each dimension of the |translation| to that
|
|
// part of it which overlaps those intervals.
|
|
const LayerRect& stickyOuter = aLayer->GetStickyScrollRangeOuter();
|
|
const LayerRect& stickyInner = aLayer->GetStickyScrollRangeInner();
|
|
|
|
translation.y = IntervalOverlap(translation.y, stickyOuter.y, stickyOuter.YMost()) -
|
|
IntervalOverlap(translation.y, stickyInner.y, stickyInner.YMost());
|
|
translation.x = IntervalOverlap(translation.x, stickyOuter.x, stickyOuter.XMost()) -
|
|
IntervalOverlap(translation.x, stickyInner.x, stickyInner.XMost());
|
|
}
|
|
|
|
// Finally, apply the 2D translation to the layer transform. Note that in
|
|
// general we need to apply the same translation to the layer's clip rect, so
|
|
// that the effective transform on the clip rect takes it back to where it was
|
|
// originally, had there been no async scroll. In the case where the
|
|
// fixed/sticky layer is the same as aTransformedSubtreeRoot, then the clip
|
|
// rect is not affected by the scroll-induced async scroll transform anyway
|
|
// (since the clip is applied post-transform) so we don't need to make the
|
|
// adjustment.
|
|
TranslateShadowLayer2D(aLayer, ThebesPoint(translation), aLayer != aTransformedSubtreeRoot);
|
|
}
|
|
|
|
static void
|
|
SampleValue(float aPortion, Animation& aAnimation, StyleAnimationValue& aStart,
|
|
StyleAnimationValue& aEnd, Animatable* aValue)
|
|
{
|
|
StyleAnimationValue interpolatedValue;
|
|
NS_ASSERTION(aStart.GetUnit() == aEnd.GetUnit() ||
|
|
aStart.GetUnit() == StyleAnimationValue::eUnit_None ||
|
|
aEnd.GetUnit() == StyleAnimationValue::eUnit_None,
|
|
"Must have same unit");
|
|
StyleAnimationValue::Interpolate(aAnimation.property(), aStart, aEnd,
|
|
aPortion, interpolatedValue);
|
|
if (aAnimation.property() == eCSSProperty_opacity) {
|
|
*aValue = interpolatedValue.GetFloatValue();
|
|
return;
|
|
}
|
|
|
|
nsCSSValueSharedList* interpolatedList =
|
|
interpolatedValue.GetCSSValueSharedListValue();
|
|
|
|
TransformData& data = aAnimation.data().get_TransformData();
|
|
nsPoint origin = data.origin();
|
|
// we expect all our transform data to arrive in device pixels
|
|
Point3D transformOrigin = data.transformOrigin();
|
|
Point3D perspectiveOrigin = data.perspectiveOrigin();
|
|
nsDisplayTransform::FrameTransformProperties props(interpolatedList,
|
|
transformOrigin,
|
|
perspectiveOrigin,
|
|
data.perspective());
|
|
gfx3DMatrix transform =
|
|
nsDisplayTransform::GetResultingTransformMatrix(props, origin,
|
|
data.appUnitsPerDevPixel(),
|
|
&data.bounds());
|
|
Point3D scaledOrigin =
|
|
Point3D(NS_round(NSAppUnitsToFloatPixels(origin.x, data.appUnitsPerDevPixel())),
|
|
NS_round(NSAppUnitsToFloatPixels(origin.y, data.appUnitsPerDevPixel())),
|
|
0.0f);
|
|
|
|
transform.Translate(scaledOrigin);
|
|
|
|
InfallibleTArray<TransformFunction> functions;
|
|
functions.AppendElement(TransformMatrix(ToMatrix4x4(transform)));
|
|
*aValue = functions;
|
|
}
|
|
|
|
static bool
|
|
SampleAnimations(Layer* aLayer, TimeStamp aPoint)
|
|
{
|
|
AnimationArray& animations = aLayer->GetAnimations();
|
|
InfallibleTArray<AnimData>& animationData = aLayer->GetAnimationData();
|
|
|
|
bool activeAnimations = false;
|
|
|
|
// Process in order, since later animations override earlier ones.
|
|
for (size_t i = 0, iEnd = animations.Length(); i < iEnd; ++i) {
|
|
Animation& animation = animations[i];
|
|
AnimData& animData = animationData[i];
|
|
|
|
activeAnimations = true;
|
|
|
|
MOZ_ASSERT(!animation.startTime().IsNull(),
|
|
"Failed to resolve start time of pending animations");
|
|
TimeDuration elapsedDuration = aPoint - animation.startTime();
|
|
// Skip animations that are yet to start.
|
|
//
|
|
// Currently, this should only happen when the refresh driver is under test
|
|
// control and is made to produce a time in the past or is restored from
|
|
// test control causing it to jump backwards in time.
|
|
//
|
|
// Since activeAnimations is true, this could mean we keep compositing
|
|
// unnecessarily during the delay, but so long as this only happens while
|
|
// the refresh driver is under test control that should be ok.
|
|
if (elapsedDuration.ToSeconds() < 0) {
|
|
continue;
|
|
}
|
|
|
|
AnimationTiming timing;
|
|
timing.mIterationDuration = animation.duration();
|
|
// Currently animations run on the compositor have their delay factored
|
|
// into their start time, hence the delay is effectively zero.
|
|
timing.mDelay = TimeDuration(0);
|
|
timing.mIterationCount = animation.iterationCount();
|
|
timing.mDirection = animation.direction();
|
|
// Animations typically only run on the compositor during their active
|
|
// interval but if we end up sampling them outside that range (for
|
|
// example, while they are waiting to be removed) we currently just
|
|
// assume that we should fill.
|
|
timing.mFillMode = NS_STYLE_ANIMATION_FILL_MODE_BOTH;
|
|
|
|
ComputedTiming computedTiming =
|
|
dom::KeyframeEffectReadOnly::GetComputedTimingAt(
|
|
Nullable<TimeDuration>(elapsedDuration), timing);
|
|
|
|
MOZ_ASSERT(0.0 <= computedTiming.mTimeFraction &&
|
|
computedTiming.mTimeFraction <= 1.0,
|
|
"time fraction should be in [0-1]");
|
|
|
|
int segmentIndex = 0;
|
|
AnimationSegment* segment = animation.segments().Elements();
|
|
while (segment->endPortion() < computedTiming.mTimeFraction) {
|
|
++segment;
|
|
++segmentIndex;
|
|
}
|
|
|
|
double positionInSegment =
|
|
(computedTiming.mTimeFraction - segment->startPortion()) /
|
|
(segment->endPortion() - segment->startPortion());
|
|
|
|
double portion =
|
|
animData.mFunctions[segmentIndex]->GetValue(positionInSegment);
|
|
|
|
// interpolate the property
|
|
Animatable interpolatedValue;
|
|
SampleValue(portion, animation, animData.mStartValues[segmentIndex],
|
|
animData.mEndValues[segmentIndex], &interpolatedValue);
|
|
LayerComposite* layerComposite = aLayer->AsLayerComposite();
|
|
switch (animation.property()) {
|
|
case eCSSProperty_opacity:
|
|
{
|
|
layerComposite->SetShadowOpacity(interpolatedValue.get_float());
|
|
break;
|
|
}
|
|
case eCSSProperty_transform:
|
|
{
|
|
Matrix4x4 matrix = interpolatedValue.get_ArrayOfTransformFunction()[0].get_TransformMatrix().value();
|
|
if (ContainerLayer* c = aLayer->AsContainerLayer()) {
|
|
matrix.PostScale(c->GetInheritedXScale(), c->GetInheritedYScale(), 1);
|
|
}
|
|
layerComposite->SetShadowTransform(matrix);
|
|
layerComposite->SetShadowTransformSetByAnimation(true);
|
|
break;
|
|
}
|
|
default:
|
|
NS_WARNING("Unhandled animated property");
|
|
}
|
|
}
|
|
|
|
for (Layer* child = aLayer->GetFirstChild(); child;
|
|
child = child->GetNextSibling()) {
|
|
activeAnimations |= SampleAnimations(child, aPoint);
|
|
}
|
|
|
|
return activeAnimations;
|
|
}
|
|
|
|
static bool
|
|
SampleAPZAnimations(const LayerMetricsWrapper& aLayer, TimeStamp aSampleTime)
|
|
{
|
|
bool activeAnimations = false;
|
|
for (LayerMetricsWrapper child = aLayer.GetFirstChild(); child;
|
|
child = child.GetNextSibling()) {
|
|
activeAnimations |= SampleAPZAnimations(child, aSampleTime);
|
|
}
|
|
|
|
if (AsyncPanZoomController* apzc = aLayer.GetApzc()) {
|
|
activeAnimations |= apzc->AdvanceAnimations(aSampleTime);
|
|
}
|
|
|
|
return activeAnimations;
|
|
}
|
|
|
|
Matrix4x4
|
|
AdjustForClip(const Matrix4x4& asyncTransform, Layer* aLayer)
|
|
{
|
|
Matrix4x4 result = asyncTransform;
|
|
|
|
// Container layers start at the origin, but they are clipped to where they
|
|
// actually have content on the screen. The tree transform is meant to apply
|
|
// to the clipped area. If the tree transform includes a scale component,
|
|
// then applying it to container as-is will produce incorrect results. To
|
|
// avoid this, translate the layer so that the clip rect starts at the origin,
|
|
// apply the tree transform, and translate back.
|
|
if (const Maybe<ParentLayerIntRect>& shadowClipRect = aLayer->AsLayerComposite()->GetShadowClipRect()) {
|
|
if (shadowClipRect->TopLeft() != ParentLayerIntPoint()) { // avoid a gratuitous change of basis
|
|
result.ChangeBasis(shadowClipRect->x, shadowClipRect->y, 0);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool
|
|
AsyncCompositionManager::ApplyAsyncContentTransformToTree(Layer *aLayer)
|
|
{
|
|
bool appliedTransform = false;
|
|
for (Layer* child = aLayer->GetFirstChild();
|
|
child; child = child->GetNextSibling()) {
|
|
appliedTransform |=
|
|
ApplyAsyncContentTransformToTree(child);
|
|
}
|
|
|
|
Matrix4x4 oldTransform = aLayer->GetTransform();
|
|
|
|
Matrix4x4 combinedAsyncTransformWithoutOverscroll;
|
|
Matrix4x4 combinedAsyncTransform;
|
|
bool hasAsyncTransform = false;
|
|
LayerMargin fixedLayerMargins(0, 0, 0, 0);
|
|
Maybe<ParentLayerIntRect> clipRect = aLayer->AsLayerComposite()->GetShadowClipRect();
|
|
|
|
for (uint32_t i = 0; i < aLayer->GetFrameMetricsCount(); i++) {
|
|
AsyncPanZoomController* controller = aLayer->GetAsyncPanZoomController(i);
|
|
if (!controller) {
|
|
continue;
|
|
}
|
|
|
|
hasAsyncTransform = true;
|
|
|
|
ViewTransform asyncTransformWithoutOverscroll;
|
|
ParentLayerPoint scrollOffset;
|
|
controller->SampleContentTransformForFrame(&asyncTransformWithoutOverscroll,
|
|
scrollOffset);
|
|
Matrix4x4 overscrollTransform = controller->GetOverscrollTransform();
|
|
|
|
if (!aLayer->IsScrollInfoLayer()) {
|
|
controller->MarkAsyncTransformAppliedToContent();
|
|
}
|
|
|
|
const FrameMetrics& metrics = aLayer->GetFrameMetrics(i);
|
|
ScreenPoint offset(0, 0);
|
|
// TODO: When we enable APZ on Fennec, we'll need to call SyncFrameMetrics here.
|
|
// When doing so, it might be useful to look at how it was called here before
|
|
// bug 1036967 removed the (dead) call.
|
|
|
|
#if defined(MOZ_ANDROID_APZ)
|
|
if (mIsFirstPaint) {
|
|
CSSToLayerScale geckoZoom = metrics.LayersPixelsPerCSSPixel().ToScaleFactor();
|
|
LayerIntPoint scrollOffsetLayerPixels = RoundedToInt(metrics.GetScrollOffset() * geckoZoom);
|
|
mContentRect = metrics.GetScrollableRect();
|
|
SetFirstPaintViewport(scrollOffsetLayerPixels,
|
|
geckoZoom,
|
|
mContentRect);
|
|
}
|
|
#endif
|
|
|
|
mIsFirstPaint = false;
|
|
mLayersUpdated = false;
|
|
|
|
// Apply the render offset
|
|
mLayerManager->GetCompositor()->SetScreenRenderOffset(offset);
|
|
|
|
// See the comment below - the first FrameMetrics has the clip computed
|
|
// by layout (currently, effectively the composition bounds), which we
|
|
// intersect here to include the layer clip.
|
|
if (i == 0 && metrics.HasClipRect()) {
|
|
if (clipRect) {
|
|
clipRect = Some(clipRect.value().Intersect(metrics.ClipRect()));
|
|
} else {
|
|
clipRect = Some(metrics.ClipRect());
|
|
}
|
|
}
|
|
|
|
combinedAsyncTransformWithoutOverscroll *= asyncTransformWithoutOverscroll;
|
|
combinedAsyncTransform *= (Matrix4x4(asyncTransformWithoutOverscroll) * overscrollTransform);
|
|
if (i > 0 && clipRect) {
|
|
// The clip rect Layout calculates is the intersection of the composition
|
|
// bounds of all the scroll frames at the time of the paint (when there
|
|
// are no async transforms).
|
|
// An async transform on a scroll frame does not affect the composition
|
|
// bounds of *that* scroll frame, but it does affect the composition
|
|
// bounds of the scroll frames *below* it.
|
|
// Therefore, if we have multiple scroll frames associated with this
|
|
// layer, the clip rect needs to be adjusted for the async transforms of
|
|
// the scroll frames other than the bottom-most one.
|
|
// To make this adjustment, we start with the Layout-provided clip rect,
|
|
// and at each level other than the bottom, transform it by the async
|
|
// transform at that level, and then re-intersect it with the composition
|
|
// bounds at that level.
|
|
ParentLayerRect transformed = TransformTo<ParentLayerPixel>(
|
|
(Matrix4x4(asyncTransformWithoutOverscroll) * overscrollTransform),
|
|
ParentLayerRect(*clipRect));
|
|
clipRect = Some(RoundedOut(transformed.Intersect(metrics.GetCompositionBounds())));
|
|
}
|
|
}
|
|
|
|
if (hasAsyncTransform) {
|
|
if (clipRect) {
|
|
aLayer->AsLayerComposite()->SetShadowClipRect(clipRect);
|
|
}
|
|
// Apply the APZ transform on top of GetLocalTransform() here (rather than
|
|
// GetTransform()) in case the OMTA code in SampleAnimations already set a
|
|
// shadow transform; in that case we want to apply ours on top of that one
|
|
// rather than clobber it.
|
|
SetShadowTransform(aLayer,
|
|
aLayer->GetLocalTransform() * AdjustForClip(combinedAsyncTransform, aLayer));
|
|
|
|
const FrameMetrics& bottom = LayerMetricsWrapper::BottommostScrollableMetrics(aLayer);
|
|
MOZ_ASSERT(bottom.IsScrollable()); // must be true because hasAsyncTransform is true
|
|
|
|
// For the purpose of aligning fixed and sticky layers, we disregard
|
|
// the overscroll transform as well as any OMTA transform when computing the
|
|
// 'aCurrentTransformForRoot' parameter. This ensures that the overscroll
|
|
// and OMTA transforms are not unapplied, and therefore that the visual
|
|
// effects apply to fixed and sticky layers. We do this by using
|
|
// GetTransform() as the base transform rather than GetLocalTransform(),
|
|
// which would include those factors.
|
|
Matrix4x4 transformWithoutOverscrollOrOmta = aLayer->GetTransform() *
|
|
AdjustForClip(combinedAsyncTransformWithoutOverscroll, aLayer);
|
|
// Since fixed/sticky layers are relative to their nearest scrolling ancestor,
|
|
// we use the ViewID from the bottommost scrollable metrics here.
|
|
AlignFixedAndStickyLayers(aLayer, aLayer, bottom.GetScrollId(), oldTransform,
|
|
transformWithoutOverscrollOrOmta, fixedLayerMargins);
|
|
|
|
appliedTransform = true;
|
|
}
|
|
|
|
if (aLayer->GetScrollbarDirection() != Layer::NONE) {
|
|
ApplyAsyncTransformToScrollbar(aLayer);
|
|
}
|
|
return appliedTransform;
|
|
}
|
|
|
|
static bool
|
|
LayerIsScrollbarTarget(const LayerMetricsWrapper& aTarget, Layer* aScrollbar)
|
|
{
|
|
AsyncPanZoomController* apzc = aTarget.GetApzc();
|
|
if (!apzc) {
|
|
return false;
|
|
}
|
|
const FrameMetrics& metrics = aTarget.Metrics();
|
|
if (metrics.GetScrollId() != aScrollbar->GetScrollbarTargetContainerId()) {
|
|
return false;
|
|
}
|
|
return !aTarget.IsScrollInfoLayer();
|
|
}
|
|
|
|
static void
|
|
ApplyAsyncTransformToScrollbarForContent(Layer* aScrollbar,
|
|
const LayerMetricsWrapper& aContent,
|
|
bool aScrollbarIsDescendant)
|
|
{
|
|
// We only apply the transform if the scroll-target layer has non-container
|
|
// children (i.e. when it has some possibly-visible content). This is to
|
|
// avoid moving scroll-bars in the situation that only a scroll information
|
|
// layer has been built for a scroll frame, as this would result in a
|
|
// disparity between scrollbars and visible content.
|
|
if (aContent.IsScrollInfoLayer()) {
|
|
return;
|
|
}
|
|
|
|
const FrameMetrics& metrics = aContent.Metrics();
|
|
AsyncPanZoomController* apzc = aContent.GetApzc();
|
|
|
|
Matrix4x4 asyncTransform = apzc->GetCurrentAsyncTransform();
|
|
|
|
// |asyncTransform| represents the amount by which we have scrolled and
|
|
// zoomed since the last paint. Because the scrollbar was sized and positioned based
|
|
// on the painted content, we need to adjust it based on asyncTransform so that
|
|
// it reflects what the user is actually seeing now.
|
|
Matrix4x4 scrollbarTransform;
|
|
if (aScrollbar->GetScrollbarDirection() == Layer::VERTICAL) {
|
|
const ParentLayerCoord asyncScrollY = asyncTransform._42;
|
|
const float asyncZoomY = asyncTransform._22;
|
|
|
|
// The scroll thumb needs to be scaled in the direction of scrolling by the
|
|
// inverse of the async zoom. This is because zooming in decreases the
|
|
// fraction of the whole srollable rect that is in view.
|
|
const float yScale = 1.f / asyncZoomY;
|
|
|
|
// Note: |metrics.GetZoom()| doesn't yet include the async zoom, so
|
|
// |metrics.CalculateCompositedSizeInCssPixels()| would not give a correct
|
|
// result.
|
|
const CSSToParentLayerScale effectiveZoom(metrics.GetZoom().yScale * asyncZoomY);
|
|
|
|
const LayoutDeviceToParentLayerScale nonLayoutScale = effectiveZoom /
|
|
metrics.GetDevPixelsPerCSSPixel();
|
|
// Here we convert the scrollbar thumb ratio into a true unitless ratio by
|
|
// dividing out the conversion factor from the scrollframe's parent's space
|
|
// to the scrollframe's space.
|
|
const float ratio = aScrollbar->GetScrollbarThumbRatio() / nonLayoutScale.scale;
|
|
ParentLayerCoord yTranslation = -asyncScrollY * ratio;
|
|
|
|
// The scroll thumb additionally needs to be translated to compensate for
|
|
// the scale applied above. The origin with respect to which the scale is
|
|
// applied is the origin of the entire scrollbar, rather than the origin of
|
|
// the scroll thumb (meaning, for a vertical scrollbar it's at the top of
|
|
// the composition bounds). This means that empty space above the thumb
|
|
// is scaled too, effectively translating the thumb. We undo that
|
|
// translation here.
|
|
// (One can think of the adjustment being done to the translation here as
|
|
// a change of basis. We have a method to help with that,
|
|
// Matrix4x4::ChangeBasis(), but it wouldn't necessarily make the code
|
|
// cleaner in this case).
|
|
const CSSCoord thumbOrigin = (metrics.GetScrollOffset().y * ratio);
|
|
const CSSCoord thumbOriginScaled = thumbOrigin * yScale;
|
|
const CSSCoord thumbOriginDelta = thumbOriginScaled - thumbOrigin;
|
|
const ParentLayerCoord thumbOriginDeltaPL = thumbOriginDelta * effectiveZoom;
|
|
yTranslation -= thumbOriginDeltaPL;
|
|
|
|
if (aScrollbarIsDescendant) {
|
|
// In cases where the scrollbar is a descendant of the content, the
|
|
// scrollbar gets painted at the same resolution as the content. Since the
|
|
// coordinate space we apply this transform in includes the resolution, we
|
|
// need to adjust for it as well here. Note that in another
|
|
// aScrollbarIsDescendant hunk below we apply a resolution-cancelling
|
|
// transform which ensures the scroll thumb isn't actually rendered
|
|
// at a larger scale.
|
|
yTranslation *= metrics.GetPresShellResolution();
|
|
}
|
|
|
|
scrollbarTransform.PostScale(1.f, yScale, 1.f);
|
|
scrollbarTransform.PostTranslate(0, yTranslation, 0);
|
|
}
|
|
if (aScrollbar->GetScrollbarDirection() == Layer::HORIZONTAL) {
|
|
// See detailed comments under the VERTICAL case.
|
|
|
|
const ParentLayerCoord asyncScrollX = asyncTransform._41;
|
|
const float asyncZoomX = asyncTransform._11;
|
|
|
|
const float xScale = 1.f / asyncZoomX;
|
|
|
|
const CSSToParentLayerScale effectiveZoom(metrics.GetZoom().xScale * asyncZoomX);
|
|
|
|
const LayoutDeviceToParentLayerScale nonLayoutScale = effectiveZoom /
|
|
metrics.GetDevPixelsPerCSSPixel();
|
|
const float ratio = aScrollbar->GetScrollbarThumbRatio() / nonLayoutScale.scale;
|
|
ParentLayerCoord xTranslation = -asyncScrollX * ratio;
|
|
|
|
const CSSCoord thumbOrigin = (metrics.GetScrollOffset().x * ratio);
|
|
const CSSCoord thumbOriginScaled = thumbOrigin * xScale;
|
|
const CSSCoord thumbOriginDelta = thumbOriginScaled - thumbOrigin;
|
|
const ParentLayerCoord thumbOriginDeltaPL = thumbOriginDelta * effectiveZoom;
|
|
xTranslation -= thumbOriginDeltaPL;
|
|
|
|
if (aScrollbarIsDescendant) {
|
|
xTranslation *= metrics.GetPresShellResolution();
|
|
}
|
|
|
|
scrollbarTransform.PostScale(xScale, 1.f, 1.f);
|
|
scrollbarTransform.PostTranslate(xTranslation, 0, 0);
|
|
}
|
|
|
|
Matrix4x4 transform = scrollbarTransform * aScrollbar->GetTransform();
|
|
|
|
if (aScrollbarIsDescendant) {
|
|
// If the scrollbar layer is a child of the content it is a scrollbar for,
|
|
// then we need to make a couple of adjustments to the scrollbar's transform.
|
|
//
|
|
// - First, the content's resolution applies to the scrollbar as well.
|
|
// Since we don't actually want the scroll thumb's size to vary with
|
|
// the zoom (other than its length reflecting the fraction of the
|
|
// scrollable length that's in view, which is taken care of above),
|
|
// we apply a transform to cancel out this resolution.
|
|
//
|
|
// - Second, if there is any async transform (including an overscroll
|
|
// transform) on the content, this needs to be cancelled out because
|
|
// layout positions and sizes the scrollbar on the assumption that there
|
|
// is no async transform, and without this adjustment the scrollbar will
|
|
// end up in the wrong place.
|
|
//
|
|
// Note that since the async transform is applied on top of the content's
|
|
// regular transform, we need to make sure to unapply the async transform
|
|
// in the same coordinate space. This requires applying the content
|
|
// transform and then unapplying it after unapplying the async transform.
|
|
Matrix4x4 resolutionCancellingTransform =
|
|
Matrix4x4::Scaling(metrics.GetPresShellResolution(),
|
|
metrics.GetPresShellResolution(),
|
|
1.0f).Inverse();
|
|
Matrix4x4 asyncUntransform = (asyncTransform * apzc->GetOverscrollTransform()).Inverse();
|
|
Matrix4x4 contentTransform = aContent.GetTransform();
|
|
Matrix4x4 contentUntransform = contentTransform.Inverse();
|
|
|
|
Matrix4x4 compensation = resolutionCancellingTransform
|
|
* contentTransform
|
|
* asyncUntransform
|
|
* contentUntransform;
|
|
transform = transform * compensation;
|
|
|
|
// We also need to make a corresponding change on the clip rect of all the
|
|
// layers on the ancestor chain from the scrollbar layer up to but not
|
|
// including the layer with the async transform. Otherwise the scrollbar
|
|
// shifts but gets clipped and so appears to flicker.
|
|
for (Layer* ancestor = aScrollbar; ancestor != aContent.GetLayer(); ancestor = ancestor->GetParent()) {
|
|
TransformClipRect(ancestor, compensation);
|
|
}
|
|
}
|
|
|
|
SetShadowTransform(aScrollbar, transform);
|
|
}
|
|
|
|
static LayerMetricsWrapper
|
|
FindScrolledLayerRecursive(Layer* aScrollbar, const LayerMetricsWrapper& aSubtreeRoot)
|
|
{
|
|
if (LayerIsScrollbarTarget(aSubtreeRoot, aScrollbar)) {
|
|
return aSubtreeRoot;
|
|
}
|
|
for (LayerMetricsWrapper child = aSubtreeRoot.GetFirstChild(); child;
|
|
child = child.GetNextSibling()) {
|
|
LayerMetricsWrapper target = FindScrolledLayerRecursive(aScrollbar, child);
|
|
if (target) {
|
|
return target;
|
|
}
|
|
}
|
|
return LayerMetricsWrapper();
|
|
}
|
|
|
|
static LayerMetricsWrapper
|
|
FindScrolledLayerForScrollbar(Layer* aScrollbar, bool* aOutIsAncestor)
|
|
{
|
|
// Search ancestors first.
|
|
LayerMetricsWrapper scrollbar(aScrollbar);
|
|
for (LayerMetricsWrapper ancestor = scrollbar; ancestor; ancestor = ancestor.GetParent()) {
|
|
if (LayerIsScrollbarTarget(ancestor, aScrollbar)) {
|
|
*aOutIsAncestor = true;
|
|
return ancestor;
|
|
}
|
|
}
|
|
|
|
// If the scrolled target is not an ancestor, search the whole layer tree.
|
|
// XXX It would be much better to search the APZC tree instead of the layer
|
|
// tree. That way we would ignore non-scrollable layers, and we'd only visit
|
|
// each scroll ID once. In the end we only need the APZC and the FrameMetrics
|
|
// of the scrolled target.
|
|
*aOutIsAncestor = false;
|
|
LayerMetricsWrapper root(aScrollbar->Manager()->GetRoot());
|
|
return FindScrolledLayerRecursive(aScrollbar, root);
|
|
}
|
|
|
|
void
|
|
AsyncCompositionManager::ApplyAsyncTransformToScrollbar(Layer* aLayer)
|
|
{
|
|
// If this layer corresponds to a scrollbar, then there should be a layer that
|
|
// is a previous sibling or a parent that has a matching ViewID on its FrameMetrics.
|
|
// That is the content that this scrollbar is for. We pick up the transient
|
|
// async transform from that layer and use it to update the scrollbar position.
|
|
// Note that it is possible that the content layer is no longer there; in
|
|
// this case we don't need to do anything because there can't be an async
|
|
// transform on the content.
|
|
bool isAncestor = false;
|
|
const LayerMetricsWrapper& scrollTarget = FindScrolledLayerForScrollbar(aLayer, &isAncestor);
|
|
if (scrollTarget) {
|
|
ApplyAsyncTransformToScrollbarForContent(aLayer, scrollTarget, isAncestor);
|
|
}
|
|
}
|
|
|
|
void
|
|
AsyncCompositionManager::TransformScrollableLayer(Layer* aLayer)
|
|
{
|
|
FrameMetrics metrics = LayerMetricsWrapper::TopmostScrollableMetrics(aLayer);
|
|
if (!metrics.IsScrollable()) {
|
|
// On Fennec it's possible that the there is no scrollable layer in the
|
|
// tree, and this function just gets called with the root layer. In that
|
|
// case TopmostScrollableMetrics will return an empty FrameMetrics but we
|
|
// still want to use the actual non-scrollable metrics from the layer.
|
|
metrics = LayerMetricsWrapper::BottommostMetrics(aLayer);
|
|
}
|
|
|
|
// We must apply the resolution scale before a pan/zoom transform, so we call
|
|
// GetTransform here.
|
|
Matrix4x4 oldTransform = aLayer->GetTransform();
|
|
|
|
CSSToLayerScale geckoZoom = metrics.LayersPixelsPerCSSPixel().ToScaleFactor();
|
|
|
|
LayerIntPoint scrollOffsetLayerPixels = RoundedToInt(metrics.GetScrollOffset() * geckoZoom);
|
|
|
|
if (mIsFirstPaint) {
|
|
mContentRect = metrics.GetScrollableRect();
|
|
SetFirstPaintViewport(scrollOffsetLayerPixels,
|
|
geckoZoom,
|
|
mContentRect);
|
|
mIsFirstPaint = false;
|
|
} else if (!metrics.GetScrollableRect().IsEqualEdges(mContentRect)) {
|
|
mContentRect = metrics.GetScrollableRect();
|
|
SetPageRect(mContentRect);
|
|
}
|
|
|
|
// We synchronise the viewport information with Java after sending the above
|
|
// notifications, so that Java can take these into account in its response.
|
|
// Calculate the absolute display port to send to Java
|
|
LayerIntRect displayPort = RoundedToInt(
|
|
(metrics.GetCriticalDisplayPort().IsEmpty()
|
|
? metrics.GetDisplayPort()
|
|
: metrics.GetCriticalDisplayPort()
|
|
) * geckoZoom);
|
|
displayPort += scrollOffsetLayerPixels;
|
|
|
|
LayerMargin fixedLayerMargins(0, 0, 0, 0);
|
|
ScreenPoint offset(0, 0);
|
|
|
|
// Ideally we would initialize userZoom to AsyncPanZoomController::CalculateResolution(metrics)
|
|
// but this causes a reftest-ipc test to fail (see bug 883646 comment 27). The reason for this
|
|
// appears to be that metrics.mZoom is poorly initialized in some scenarios. In these scenarios,
|
|
// however, we can assume there is no async zooming in progress and so the following statement
|
|
// works fine.
|
|
CSSToParentLayerScale userZoom(metrics.GetDevPixelsPerCSSPixel()
|
|
// This function only applies to the root scrollable frame,
|
|
// for which we can assume that x and y scales are equal.
|
|
* metrics.GetCumulativeResolution().ToScaleFactor()
|
|
* LayerToParentLayerScale(1));
|
|
ParentLayerPoint userScroll = metrics.GetScrollOffset() * userZoom;
|
|
SyncViewportInfo(displayPort, geckoZoom, mLayersUpdated,
|
|
userScroll, userZoom, fixedLayerMargins,
|
|
offset);
|
|
mLayersUpdated = false;
|
|
|
|
// Apply the render offset
|
|
mLayerManager->GetCompositor()->SetScreenRenderOffset(offset);
|
|
|
|
// Handle transformations for asynchronous panning and zooming. We determine the
|
|
// zoom used by Gecko from the transformation set on the root layer, and we
|
|
// determine the scroll offset used by Gecko from the frame metrics of the
|
|
// primary scrollable layer. We compare this to the user zoom and scroll
|
|
// offset in the view transform we obtained from Java in order to compute the
|
|
// transformation we need to apply.
|
|
ParentLayerPoint geckoScroll(0, 0);
|
|
if (metrics.IsScrollable()) {
|
|
geckoScroll = metrics.GetScrollOffset() * userZoom;
|
|
}
|
|
|
|
LayerToParentLayerScale asyncZoom = userZoom / metrics.LayersPixelsPerCSSPixel().ToScaleFactor();
|
|
ParentLayerPoint translation = userScroll - geckoScroll;
|
|
Matrix4x4 treeTransform = ViewTransform(asyncZoom, -translation);
|
|
|
|
// Apply the tree transform on top of GetLocalTransform() here (rather than
|
|
// GetTransform()) in case the OMTA code in SampleAnimations already set a
|
|
// shadow transform; in that case we want to apply ours on top of that one
|
|
// rather than clobber it.
|
|
SetShadowTransform(aLayer, aLayer->GetLocalTransform() * treeTransform);
|
|
|
|
// Make sure that overscroll and under-zoom are represented in the old
|
|
// transform so that fixed position content moves and scales accordingly.
|
|
// These calculations will effectively scale and offset fixed position layers
|
|
// in screen space when the compensatory transform is performed in
|
|
// AlignFixedAndStickyLayers.
|
|
ParentLayerRect contentScreenRect = mContentRect * userZoom;
|
|
Point3D overscrollTranslation;
|
|
if (userScroll.x < contentScreenRect.x) {
|
|
overscrollTranslation.x = contentScreenRect.x - userScroll.x;
|
|
} else if (userScroll.x + metrics.GetCompositionBounds().width > contentScreenRect.XMost()) {
|
|
overscrollTranslation.x = contentScreenRect.XMost() -
|
|
(userScroll.x + metrics.GetCompositionBounds().width);
|
|
}
|
|
if (userScroll.y < contentScreenRect.y) {
|
|
overscrollTranslation.y = contentScreenRect.y - userScroll.y;
|
|
} else if (userScroll.y + metrics.GetCompositionBounds().height > contentScreenRect.YMost()) {
|
|
overscrollTranslation.y = contentScreenRect.YMost() -
|
|
(userScroll.y + metrics.GetCompositionBounds().height);
|
|
}
|
|
oldTransform.PreTranslate(overscrollTranslation.x,
|
|
overscrollTranslation.y,
|
|
overscrollTranslation.z);
|
|
|
|
gfx::Size underZoomScale(1.0f, 1.0f);
|
|
if (mContentRect.width * userZoom.scale < metrics.GetCompositionBounds().width) {
|
|
underZoomScale.width = (mContentRect.width * userZoom.scale) /
|
|
metrics.GetCompositionBounds().width;
|
|
}
|
|
if (mContentRect.height * userZoom.scale < metrics.GetCompositionBounds().height) {
|
|
underZoomScale.height = (mContentRect.height * userZoom.scale) /
|
|
metrics.GetCompositionBounds().height;
|
|
}
|
|
oldTransform.PreScale(underZoomScale.width, underZoomScale.height, 1);
|
|
|
|
// Make sure fixed position layers don't move away from their anchor points
|
|
// when we're asynchronously panning or zooming
|
|
AlignFixedAndStickyLayers(aLayer, aLayer, metrics.GetScrollId(), oldTransform,
|
|
aLayer->GetLocalTransform(), fixedLayerMargins);
|
|
}
|
|
|
|
bool
|
|
AsyncCompositionManager::TransformShadowTree(TimeStamp aCurrentFrame,
|
|
TransformsToSkip aSkip)
|
|
{
|
|
PROFILER_LABEL("AsyncCompositionManager", "TransformShadowTree",
|
|
js::ProfileEntry::Category::GRAPHICS);
|
|
|
|
Layer* root = mLayerManager->GetRoot();
|
|
if (!root) {
|
|
return false;
|
|
}
|
|
|
|
// First, compute and set the shadow transforms from OMT animations.
|
|
// NB: we must sample animations *before* sampling pan/zoom
|
|
// transforms.
|
|
bool wantNextFrame = SampleAnimations(root, aCurrentFrame);
|
|
|
|
if (!(aSkip & TransformsToSkip::APZ)) {
|
|
// FIXME/bug 775437: unify this interface with the ~native-fennec
|
|
// derived code
|
|
//
|
|
// Attempt to apply an async content transform to any layer that has
|
|
// an async pan zoom controller (which means that it is rendered
|
|
// async using Gecko). If this fails, fall back to transforming the
|
|
// primary scrollable layer. "Failing" here means that we don't
|
|
// find a frame that is async scrollable. Note that the fallback
|
|
// code also includes Fennec which is rendered async. Fennec uses
|
|
// its own platform-specific async rendering that is done partially
|
|
// in Gecko and partially in Java.
|
|
wantNextFrame |= SampleAPZAnimations(LayerMetricsWrapper(root), aCurrentFrame);
|
|
if (!ApplyAsyncContentTransformToTree(root)) {
|
|
nsAutoTArray<Layer*,1> scrollableLayers;
|
|
#ifdef MOZ_WIDGET_ANDROID
|
|
mLayerManager->GetRootScrollableLayers(scrollableLayers);
|
|
#else
|
|
mLayerManager->GetScrollableLayers(scrollableLayers);
|
|
#endif
|
|
|
|
for (uint32_t i = 0; i < scrollableLayers.Length(); i++) {
|
|
if (scrollableLayers[i]) {
|
|
TransformScrollableLayer(scrollableLayers[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
LayerComposite* rootComposite = root->AsLayerComposite();
|
|
|
|
gfx::Matrix4x4 trans = rootComposite->GetShadowTransform();
|
|
trans *= gfx::Matrix4x4::From2D(mWorldTransform);
|
|
rootComposite->SetShadowTransform(trans);
|
|
|
|
|
|
return wantNextFrame;
|
|
}
|
|
|
|
void
|
|
AsyncCompositionManager::SetFirstPaintViewport(const LayerIntPoint& aOffset,
|
|
const CSSToLayerScale& aZoom,
|
|
const CSSRect& aCssPageRect)
|
|
{
|
|
#ifdef MOZ_WIDGET_ANDROID
|
|
AndroidBridge::Bridge()->SetFirstPaintViewport(aOffset, aZoom, aCssPageRect);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
AsyncCompositionManager::SetPageRect(const CSSRect& aCssPageRect)
|
|
{
|
|
#ifdef MOZ_WIDGET_ANDROID
|
|
AndroidBridge::Bridge()->SetPageRect(aCssPageRect);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
AsyncCompositionManager::SyncViewportInfo(const LayerIntRect& aDisplayPort,
|
|
const CSSToLayerScale& aDisplayResolution,
|
|
bool aLayersUpdated,
|
|
ParentLayerPoint& aScrollOffset,
|
|
CSSToParentLayerScale& aScale,
|
|
LayerMargin& aFixedLayerMargins,
|
|
ScreenPoint& aOffset)
|
|
{
|
|
#ifdef MOZ_WIDGET_ANDROID
|
|
AndroidBridge::Bridge()->SyncViewportInfo(aDisplayPort,
|
|
aDisplayResolution,
|
|
aLayersUpdated,
|
|
aScrollOffset,
|
|
aScale,
|
|
aFixedLayerMargins,
|
|
aOffset);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
AsyncCompositionManager::SyncFrameMetrics(const ParentLayerPoint& aScrollOffset,
|
|
float aZoom,
|
|
const CSSRect& aCssPageRect,
|
|
bool aLayersUpdated,
|
|
const CSSRect& aDisplayPort,
|
|
const CSSToLayerScale& aDisplayResolution,
|
|
bool aIsFirstPaint,
|
|
LayerMargin& aFixedLayerMargins,
|
|
ScreenPoint& aOffset)
|
|
{
|
|
#ifdef MOZ_WIDGET_ANDROID
|
|
AndroidBridge::Bridge()->SyncFrameMetrics(aScrollOffset, aZoom, aCssPageRect,
|
|
aLayersUpdated, aDisplayPort,
|
|
aDisplayResolution, aIsFirstPaint,
|
|
aFixedLayerMargins, aOffset);
|
|
#endif
|
|
}
|
|
|
|
} // namespace layers
|
|
} // namespace mozilla
|