tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

StackingContextHelper.cpp (11710B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "mozilla/layers/StackingContextHelper.h"
      8 
      9 #include "mozilla/PresShell.h"
     10 #include "mozilla/gfx/Point.h"
     11 #include "mozilla/gfx/Matrix.h"
     12 #include "UnitTransforms.h"
     13 #include "nsDisplayList.h"
     14 #include "mozilla/dom/BrowserChild.h"
     15 #include "nsLayoutUtils.h"
     16 #include "ActiveLayerTracker.h"
     17 
     18 namespace mozilla {
     19 namespace layers {
     20 using namespace gfx;
     21 
     22 StackingContextHelper::StackingContextHelper()
     23    : mBuilder(nullptr),
     24      mScale(1.0f, 1.0f),
     25      mAffectsClipPositioning(false),
     26      mDeferredTransformItem(nullptr),
     27      mRasterizeLocally(false) {}
     28 
     29 static nsSize ComputeDesiredDisplaySizeForAnimation(nsIFrame* aContainerFrame) {
     30  // Use the size of the nearest widget as the maximum size.  This
     31  // is important since it might be a popup that is bigger than the
     32  // pres context's size.
     33  nsPresContext* presContext = aContainerFrame->PresContext();
     34  nsIWidget* widget = aContainerFrame->GetNearestWidget();
     35  if (widget) {
     36    return LayoutDevicePixel::ToAppUnits(widget->GetClientSize(),
     37                                         presContext->AppUnitsPerDevPixel());
     38  }
     39 
     40  return presContext->GetVisibleArea().Size();
     41 }
     42 
     43 /* static */
     44 MatrixScales ChooseScale(nsIFrame* aContainerFrame,
     45                         nsDisplayItem* aContainerItem,
     46                         const nsRect& aVisibleRect, float aXScale,
     47                         float aYScale, const Matrix& aTransform2d,
     48                         bool aCanDraw2D) {
     49  MatrixScales scale;
     50  // XXX Should we do something for 3D transforms?
     51  if (aCanDraw2D && !aContainerFrame->Combines3DTransformWithAncestors() &&
     52      !aContainerFrame->HasPerspective()) {
     53    // If the container's transform is animated off main thread, fix a suitable
     54    // scale size for animation
     55    if (aContainerItem &&
     56        aContainerItem->GetType() == DisplayItemType::TYPE_TRANSFORM &&
     57        // FIXME: What we need is only transform, rotate, and scale, not
     58        // translate, so it's be better to use a property set, instead of
     59        // display item type here.
     60        EffectCompositor::HasAnimationsForCompositor(
     61            aContainerFrame, DisplayItemType::TYPE_TRANSFORM)) {
     62      nsSize displaySize =
     63          ComputeDesiredDisplaySizeForAnimation(aContainerFrame);
     64      // compute scale using the animation on the container, taking ancestors in
     65      // to account
     66      nsSize scaledVisibleSize = nsSize(aVisibleRect.Width() * aXScale,
     67                                        aVisibleRect.Height() * aYScale);
     68      scale = nsLayoutUtils::ComputeSuitableScaleForAnimation(
     69          aContainerFrame, scaledVisibleSize, displaySize);
     70      // multiply by the scale inherited from ancestors--we use a uniform
     71      // scale factor to prevent blurring when the layer is rotated.
     72      float incomingScale = std::max(aXScale, aYScale);
     73      scale = scale * ScaleFactor<UnknownUnits, UnknownUnits>(incomingScale);
     74    } else {
     75      // Scale factors are normalized to a power of 2 to reduce the number of
     76      // resolution changes
     77      scale = (aTransform2d * gfx::Matrix::Scaling(aXScale, aYScale))
     78                  .ScaleFactors();
     79      // For frames with a changing scale transform round scale factors up to
     80      // nearest power-of-2 boundary so that we don't keep having to redraw
     81      // the content as it scales up and down. Rounding up to nearest
     82      // power-of-2 boundary ensures we never scale up, only down --- avoiding
     83      // jaggies. It also ensures we never scale down by more than a factor of
     84      // 2, avoiding bad downscaling quality.
     85      Matrix frameTransform;
     86      if (ActiveLayerTracker::IsScaleSubjectToAnimation(aContainerFrame)) {
     87        scale.xScale = gfxUtils::ClampToScaleFactor(scale.xScale);
     88        scale.yScale = gfxUtils::ClampToScaleFactor(scale.yScale);
     89 
     90        // Limit animated scale factors to not grow excessively beyond the
     91        // display size.
     92        nsSize maxScale(4, 4);
     93        if (!aVisibleRect.IsEmpty()) {
     94          nsSize displaySize =
     95              ComputeDesiredDisplaySizeForAnimation(aContainerFrame);
     96          maxScale = Max(maxScale, displaySize / aVisibleRect.Size());
     97        }
     98        if (scale.xScale > maxScale.width) {
     99          scale.xScale = gfxUtils::ClampToScaleFactor(maxScale.width, true);
    100        }
    101        if (scale.yScale > maxScale.height) {
    102          scale.yScale = gfxUtils::ClampToScaleFactor(maxScale.height, true);
    103        }
    104      } else {
    105        // XXX Do we need to move nearly-integer values to integers here?
    106      }
    107    }
    108    // If the scale factors are too small, just use 1.0. The content is being
    109    // scaled out of sight anyway.
    110    if (fabs(scale.xScale) < 1e-8 || fabs(scale.yScale) < 1e-8) {
    111      scale = MatrixScales(1.0, 1.0);
    112    }
    113  } else {
    114    scale = MatrixScales(1.0, 1.0);
    115  }
    116 
    117  // Prevent the scale from getting too large, to avoid excessive memory
    118  // allocation. Usually memory allocation is limited by the visible region,
    119  // which should be restricted to the display port. But at very large scales
    120  // the visible region itself can become excessive due to rounding errors.
    121  // Clamping the scale here prevents that.
    122  return MatrixScales(std::min(scale.xScale, 32768.0f),
    123                      std::min(scale.yScale, 32768.0f));
    124 }
    125 
    126 StackingContextHelper::StackingContextHelper(
    127    const StackingContextHelper& aParentSC, const ActiveScrolledRoot* aAsr,
    128    nsIFrame* aContainerFrame, nsDisplayItem* aContainerItem,
    129    wr::DisplayListBuilder& aBuilder, const wr::StackingContextParams& aParams,
    130    const LayoutDeviceRect& aBounds)
    131    : mBuilder(&aBuilder),
    132      mScale(1.0f, 1.0f),
    133      mDeferredTransformItem(aParams.mDeferredTransformItem),
    134      mRasterizeLocally(aParams.mRasterizeLocally ||
    135                        aParentSC.mRasterizeLocally) {
    136  MOZ_ASSERT(!aContainerItem || aContainerItem->CreatesStackingContextHelper());
    137 
    138  // Compute scale for fallback rendering. We don't try to guess a scale for 3d
    139  // transformed items
    140 
    141  if (aParams.mBoundTransform) {
    142    gfx::Matrix transform2d;
    143    bool canDraw2D = aParams.mBoundTransform->CanDraw2D(&transform2d);
    144    if (canDraw2D &&
    145        aParams.reference_frame_kind != wr::WrReferenceFrameKind::Perspective &&
    146        !aContainerFrame->Combines3DTransformWithAncestors()) {
    147      mInheritedTransform = transform2d * aParentSC.mInheritedTransform;
    148 
    149      int32_t apd = aContainerFrame->PresContext()->AppUnitsPerDevPixel();
    150      nsRect r = LayoutDevicePixel::ToAppUnits(aBounds, apd);
    151      mScale = ChooseScale(aContainerFrame, aContainerItem, r,
    152                           aParentSC.mScale.xScale, aParentSC.mScale.yScale,
    153                           transform2d,
    154                           /* aCanDraw2D = */ true);
    155    } else {
    156      mScale = gfx::MatrixScales(1.0f, 1.0f);
    157      mInheritedTransform = gfx::Matrix::Scaling(1.f, 1.f);
    158    }
    159 
    160    if (aParams.mAnimated) {
    161      mSnappingSurfaceTransform = gfx::Matrix::Scaling(mScale);
    162    } else {
    163      mSnappingSurfaceTransform =
    164          transform2d * aParentSC.mSnappingSurfaceTransform;
    165    }
    166 
    167  } else if (aParams.reference_frame_kind ==
    168                 wr::WrReferenceFrameKind::Transform &&
    169             aContainerItem &&
    170             aContainerItem->GetType() == DisplayItemType::TYPE_ASYNC_ZOOM &&
    171             aContainerItem->Frame()) {
    172    float resolution = aContainerItem->Frame()->PresShell()->GetResolution();
    173    gfx::Matrix transform = gfx::Matrix::Scaling(resolution, resolution);
    174 
    175    mInheritedTransform = transform * aParentSC.mInheritedTransform;
    176    mScale =
    177        ScaleFactor<UnknownUnits, UnknownUnits>(resolution) * aParentSC.mScale;
    178 
    179    MOZ_ASSERT(!aParams.mAnimated);
    180    mSnappingSurfaceTransform = transform * aParentSC.mSnappingSurfaceTransform;
    181 
    182  } else if (!aAsr && !aContainerFrame && !aContainerItem &&
    183             aParams.mRootReferenceFrame) {
    184    // this is the root stacking context helper
    185    Scale2D resolution;
    186 
    187    // If we are in a remote browser, then apply scaling from ancestor browsers
    188    if (mozilla::dom::BrowserChild* browserChild =
    189            mozilla::dom::BrowserChild::GetFrom(
    190                aParams.mRootReferenceFrame->PresShell())) {
    191      resolution = browserChild->GetEffectsInfo().mRasterScale;
    192    }
    193 
    194    gfx::Matrix transform =
    195        gfx::Matrix::Scaling(resolution.xScale, resolution.yScale);
    196 
    197    mInheritedTransform = transform * aParentSC.mInheritedTransform;
    198    mScale = aParentSC.mScale * resolution;
    199 
    200    MOZ_ASSERT(!aParams.mAnimated);
    201    mSnappingSurfaceTransform = transform * aParentSC.mSnappingSurfaceTransform;
    202 
    203  } else {
    204    mInheritedTransform = aParentSC.mInheritedTransform;
    205    mScale = aParentSC.mScale;
    206  }
    207 
    208  auto rasterSpace =
    209      mRasterizeLocally
    210          ? wr::RasterSpace::Local(std::max(mScale.xScale, mScale.yScale))
    211          : wr::RasterSpace::Screen();
    212 
    213  MOZ_ASSERT(!aParams.clip.IsNone());
    214  mReferenceFrameId = mBuilder->PushStackingContext(
    215      aParams, wr::ToLayoutRect(aBounds), rasterSpace);
    216 
    217  if (mReferenceFrameId) {
    218    mSpaceAndClipChainHelper.emplace(aBuilder, mReferenceFrameId.ref());
    219  }
    220 
    221  mAffectsClipPositioning =
    222      mReferenceFrameId.isSome() || (aBounds.TopLeft() != LayoutDevicePoint());
    223 
    224  // If the parent stacking context has a deferred transform item, inherit it
    225  // into this stacking context, as long as the ASR hasn't changed. Refer to
    226  // the comments on StackingContextHelper::mDeferredTransformItem for an
    227  // explanation of what goes in these fields.
    228  if (aParentSC.mDeferredTransformItem &&
    229      aAsr == aParentSC.mDeferredTransformItem->GetActiveScrolledRoot()) {
    230    if (mDeferredTransformItem) {
    231      // If we are deferring another transform, put the combined transform from
    232      // all the ancestor deferred items into mDeferredAncestorTransform
    233      mDeferredAncestorTransform = aParentSC.GetDeferredTransformMatrix();
    234    } else {
    235      // We are not deferring another transform, so we can just inherit the
    236      // parent stacking context's deferred data without any modification.
    237      mDeferredTransformItem = aParentSC.mDeferredTransformItem;
    238      mDeferredAncestorTransform = aParentSC.mDeferredAncestorTransform;
    239    }
    240  }
    241 }
    242 
    243 StackingContextHelper::~StackingContextHelper() {
    244  if (mBuilder) {
    245    mSpaceAndClipChainHelper.reset();
    246    mBuilder->PopStackingContext(mReferenceFrameId.isSome());
    247  }
    248 }
    249 
    250 nsDisplayTransform* StackingContextHelper::GetDeferredTransformItem() const {
    251  return mDeferredTransformItem;
    252 }
    253 
    254 Maybe<gfx::Matrix4x4> StackingContextHelper::GetDeferredTransformMatrix()
    255    const {
    256  if (mDeferredTransformItem) {
    257    // See the comments on StackingContextHelper::mDeferredTransformItem for
    258    // an explanation of what's stored in mDeferredTransformItem and
    259    // mDeferredAncestorTransform. Here we need to return the combined transform
    260    // transform from all the deferred ancestors, including
    261    // mDeferredTransformItem.
    262    gfx::Matrix4x4 result = mDeferredTransformItem->GetTransform().GetMatrix();
    263    if (mDeferredAncestorTransform) {
    264      result = result * *mDeferredAncestorTransform;
    265    }
    266    return Some(result);
    267  } else {
    268    return Nothing();
    269  }
    270 }
    271 
    272 void StackingContextHelper::ClearDeferredTransformItem() const {
    273  mDeferredTransformItem = nullptr;
    274 }
    275 
    276 void StackingContextHelper::RestoreDeferredTransformItem(
    277    nsDisplayTransform* aItem) const {
    278  mDeferredTransformItem = aItem;
    279 }
    280 
    281 }  // namespace layers
    282 }  // namespace mozilla