Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/gfx/layers/wr/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 27 kB image not shown  

Quelle  ClipManager.cpp

  Sprache: C
 

/* 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/ClipManager.h"

#include "DisplayItemClipChain.h"
#include "FrameMetrics.h"
#include "mozilla/ScrollContainerFrame.h"
#include "mozilla/dom/Document.h"
#include "mozilla/layers/StackingContextHelper.h"
#include "mozilla/layers/WebRenderLayerManager.h"
#include "mozilla/StaticPrefs_apz.h"
#include "mozilla/webrender/WebRenderAPI.h"
#include "nsDisplayList.h"
#include "nsLayoutUtils.h"
#include "nsRefreshDriver.h"
#include "nsStyleStructInlines.h"
#include "UnitTransforms.h"

static mozilla::LazyLogModule sClipLog("wr.clip");
#define CLIP_LOG(...) MOZ_LOG(sClipLog, LogLevel::Debug, (__VA_ARGS__))

namespace mozilla::layers {

ClipManager::ClipManager() : mManager(nullptr), mBuilder(nullptr) {}

void ClipManager::PushCacheScope() {
  if (mCacheStackTop < mCacheStack.size()) {
    mCacheStack[mCacheStackTop].clear();
  } else {
    mCacheStack.emplace_back();
  }
  mCacheStackTop++;
}

void ClipManager::PopCacheScope() {
  MOZ_ASSERT(mCacheStackTop > 0);
  mCacheStackTop--;
}

void ClipManager::BeginBuild(WebRenderLayerManager* aManager,
                             wr::DisplayListBuilder& aBuilder) {
  MOZ_ASSERT(!mManager);
  mManager = aManager;
  MOZ_ASSERT(!mBuilder);
  mBuilder = &aBuilder;
  MOZ_ASSERT(mCacheStackTop == 0);
  PushCacheScope();
  MOZ_ASSERT(mASROverride.empty());
  MOZ_ASSERT(mItemClipStack.empty());
}

void ClipManager::EndBuild() {
  mBuilder = nullptr;
  mManager = nullptr;
  PopCacheScope();
  MOZ_ASSERT(mCacheStackTop == 0);
  MOZ_ASSERT(mASROverride.empty());
  MOZ_ASSERT(mItemClipStack.empty());
}

void ClipManager::BeginList(const StackingContextHelper& aStackingContext) {
  CLIP_LOG("begin list %p affects = %d, ref-frame = %d\n", &aStackingContext,
           aStackingContext.AffectsClipPositioning(),
           aStackingContext.ReferenceFrameId().isSome());

  ItemClips clips(nullptr, nullptr, 0);
  if (!mItemClipStack.empty()) {
    clips = mItemClipStack.top();
  }

  if (aStackingContext.AffectsClipPositioning()) {
    if (auto referenceFrameId = aStackingContext.ReferenceFrameId()) {
      PushOverrideForASR(clips.mASR, *referenceFrameId);
      clips.mScrollId = *referenceFrameId;
    } else {
      // Start a new cache
      PushCacheScope();
    }
    // Ensure we recreate the chain id if needed.
    clips.mClipChainId.reset();
  }

  CLIP_LOG("  push: clip: %p, asr: %p, scroll =%" PRIuPTR ", clip =%" PRIu64
           "\n",
           clips.mChain, clips.mASR, clips.mScrollId.id,
           clips.mClipChainId.valueOr(wr::WrClipChainId{0}).id);

  mItemClipStack.push(clips);
}

void ClipManager::EndList(const StackingContextHelper& aStackingContext) {
  MOZ_ASSERT(!mItemClipStack.empty());

  CLIP_LOG("end list %p\n", &aStackingContext);

  mItemClipStack.pop();

  if (aStackingContext.AffectsClipPositioning()) {
    if (aStackingContext.ReferenceFrameId()) {
      PopOverrideForASR(mItemClipStack.empty() ? nullptr
                                               : mItemClipStack.top().mASR);
    } else {
      PopCacheScope();
    }
  }
}

void ClipManager::PushOverrideForASR(const ActiveScrolledRoot* aASR,
                                     const wr::WrSpatialId& aSpatialId) {
  wr::WrSpatialId space = GetSpatialId(aASR);

  CLIP_LOG("Pushing %p override %zu -> %zu\n", aASR, space.id, aSpatialId.id);
  auto it = mASROverride.insert({space, std::stack<wr::WrSpatialId>()});
  it.first->second.push(aSpatialId);

  // Start a new cache
  PushCacheScope();

  // Fix up our cached item clip if needed.
  if (!mItemClipStack.empty()) {
    auto& top = mItemClipStack.top();
    if (top.mASR == aASR) {
      top.mScrollId = aSpatialId;
      top.mClipChainId.reset();
    }
  }
}

void ClipManager::PopOverrideForASR(const ActiveScrolledRoot* aASR) {
  PopCacheScope();

  wr::WrSpatialId space = GetSpatialId(aASR);
  auto it = mASROverride.find(space);
  if (it == mASROverride.end()) {
    MOZ_ASSERT_UNREACHABLE("Push/PopOverrideForASR should be balanced");
  } else {
    CLIP_LOG("Popping %p override %zu -> %zu\n", aASR, space.id,
             it->second.top().id);
    it->second.pop();
  }

  if (!mItemClipStack.empty()) {
    auto& top = mItemClipStack.top();
    if (top.mASR == aASR) {
      top.mScrollId = (it == mASROverride.end() || it->second.empty())
                          ? space
                          : it->second.top();
      top.mClipChainId.reset();
    }
  }

  if (it != mASROverride.end() && it->second.empty()) {
    mASROverride.erase(it);
  }
}

wr::WrSpatialId ClipManager::SpatialIdAfterOverride(
    const wr::WrSpatialId& aSpatialId) {
  auto it = mASROverride.find(aSpatialId);
  if (it == mASROverride.end()) {
    return aSpatialId;
  }
  MOZ_ASSERT(!it->second.empty());
  CLIP_LOG("Overriding %zu with %zu\n", aSpatialId.id, it->second.top().id);
  return it->second.top();
}

wr::WrSpaceAndClipChain ClipManager::SwitchItem(nsDisplayListBuilder* aBuilder,
                                                nsDisplayItem* aItem) {
  const DisplayItemClipChain* clip = aItem->GetClipChain();
  const DisplayItemClipChain* inheritedClipChain =
      mBuilder->GetInheritedClipChain();
  if (inheritedClipChain && inheritedClipChain != clip) {
    if (!clip) {
      clip = mBuilder->GetInheritedClipChain();
    } else {
      clip = aBuilder->CreateClipChainIntersection(
          mBuilder->GetInheritedClipChain(), clip);
    }
  }
  const ActiveScrolledRoot* asr = aItem->GetActiveScrolledRoot();
  DisplayItemType type = aItem->GetType();
  const ActiveScrolledRoot* stickyAsr = nullptr;
  if (type == DisplayItemType::TYPE_STICKY_POSITION) {
    // For sticky position items, the ASR is computed differently depending on
    // whether the item has a fixed descendant or not. But for WebRender
    // purposes we always want to use the ASR that would have been used if it
    // didn't have fixed descendants, which is stored as the "container ASR" on
    // the sticky item.
    auto* sticky = static_cast<nsDisplayStickyPosition*>(aItem);
    asr = sticky->GetContainerASR();
    stickyAsr = ActiveScrolledRoot::GetStickyASRFromFrame(sticky->Frame());
    MOZ_ASSERT(stickyAsr);
  }

  CLIP_LOG("processing item %p (%s) asr %p clip %p, inherited = %p\n", aItem,
           DisplayItemTypeName(aItem->GetType()), asr, clip,
           inheritedClipChain);

  // Zoom display items report their bounds etc using the parent document's
  // APD because zoom items act as a conversion layer between the two different
  // APDs.
  const int32_t auPerDevPixel = [&] {
    if (type == DisplayItemType::TYPE_ZOOM) {
      return static_cast<nsDisplayZoom*>(aItem)->GetParentAppUnitsPerDevPixel();
    }
    return aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
  }();

  ItemClips clips(asr, clip, auPerDevPixel);
  MOZ_ASSERT(!mItemClipStack.empty());
  if (clips.HasSameInputs(mItemClipStack.top())) {
    // Early-exit because if the clips are the same as aItem's previous sibling,
    // then we don't need to do do the work of popping the old stuff and then
    // pushing it right back on for the new item. Note that if aItem doesn't
    // have a previous sibling, that means BeginList would have been called
    // just before this, which will have pushed a ItemClips(nullptr, nullptr)
    // onto mItemClipStack, so the HasSameInputs check should return false.
    CLIP_LOG("\tearly-exit for %p\n", aItem);
    auto& clips = mItemClipStack.top();
    if (!clips.mClipChainId && clips.mChain) {
      clips.mClipChainId =
          DefineClipChain(clips.mChain, clips.mAppUnitsPerDevPixel);
    }
    return wr::WrSpaceAndClipChain{clips.mScrollId, clips.mClipChainId
                                                        ? clips.mClipChainId->id
                                                        : wr::ROOT_CLIP_CHAIN};
  }

  // Pop aItem's previous sibling's stuff from mBuilder in preparation for
  // pushing aItem's stuff.
  mItemClipStack.pop();

  // There are up to three ASR chains here that we need to be fully defined:
  //  1. The ASR chain pointed to by |asr|
  //  2. The ASR chain pointed to by clip->mASR
  //  3. For a sticky item, the ASR chain pointed to by the item's sticky ASR.
  //     This one is needed so that when we create WebRender commands for the
  //     sticky item, we can give WebRender accurate information about the
  //     spatial node we're in.
  // These chains will often be the same, or one will include the other,
  // but we can't rely on that always being the case, so we make a
  // DefineSpatialNodes call on all three. These calls will recursively
  // define all the ASRs that we care about for this item, but will not
  // actually push anything onto the WR stack.
  (void)DefineSpatialNodes(aBuilder, asr, aItem);
  if (clip && clip->mASR != asr) {
    (void)DefineSpatialNodes(aBuilder, clip->mASR, aItem);
  }
  if (stickyAsr && stickyAsr != asr) {
    (void)DefineSpatialNodes(aBuilder, stickyAsr, aItem);
  }

  // Define all the clips in the item's clip chain, and obtain a clip chain id
  // for it.
  clips.mClipChainId = DefineClipChain(clip, auPerDevPixel);

  wr::WrSpatialId space = GetSpatialId(asr);
  clips.mScrollId = SpatialIdAfterOverride(space);
  CLIP_LOG("\tassigning %d -> %d\n", (int)space.id, (int)clips.mScrollId.id);

  // Now that we have the scroll id and a clip id for the item, push it onto
  // the WR stack.
  const wr::WrSpaceAndClipChain spaceAndClipChain{
      clips.mScrollId,
      clips.mClipChainId ? clips.mClipChainId->id : wr::ROOT_CLIP_CHAIN};

  CLIP_LOG("  push: clip: %p, asr: %p, scroll = %" PRIuPTR ", clip = %" PRIu64
           "\n",
           clips.mChain, clips.mASR, spaceAndClipChain.space.id,
           spaceAndClipChain.clip_chain);

  mItemClipStack.push(clips);

  CLIP_LOG("done setup for %p\n", aItem);
  return spaceAndClipChain;
}

wr::WrSpatialId ClipManager::GetSpatialId(const ActiveScrolledRoot* aASR) {
  for (const ActiveScrolledRoot* asr = aASR; asr; asr = asr->mParent) {
    // The map handles both sticky and scroll ASRs
    Maybe<wr::WrSpatialId> space = mBuilder->GetSpatialIdForDefinedLayer(asr);

    if (space) {
      return *space;
    }
  }
  // If this ASR doesn't have a spatial ID, then we should check its ancestor.
  // This can happen if e.g. we failed to get scroll metadata for a scroll ASR.

  Maybe<wr::WrSpatialId> space = mBuilder->GetSpatialIdForDefinedLayer(nullptr);
  MOZ_ASSERT(space.isSome());
  return *space;
}

StickyScrollContainer* ClipManager::GetStickyScrollContainer(
    const ActiveScrolledRoot* aASR) {
  MOZ_ASSERT(aASR->mKind == ActiveScrolledRoot::ASRKind::Sticky);
  StickyScrollContainer* stickyScrollContainer =
      StickyScrollContainer::GetOrCreateForFrame(aASR->mFrame);
  if (stickyScrollContainer) {
    // If there's no ASR for the scrollframe that this sticky item is attached
    // to, then don't create a WR sticky item for it either. Trying to do so
    // will end in sadness because WR will interpret some coordinates as
    // relative to the nearest enclosing scrollframe, which will correspond
    // to the nearest ancestor ASR on the gecko side. That ASR will not be the
    // same as the scrollframe this sticky item is actually supposed to be
    // attached to, thus the sadness.
    // Not sending WR the sticky item is ok, because the enclosing scrollframe
    // will never be asynchronously scrolled. Instead we will always position
    // the sticky items correctly on the gecko side and WR will never need to
    // adjust their position itself.
    if (!stickyScrollContainer->ScrollContainer()
             ->IsMaybeAsynchronouslyScrolled()) {
      stickyScrollContainer = nullptr;
    }
  }
  return stickyScrollContainer;
}

// Returns the smallest distance from "0" to the range [min, max] where
// min <= max. Despite the name, the return value is actually a 1-D vector,
// and so may be negative if max < 0.
static nscoord DistanceToRange(nscoord min, nscoord max) {
  MOZ_ASSERT(min <= max);
  if (max < 0) {
    return max;
  }
  if (min > 0) {
    return min;
  }
  MOZ_ASSERT(min <= 0 && max >= 0);
  return 0;
}

// Returns the magnitude of the part of the range [min, max] that is greater
// than zero. The return value is always non-negative.
static nscoord PositivePart(nscoord min, nscoord max) {
  MOZ_ASSERT(min <= max);
  if (min >= 0) {
    return max - min;
  }
  if (max > 0) {
    return max;
  }
  return 0;
}

// Returns the magnitude of the part of the range [min, max] that is less
// than zero. The return value is always non-negative.
static nscoord NegativePart(nscoord min, nscoord max) {
  MOZ_ASSERT(min <= max);
  if (max <= 0) {
    return max - min;
  }
  if (min < 0) {
    return 0 - min;
  }
  return 0;
}

Maybe<wr::WrSpatialId> ClipManager::DefineStickyNode(
    nsDisplayListBuilder* aBuilder, Maybe<wr::WrSpatialId> aParentSpatialId,
    const ActiveScrolledRoot* aASR, nsDisplayItem* aItem) {
  nsIFrame* stickyFrame = aASR->mFrame;

  StickyScrollContainer* stickyScrollContainer = GetStickyScrollContainer(aASR);
  if (!stickyScrollContainer) {
    // This may indicated a sticky item that does not need a webrender spatial
    // node. See the comment in GetStickyScrollContainer for details.
    return Nothing();
  }

  // Do not create a spatial node for sticky items with mShouldFlatten=true.
  // These are inside inactive scroll frames and so cannot move asynchronously.
  if (stickyScrollContainer->ShouldFlattenAway()) {
    return Nothing();
  }

  float auPerDevPixel = stickyFrame->PresContext()->AppUnitsPerDevPixel();

  nsRect itemBounds;

  // Make both itemBounds and scrollPort be relative to the reference frame
  // of the sticky frame's parent.
  nsRect scrollPort =
      stickyScrollContainer->ScrollContainer()->GetScrollPortRect();
  const nsIFrame* referenceFrame =
      aBuilder->FindReferenceFrameFor(stickyFrame->GetParent());
  nsRect transformedBounds = stickyFrame->GetRectRelativeToSelf();
  DebugOnly transformResult = nsLayoutUtils::TransformRect(
      stickyFrame, referenceFrame, transformedBounds);
  MOZ_ASSERT(transformResult == nsLayoutUtils::TRANSFORM_SUCCEEDED);
  itemBounds = transformedBounds;
  scrollPort = scrollPort +
               stickyScrollContainer->ScrollContainer()->GetOffsetToCrossDoc(
                   referenceFrame);

  Maybe<float> topMargin;
  Maybe<float> rightMargin;
  Maybe<float> bottomMargin;
  Maybe<float> leftMargin;
  wr::StickyOffsetBounds vBounds = {0.00.0};
  wr::StickyOffsetBounds hBounds = {0.00.0};
  nsPoint appliedOffset;

  nsRectAbsolute outer;
  nsRectAbsolute inner;
  stickyScrollContainer->GetScrollRanges(stickyFrame, &outer, &inner);

  // The following computations make more sense upon understanding the
  // semantics of "inner" and "outer", which is explained in the comment on
  // SetStickyPositionData in Layers.h.

  if (outer.YMost() != inner.YMost()) {
    // Question: How far will itemBounds.y be from the top of the scrollport
    // when we have scrolled from the current scroll position of "0" to
    // reach the range [inner.YMost(), outer.YMost()] where the item gets
    // stuck?
    // Answer: the current distance is "itemBounds.y - scrollPort.y". That
    // needs to be adjusted by the distance to the range, less any other
    // sticky ranges that fall between 0 and the range. If the distance is
    // negative (i.e. inner.YMost() <= outer.YMost() < 0) then we would be
    // scrolling upwards (decreasing scroll offset) to reach that range,
    // which would increase itemBounds.y and make it farther away from the
    // top of the scrollport. So in that case the adjustment is -distance.
    // If the distance is positive (0 < inner.YMost() <= outer.YMost()) then
    // we would be scrolling downwards, itemBounds.y would decrease, and we
    // again need to adjust by -distance. If we are already in the range
    // then no adjustment is needed and distance is 0 so again using
    // -distance works. If the distance is positive, and the item has both
    // top and bottom sticky ranges, then the bottom sticky range may fall
    // (entirely[1] or partly[2]) between the current scroll position.
    // [1]: 0 <= outer.Y() <= inner.Y() < inner.YMost() <= outer.YMost()
    // [2]: outer.Y() < 0 <= inner.Y() < inner.YMost() <= outer.YMost()
    // In these cases, the item doesn't actually move for that part of the
    // distance, so we need to subtract out that bit, which can be computed
    // as the positive portion of the range [outer.Y(), inner.Y()].
    nscoord distance = DistanceToRange(inner.YMost(), outer.YMost());
    if (distance > 0) {
      distance -= PositivePart(outer.Y(), inner.Y());
    }
    topMargin = Some(NSAppUnitsToFloatPixels(
        itemBounds.y - scrollPort.y - distance, auPerDevPixel));
    // Question: What is the maximum positive ("downward") offset that WR
    // will have to apply to this item in order to prevent the item from
    // visually moving?
    // Answer: Since the item is "sticky" in the range [inner.YMost(),
    // outer.YMost()], the maximum offset will be the size of the range, which
    // is outer.YMost() - inner.YMost().
    vBounds.max =
        NSAppUnitsToFloatPixels(outer.YMost() - inner.YMost(), auPerDevPixel);
    // Question: how much of an offset has layout already applied to the item?
    // Answer: if we are
    // (a) inside the sticky range (inner.YMost() < 0 <= outer.YMost()), or
    // (b) past the sticky range (inner.YMost() < outer.YMost() < 0)
    // then layout has already applied some offset to the position of the
    // item. The amount of the adjustment is |0 - inner.YMost()| in case (a)
    // and |outer.YMost() - inner.YMost()| in case (b).
    if (inner.YMost() < 0) {
      appliedOffset.y = std::min(0, outer.YMost()) - inner.YMost();
      MOZ_ASSERT(appliedOffset.y > 0);
    }
  }
  if (outer.Y() != inner.Y()) {
    // Similar logic as in the previous section, but this time we care about
    // the distance from itemBounds.YMost() to scrollPort.YMost().
    nscoord distance = DistanceToRange(outer.Y(), inner.Y());
    if (distance < 0) {
      distance += NegativePart(inner.YMost(), outer.YMost());
    }
    bottomMargin = Some(NSAppUnitsToFloatPixels(
        scrollPort.YMost() - itemBounds.YMost() + distance, auPerDevPixel));
    // And here WR will be moving the item upwards rather than downwards so
    // again things are inverted from the previous block.
    vBounds.min = NSAppUnitsToFloatPixels(outer.Y() - inner.Y(), auPerDevPixel);
    // We can't have appliedOffset be both positive and negative, and the top
    // adjustment takes priority. So here we only update appliedOffset.y if
    // it wasn't set by the top-sticky case above.
    if (appliedOffset.y == 0 && inner.Y() > 0) {
      appliedOffset.y = std::max(0, outer.Y()) - inner.Y();
      MOZ_ASSERT(appliedOffset.y < 0);
    }
  }
  // Same as above, but for the x-axis
  if (outer.XMost() != inner.XMost()) {
    nscoord distance = DistanceToRange(inner.XMost(), outer.XMost());
    if (distance > 0) {
      distance -= PositivePart(outer.X(), inner.X());
    }
    leftMargin = Some(NSAppUnitsToFloatPixels(
        itemBounds.x - scrollPort.x - distance, auPerDevPixel));
    hBounds.max =
        NSAppUnitsToFloatPixels(outer.XMost() - inner.XMost(), auPerDevPixel);
    if (inner.XMost() < 0) {
      appliedOffset.x = std::min(0, outer.XMost()) - inner.XMost();
      MOZ_ASSERT(appliedOffset.x > 0);
    }
  }
  if (outer.X() != inner.X()) {
    nscoord distance = DistanceToRange(outer.X(), inner.X());
    if (distance < 0) {
      distance += NegativePart(inner.XMost(), outer.XMost());
    }
    rightMargin = Some(NSAppUnitsToFloatPixels(
        scrollPort.XMost() - itemBounds.XMost() + distance, auPerDevPixel));
    hBounds.min = NSAppUnitsToFloatPixels(outer.X() - inner.X(), auPerDevPixel);
    if (appliedOffset.x == 0 && inner.X() > 0) {
      appliedOffset.x = std::max(0, outer.X()) - inner.X();
      MOZ_ASSERT(appliedOffset.x < 0);
    }
  }

  LayoutDeviceRect bounds =
      LayoutDeviceRect::FromAppUnits(itemBounds, auPerDevPixel);
  wr::LayoutVector2D applied = {
      NSAppUnitsToFloatPixels(appliedOffset.x, auPerDevPixel),
      NSAppUnitsToFloatPixels(appliedOffset.y, auPerDevPixel)};
  bool needsProp =
      nsDisplayStickyPosition::ShouldGetStickyAnimationId(stickyFrame);
  Maybe<wr::WrAnimationProperty> prop;
  if (needsProp) {
    auto displayItemKey = nsDisplayItem::GetPerFrameKey(
        00, DisplayItemType::TYPE_STICKY_POSITION);
    RefPtr<WebRenderAPZAnimationData> animationData =
        mManager->CommandBuilder()
            .CreateOrRecycleWebRenderUserData<WebRenderAPZAnimationData>(
                displayItemKey, stickyFrame);
    uint64_t animationId = animationData->GetAnimationId();

    prop.emplace();
    prop->id = animationId;
    prop->effect_type = wr::WrAnimationType::Transform;
  }
  wr::WrSpatialId spatialId = mBuilder->DefineStickyFrame(
      aASR, aParentSpatialId, wr::ToLayoutRect(bounds),
      topMargin.ptrOr(nullptr), rightMargin.ptrOr(nullptr),
      bottomMargin.ptrOr(nullptr), leftMargin.ptrOr(nullptr), vBounds, hBounds,
      applied, prop.ptrOr(nullptr));

  return Some(spatialId);
}

Maybe<wr::WrSpatialId> ClipManager::DefineSpatialNodes(
    nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR,
    nsDisplayItem* aItem) {
  if (!aASR) {
    // Recursion base case
    return Nothing();
  }

  Maybe<wr::WrSpatialId> space = mBuilder->GetSpatialIdForDefinedLayer(aASR);
  if (space) {
    // If we've already defined this layer before, we can early-exit
    return space;
  }

  // Recurse to define the ancestors
  Maybe<wr::WrSpatialId> ancestorSpace =
      DefineSpatialNodes(aBuilder, aASR->mParent, aItem);

  Maybe<wr::WrSpatialId> parent = ancestorSpace;
  if (parent) {
    *parent = SpatialIdAfterOverride(*parent);
  }

  if (aASR->mKind == ActiveScrolledRoot::ASRKind::Sticky) {
    return ClipManager::DefineStickyNode(aBuilder, parent, aASR, aItem);
  }

  MOZ_ASSERT(aASR->mKind == ActiveScrolledRoot::ASRKind::Scroll);
  ScrollableLayerGuid::ViewID viewId = aASR->GetViewId();

  MOZ_ASSERT(viewId != ScrollableLayerGuid::NULL_SCROLL_ID);

  ScrollContainerFrame* scrollContainerFrame = aASR->ScrollFrame();
  Maybe<ScrollMetadata> metadata = scrollContainerFrame->ComputeScrollMetadata(
      mManager, aItem->Frame(), aItem->ToReferenceFrame());
  if (!metadata) {
    MOZ_ASSERT_UNREACHABLE("Expected scroll metadata to be available!");
    return ancestorSpace;
  }

  FrameMetrics& metrics = metadata->GetMetrics();
  if (!metrics.IsScrollable()) {
    // This item is a scrolling no-op, skip over it in the ASR chain.
    return ancestorSpace;
  }

  nsPoint offset = scrollContainerFrame->GetOffsetToCrossDoc(aItem->Frame()) +
                   aItem->ToReferenceFrame();
  int32_t auPerDevPixel = aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
  nsRect scrollPort = scrollContainerFrame->GetScrollPortRect() + offset;
  LayoutDeviceRect clipBounds =
      LayoutDeviceRect::FromAppUnits(scrollPort, auPerDevPixel);

  // The content rect that we hand to PushScrollLayer should be relative to
  // the same origin as the clipBounds that we hand to PushScrollLayer -
  // that is, both of them should be relative to the stacking context `aSc`.
  // However, when we get the scrollable rect from the FrameMetrics, the
  // origin has nothing to do with the position of the frame but instead
  // represents the minimum allowed scroll offset of the scrollable content.
  // While APZ uses this to clamp the scroll position, we don't need to send
  // this to WebRender at all. Instead, we take the position from the
  // composition bounds.
  LayoutDeviceRect contentRect =
      metrics.GetExpandedScrollableRect() * metrics.GetDevPixelsPerCSSPixel();
  contentRect.MoveTo(clipBounds.TopLeft());

  // The external scroll offset is accumulated into the local space positions of
  // display items inside WR, so that the elements hash (intern) to the same
  // content ID for quick comparisons. To avoid invalidations when the
  // auPerDevPixel is not a round value, round here directly from app units.
  // This guarantees we won't introduce any inaccuracy in the external scroll
  // offset passed to WR.
  const bool useRoundedOffset =
      StaticPrefs::apz_rounded_external_scroll_offset();
  LayoutDevicePoint scrollOffset =
      useRoundedOffset
          ? LayoutDevicePoint::FromAppUnitsRounded(
                scrollContainerFrame->GetScrollPosition(), auPerDevPixel)
          : LayoutDevicePoint::FromAppUnits(
                scrollContainerFrame->GetScrollPosition(), auPerDevPixel);

  // Currently we track scroll-linked effects at the granularity of documents,
  // not scroll frames, so we consider a scroll frame to have a scroll-linked
  // effect whenever its containing document does.
  nsPresContext* presContext = aItem->Frame()->PresContext();
  const bool hasScrollLinkedEffect =
      !StaticPrefs::apz_disable_for_scroll_linked_effects() &&
      presContext->Document()->HasScrollLinkedEffect();

  return Some(mBuilder->DefineScrollLayer(
      aASR, viewId, parent, wr::ToLayoutRect(contentRect),
      wr::ToLayoutRect(clipBounds), wr::ToLayoutVector2D(scrollOffset),
      wr::ToWrAPZScrollGeneration(
          scrollContainerFrame->ScrollGenerationOnApz()),
      wr::ToWrHasScrollLinkedEffect(hasScrollLinkedEffect)));
}

Maybe<wr::WrClipChainId> ClipManager::DefineClipChain(
    const DisplayItemClipChain* aChain, int32_t aAppUnitsPerDevPixel) {
  MOZ_ASSERT(mCacheStackTop > 0);
  if (!aChain) {
    return Nothing();
  }

  ClipIdMap& cache = mCacheStack[mCacheStackTop - 1];
  MOZ_DIAGNOSTIC_ASSERT(aChain->mOnStack || !aChain->mASR ||
                        aChain->mASR->mFrame);

  if (auto iter = cache.find(aChain); iter != cache.end()) {
    // Found it in the currently-active cache, so just use the id we have for
    // it.
    CLIP_LOG("cache[%p] => hit\n", aChain);
    return iter->second.mWrChainID;
  }

  const auto parentChain =
      DefineClipChain(aChain->mParent, aAppUnitsPerDevPixel);
  if (!aChain->mClip.HasClip()) {
    cache[aChain] = {parentChain};
    // This item in the chain is a no-op, skip over it
    return parentChain;
  }

  auto clip = LayoutDeviceRect::FromAppUnits(aChain->mClip.GetClipRect(),
                                             aAppUnitsPerDevPixel);
  AutoTArray<wr::ComplexClipRegion, 6> wrRoundedRects;
  aChain->mClip.ToComplexClipRegions(aAppUnitsPerDevPixel, wrRoundedRects);
  wr::WrSpatialId space = GetSpatialId(aChain->mASR);
  // Define the clip
  space = SpatialIdAfterOverride(space);
  // Iterate through the clips in the current item's clip chain, define them
  // in WR, and put their IDs into |clipIds|.
  AutoTArray<wr::WrClipId, 6> clipChainClipIds;
  auto rectClipId =
      mBuilder->DefineRectClip(Some(space), wr::ToLayoutRect(clip));
  CLIP_LOG("cache[%p] <= %zu\n", aChain, rectClipId.id);
  clipChainClipIds.AppendElement(rectClipId);

  for (const auto& complexClip : wrRoundedRects) {
    auto complexClipId =
        mBuilder->DefineRoundedRectClip(Some(space), complexClip);
    CLIP_LOG("cache[%p] <= %zu\n", aChain, complexClipId.id);
    clipChainClipIds.AppendElement(complexClipId);
  }
  auto id = Some(mBuilder->DefineClipChain(clipChainClipIds, parentChain));
  cache[aChain] = {id};
  return id;
}

ClipManager::~ClipManager() {
  MOZ_ASSERT(!mBuilder);
  MOZ_ASSERT(mCacheStackTop == 0);
  MOZ_ASSERT(mItemClipStack.empty());
}

ClipManager::ItemClips::ItemClips(const ActiveScrolledRoot* aASR,
                                  const DisplayItemClipChain* aChain,
                                  int32_t aAppUnitsPerDevPixel)
    : mASR(aASR), mChain(aChain), mAppUnitsPerDevPixel(aAppUnitsPerDevPixel) {
  mScrollId = wr::wr_root_scroll_node_id();
}

bool ClipManager::ItemClips::HasSameInputs(const ItemClips& aOther) {
  if (mASR != aOther.mASR || mChain != aOther.mChain) {
    return false;
  }
  // AUPDP only matters if we have a clip chain, since it's only used to compute
  // the device space clip rect.
  if (mChain && mAppUnitsPerDevPixel != aOther.mAppUnitsPerDevPixel) {
    return false;
  }
  return true;
}

}  // namespace mozilla::layers

Messung V0.5 in Prozent
C=93 H=95 G=93

¤ Dauer der Verarbeitung: 0.9 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.