Bug 1367488 - Pre-render offscreen portions of scrollbar thumbs inside an iframe. r=mstange
MozReview-Commit-ID: LCBHnFJdGtp
--- a/layout/painting/FrameLayerBuilder.cpp
+++ b/layout/painting/FrameLayerBuilder.cpp
@@ -3928,16 +3928,23 @@ ContainerState::SetupMaskLayerForCSSMask
return;
}
maskLayer->SetContainer(imgContainer);
*oldUserData = Move(newUserData);
aLayer->SetMaskLayer(maskLayer);
}
+static bool
+IsScrollThumbLayer(nsDisplayItem* aItem)
+{
+ return aItem->GetType() == nsDisplayItem::TYPE_OWN_LAYER &&
+ static_cast<nsDisplayOwnLayer*>(aItem)->IsScrollThumbLayer();
+}
+
/*
* Iterate through the non-clip items in aList and its descendants.
* For each item we compute the effective clip rect. Each item is assigned
* to a layer. We invalidate the areas in PaintedLayers where an item
* has moved from one PaintedLayer to another. Also,
* aState->mInvalidPaintedContent is invalidated in every PaintedLayer.
* We set the clip rect for items that generated their own layer, and
* create a mask layer to do any rounded rect clipping.
@@ -4197,18 +4204,19 @@ ContainerState::ProcessDisplayItems(nsDi
// the whole clip chain is always just one fused clip.
// Bug 1336516 aims to change that and to remove this workaround.
AnimatedGeometryRoot* clipAGR = item->AnimatedGeometryRootForScrollMetadata();
nsIntRect scrolledClipRect =
ScaleToNearestPixels(itemClip.GetClipRect()) + mParameters.mOffset;
mPaintedLayerDataTree.AddingOwnLayer(clipAGR,
&scrolledClipRect,
uniformColorPtr);
- } else if (*animatedGeometryRoot == item->Frame() &&
- *animatedGeometryRoot != mBuilder->RootReferenceFrame()) {
+ } else if ((*animatedGeometryRoot == item->Frame() &&
+ *animatedGeometryRoot != mBuilder->RootReferenceFrame()) ||
+ (IsScrollThumbLayer(item) && mManager->IsWidgetLayerManager())) {
// This is the case for scrollbar thumbs, for example. In that case the
// clip we care about is the overflow:hidden clip on the scrollbar.
mPaintedLayerDataTree.AddingOwnLayer(animatedGeometryRoot->mParentAGR,
clipPtr,
uniformColorPtr);
} else if (prerenderedTransform) {
mPaintedLayerDataTree.AddingOwnLayer(animatedGeometryRoot,
clipPtr,
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -6136,16 +6136,24 @@ nsDisplayOwnLayer::nsDisplayOwnLayer(nsD
bool aForceActive)
: nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot)
, mFlags(aFlags)
, mScrollTarget(aScrollTarget)
, mThumbData(aThumbData)
, mForceActive(aForceActive)
{
MOZ_COUNT_CTOR(nsDisplayOwnLayer);
+
+ // For scroll thumb layers, override the AGR to be the thumb's AGR rather
+ // than the AGR for mFrame (which is the slider frame).
+ if (IsScrollThumbLayer()) {
+ if (nsIFrame* thumbFrame = nsBox::GetChildXULBox(mFrame)) {
+ mAnimatedGeometryRoot = aBuilder->FindAnimatedGeometryRootFor(thumbFrame);
+ }
+ }
}
#ifdef NS_BUILD_REFCNT_LOGGING
nsDisplayOwnLayer::~nsDisplayOwnLayer() {
MOZ_COUNT_DTOR(nsDisplayOwnLayer);
}
#endif
@@ -6156,16 +6164,30 @@ nsDisplayOwnLayer::GetLayerState(nsDispl
{
if (mForceActive) {
return mozilla::LAYER_ACTIVE_FORCE;
}
return RequiredLayerStateForChildren(aBuilder, aManager, aParameters, mList, mAnimatedGeometryRoot);
}
+bool
+nsDisplayOwnLayer::IsScrollThumbLayer() const
+{
+ return (mFlags & VERTICAL_SCROLLBAR) || (mFlags & HORIZONTAL_SCROLLBAR);
+}
+
+bool
+nsDisplayOwnLayer::ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder* aBuilder)
+{
+ // Render scroll thumb layers even if they are invisible, because async
+ // scrolling might bring them into view.
+ return IsScrollThumbLayer();
+}
+
// nsDisplayOpacity uses layers for rendering
already_AddRefed<Layer>
nsDisplayOwnLayer::BuildLayer(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerLayerParameters& aContainerParameters)
{
RefPtr<ContainerLayer> layer = aManager->GetLayerBuilder()->
BuildContainerLayerFor(aBuilder, aManager, mFrame, this, &mList,
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -1423,16 +1423,17 @@ private:
nsIFrame* FindAnimatedGeometryRootFrameFor(nsIFrame* aFrame);
friend class nsDisplayCanvasBackgroundImage;
friend class nsDisplayBackgroundImage;
friend class nsDisplayFixedPosition;
AnimatedGeometryRoot* FindAnimatedGeometryRootFor(nsDisplayItem* aItem);
friend class nsDisplayItem;
+ friend class nsDisplayOwnLayer;
AnimatedGeometryRoot* FindAnimatedGeometryRootFor(nsIFrame* aFrame);
AnimatedGeometryRoot* WrapAGRForFrame(nsIFrame* aAnimatedGeometryRoot,
AnimatedGeometryRoot* aParent = nullptr);
nsDataHashtable<nsPtrHashKey<nsIFrame>, AnimatedGeometryRoot*> mFrameToAnimatedGeometryRootMap;
/**
@@ -4074,32 +4075,33 @@ public:
const ActiveScrolledRoot* aActiveScrolledRoot,
uint32_t aFlags = 0,
ViewID aScrollTarget = mozilla::layers::FrameMetrics::NULL_SCROLL_ID,
const ScrollThumbData& aThumbData = ScrollThumbData{},
bool aForceActive = true);
#ifdef NS_BUILD_REFCNT_LOGGING
virtual ~nsDisplayOwnLayer();
#endif
-
+ virtual bool ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder* aBuilder) override;
virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerLayerParameters& aContainerParameters) override;
virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerLayerParameters& aParameters) override;
virtual bool TryMerge(nsDisplayItem* aItem) override
{
// Don't allow merging, each sublist must have its own layer
return false;
}
virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override {
return false;
}
uint32_t GetFlags() { return mFlags; }
+ bool IsScrollThumbLayer() const;
NS_DISPLAY_DECL_NAME("OwnLayer", TYPE_OWN_LAYER)
protected:
uint32_t mFlags;
ViewID mScrollTarget;
// If this nsDisplayOwnLayer represents a scroll thumb layer, mThumbData
// stores information about the scroll thumb. Otherwise, mThumbData will be
// default-constructed (in particular with mDirection == ScrollDirection::NONE)
// and can be ignored.
--- a/layout/xul/nsSliderFrame.cpp
+++ b/layout/xul/nsSliderFrame.cpp
@@ -416,34 +416,51 @@ nsSliderFrame::BuildDisplayListForChildr
gfxSize scale = nsLayoutUtils::GetTransformToAncestorScale(thumb);
if (scale.width != 0 && scale.height != 0) {
refSize.width /= scale.width;
refSize.height /= scale.height;
}
nsRect dirty = aDirtyRect.Intersect(thumbRect);
dirty = nsLayoutUtils::ComputePartialPrerenderArea(aDirtyRect, overflow, refSize);
+ // Clip the thumb layer to the slider track. This is necessary to ensure
+ // FrameLayerBuilder is able to merge content before and after the
+ // scrollframe into the same layer (otherwise it thinks the thumb could
+ // potentially move anywhere within the existing clip).
+ DisplayListClipState::AutoSaveRestore thumbClipState(aBuilder);
+ aBuilder->GetCurrentReferenceFrame();
+ thumbClipState.ClipContainingBlockDescendants(
+ GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(this));
+
+ // Have the thumb's container layer capture the current clip, so
+ // it doesn't apply to the thumb's contents. This allows the contents
+ // to be fully rendered even if they're partially or fully offscreen,
+ // so async scrolling can still bring it into view.
+ DisplayListClipState::AutoSaveRestore thumbContentsClipState(aBuilder);
+ thumbContentsClipState.Clear();
+
nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
nsDisplayListCollection tempLists;
nsBoxFrame::BuildDisplayListForChildren(aBuilder, dirty, tempLists);
// This is a bit of a hack. Collect up all descendant display items
// and merge them into a single Content() list.
nsDisplayList masterList;
masterList.AppendToTop(tempLists.BorderBackground());
masterList.AppendToTop(tempLists.BlockBorderBackgrounds());
masterList.AppendToTop(tempLists.Floats());
masterList.AppendToTop(tempLists.Content());
masterList.AppendToTop(tempLists.PositionedDescendants());
masterList.AppendToTop(tempLists.Outlines());
+ // Restore the saved clip so it applies to the thumb container layer.
+ thumbContentsClipState.Restore();
+
// Wrap the list to make it its own layer.
const ActiveScrolledRoot* ownLayerASR = contASRTracker.GetContainerASR();
- DisplayListClipState::AutoSaveRestore ownLayerClipState(aBuilder);
- ownLayerClipState.ClearUpToASR(ownLayerASR);
aLists.Content()->AppendNewToTop(new (aBuilder)
nsDisplayOwnLayer(aBuilder, this, &masterList, ownLayerASR,
flags, scrollTargetId,
ScrollThumbData{scrollDirection,
GetThumbRatio(),
thumbStart,
thumbLength,
isAsyncDraggable,