--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -3571,170 +3571,230 @@ ScrollFrameHelper::BuildDisplayList(nsDi
contentBoxClipState.emplace(aBuilder);
if (mClipAllDescendants) {
contentBoxClipState->ClipContentDescendants(*contentBoxClip);
} else {
contentBoxClipState->ClipContainingBlockDescendants(*contentBoxClip);
}
}
- nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(aBuilder);
- if (mWillBuildScrollableLayer && aBuilder->IsPaintingToWindow()) {
- asrSetter.EnterScrollFrame(sf);
- }
-
- if (mIsScrollableLayerInRootContainer) {
- aBuilder->SetActiveScrolledRootForRootScrollframe(aBuilder->CurrentActiveScrolledRoot());
- }
-
- if (mWillBuildScrollableLayer) {
- // Create a hit test info item for the scrolled content that's not
- // clipped to the displayport. This ensures that within the bounds
- // of the scroll frame, the scrolled content is always hit, even
- // if we are checkerboarding.
- if (aBuilder->BuildCompositorHitTestInfo()) {
- CompositorHitTestInfo info = mScrolledFrame->GetCompositorHitTestInfo(aBuilder);
- if (info != CompositorHitTestInfo::eInvisibleToHitTest) {
- nsDisplayCompositorHitTestInfo* hitInfo =
- MakeDisplayItem<nsDisplayCompositorHitTestInfo>(aBuilder, mScrolledFrame, info, 1);
- aBuilder->SetCompositorHitTestInfo(hitInfo);
- scrolledContent.BorderBackground()->AppendToTop(hitInfo);
+ nsDisplayListCollection set(aBuilder);
+ {
+ nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(aBuilder);
+ if (mWillBuildScrollableLayer && aBuilder->IsPaintingToWindow()) {
+ asrSetter.EnterScrollFrame(sf);
+ }
+
+ if (mIsScrollableLayerInRootContainer) {
+ aBuilder->SetActiveScrolledRootForRootScrollframe(aBuilder->CurrentActiveScrolledRoot());
+ }
+
+ if (mWillBuildScrollableLayer) {
+ // Create a hit test info item for the scrolled content that's not
+ // clipped to the displayport. This ensures that within the bounds
+ // of the scroll frame, the scrolled content is always hit, even
+ // if we are checkerboarding.
+ if (aBuilder->BuildCompositorHitTestInfo()) {
+ CompositorHitTestInfo info = mScrolledFrame->GetCompositorHitTestInfo(aBuilder);
+ if (info != CompositorHitTestInfo::eInvisibleToHitTest) {
+ nsDisplayCompositorHitTestInfo* hitInfo =
+ MakeDisplayItem<nsDisplayCompositorHitTestInfo>(aBuilder, mScrolledFrame, info, 1);
+ aBuilder->SetCompositorHitTestInfo(hitInfo);
+ set.BorderBackground()->AppendToTop(hitInfo);
+ }
+ }
+ }
+
+ {
+ // Clip our contents to the unsnapped scrolled rect. This makes sure that
+ // we don't have display items over the subpixel seam at the edge of the
+ // scrolled area.
+ DisplayListClipState::AutoSaveRestore scrolledRectClipState(aBuilder);
+ nsRect scrolledRectClip =
+ GetUnsnappedScrolledRectInternal(mScrolledFrame->GetScrollableOverflowRect(),
+ mScrollPort.Size()) + mScrolledFrame->GetPosition();
+ if (mWillBuildScrollableLayer && aBuilder->IsPaintingToWindow()) {
+ // Clip the contents to the display port.
+ // The dirty rect already acts kind of like a clip, in that
+ // FrameLayerBuilder intersects item bounds and opaque regions with
+ // it, but it doesn't have the consistent snapping behavior of a
+ // true clip.
+ // For a case where this makes a difference, imagine the following
+ // scenario: The display port has an edge that falls on a fractional
+ // layer pixel, and there's an opaque display item that covers the
+ // whole display port up until that fractional edge, and there is a
+ // transparent display item that overlaps the edge. We want to prevent
+ // this transparent item from enlarging the scrolled layer's visible
+ // region beyond its opaque region. The dirty rect doesn't do that -
+ // it gets rounded out, whereas a true clip gets rounded to nearest
+ // pixels.
+ // If there is no display port, we don't need this because the clip
+ // from the scroll port is still applied.
+ scrolledRectClip = scrolledRectClip.Intersect(visibleRect);
+ }
+ scrolledRectClipState.ClipContainingBlockDescendants(
+ scrolledRectClip + aBuilder->ToReferenceFrame(mOuter));
+
+ nsDisplayListBuilder::AutoBuildingDisplayList
+ building(aBuilder, mOuter, visibleRect, dirtyRect, aBuilder->IsAtRootOfPseudoStackingContext());
+
+ mOuter->BuildDisplayListForChild(aBuilder, mScrolledFrame, set);
+
+ if (dirtyRectHasBeenOverriden && gfxPrefs::LayoutDisplayListShowArea()) {
+ nsDisplaySolidColor* color =
+ MakeDisplayItem<nsDisplaySolidColor>(aBuilder, mOuter,
+ dirtyRect + aBuilder->GetCurrentFrameOffsetToReferenceFrame(),
+ NS_RGBA(0, 0, 255, 64), false);
+ color->SetOverrideZIndex(INT32_MAX);
+ set.PositionedDescendants()->AppendToTop(color);
+ }
+ }
+
+ if (extraContentBoxClipForNonCaretContent) {
+ // The items were built while the inflated content box clip was in
+ // effect, so that the caret wasn't clipped unnecessarily. We apply
+ // the non-inflated clip to the non-caret items now, by intersecting
+ // it with their existing clip.
+ ClipListsExceptCaret(&set, aBuilder, mScrolledFrame,
+ *extraContentBoxClipForNonCaretContent);
+ }
+
+ if (aBuilder->IsPaintingToWindow()) {
+ mIsScrollParent = idSetter.ShouldForceLayerForScrollParent();
+ }
+ if (idSetter.ShouldForceLayerForScrollParent() &&
+ !gfxPrefs::LayoutUseContainersForRootFrames())
+ {
+ // Note that forcing layerization of scroll parents follows the scroll
+ // handoff chain which is subject to the out-of-flow-frames caveat noted
+ // above (where the idSetter variable is created).
+ //
+ // This is not compatible when using containes for root scrollframes.
+ MOZ_ASSERT(couldBuildLayer && mScrolledFrame->GetContent() &&
+ aBuilder->IsPaintingToWindow());
+ if (!mWillBuildScrollableLayer) {
+ // Set a displayport so next paint we don't have to force layerization
+ // after the fact.
+ nsLayoutUtils::SetDisplayPortMargins(mOuter->GetContent(),
+ mOuter->PresShell(),
+ ScreenMargin(),
+ 0,
+ nsLayoutUtils::RepaintMode::DoNotRepaint);
+ // Call DecideScrollableLayer to recompute mWillBuildScrollableLayer and
+ // recompute the current animated geometry root if needed.
+ // It's too late to change the dirty rect so pass a copy.
+ nsRect copyOfDirtyRect = dirtyRect;
+ nsRect copyOfVisibleRect = visibleRect;
+ Unused << DecideScrollableLayer(aBuilder, ©OfVisibleRect, ©OfDirtyRect,
+ /* aSetBase = */ false, nullptr);
+ if (mWillBuildScrollableLayer) {
+ asrSetter.InsertScrollFrame(sf);
+ }
}
}
}
- {
- // Clip our contents to the unsnapped scrolled rect. This makes sure that
- // we don't have display items over the subpixel seam at the edge of the
- // scrolled area.
- DisplayListClipState::AutoSaveRestore scrolledRectClipState(aBuilder);
- nsRect scrolledRectClip =
- GetUnsnappedScrolledRectInternal(mScrolledFrame->GetScrollableOverflowRect(),
- mScrollPort.Size()) + mScrolledFrame->GetPosition();
- if (mWillBuildScrollableLayer && aBuilder->IsPaintingToWindow()) {
- // Clip the contents to the display port.
- // The dirty rect already acts kind of like a clip, in that
- // FrameLayerBuilder intersects item bounds and opaque regions with
- // it, but it doesn't have the consistent snapping behavior of a
- // true clip.
- // For a case where this makes a difference, imagine the following
- // scenario: The display port has an edge that falls on a fractional
- // layer pixel, and there's an opaque display item that covers the
- // whole display port up until that fractional edge, and there is a
- // transparent display item that overlaps the edge. We want to prevent
- // this transparent item from enlarging the scrolled layer's visible
- // region beyond its opaque region. The dirty rect doesn't do that -
- // it gets rounded out, whereas a true clip gets rounded to nearest
- // pixels.
- // If there is no display port, we don't need this because the clip
- // from the scroll port is still applied.
- scrolledRectClip = scrolledRectClip.Intersect(visibleRect);
+ // scrolled ASR is no longer on the stack, viewport clip still is.
+
+ if (mWillBuildScrollableLayer && aBuilder->IsPaintingToWindow()) {
+ aBuilder->ForceLayerForScrollParent();
+ }
+
+ if (couldBuildLayer) {
+ // Make sure that APZ will dispatch events back to content so we can create
+ // a displayport for this frame. We'll add the item later on.
+ if (!mWillBuildScrollableLayer) {
+ if (aBuilder->BuildCompositorHitTestInfo()) {
+ CompositorHitTestInfo info = CompositorHitTestInfo::eVisibleToHitTest
+ | CompositorHitTestInfo::eDispatchToContent;
+ // If the scroll frame has non-default overscroll-behavior, instruct
+ // APZ to require a target confirmation before processing events that
+ // hit this scroll frame (that is, to drop the events if a confirmation
+ // does not arrive within the timeout period). Otherwise, APZ's
+ // fallback behaviour of scrolling the enclosing scroll frame would
+ // violate the specified overscroll-behavior.
+ ScrollbarStyles scrollbarStyles = GetScrollbarStylesFromFrame();
+ if (scrollbarStyles.mOverscrollBehaviorX != StyleOverscrollBehavior::Auto ||
+ scrollbarStyles.mOverscrollBehaviorY != StyleOverscrollBehavior::Auto) {
+ info |= CompositorHitTestInfo::eRequiresTargetConfirmation;
+ }
+ nsDisplayCompositorHitTestInfo* hitInfo =
+ MakeDisplayItem<nsDisplayCompositorHitTestInfo>(aBuilder, mScrolledFrame, info, 1,
+ Some(mScrollPort + aBuilder->ToReferenceFrame(mOuter)));
+ AppendInternalItemToTop(set, hitInfo, Some(INT32_MAX));
+ }
+ if (aBuilder->IsBuildingLayerEventRegions()) {
+ nsDisplayLayerEventRegions* inactiveRegionItem =
+ MakeDisplayItem<nsDisplayLayerEventRegions>(aBuilder, mScrolledFrame, 1);
+ inactiveRegionItem->AddInactiveScrollPort(mScrolledFrame, mScrollPort + aBuilder->ToReferenceFrame(mOuter));
+ AppendInternalItemToTop(set, inactiveRegionItem, Some(INT32_MAX));
+ }
}
- scrolledRectClipState.ClipContainingBlockDescendants(
- scrolledRectClip + aBuilder->ToReferenceFrame(mOuter));
-
- nsDisplayListBuilder::AutoBuildingDisplayList
- building(aBuilder, mOuter, visibleRect, dirtyRect, aBuilder->IsAtRootOfPseudoStackingContext());
-
- mOuter->BuildDisplayListForChild(aBuilder, mScrolledFrame, scrolledContent);
-
- if (dirtyRectHasBeenOverriden && gfxPrefs::LayoutDisplayListShowArea()) {
- nsDisplaySolidColor* color =
- MakeDisplayItem<nsDisplaySolidColor>(aBuilder, mOuter,
- dirtyRect + aBuilder->GetCurrentFrameOffsetToReferenceFrame(),
- NS_RGBA(0, 0, 255, 64), false);
- color->SetOverrideZIndex(INT32_MAX);
- scrolledContent.PositionedDescendants()->AppendToTop(color);
+
+ if (aBuilder->ShouldBuildScrollInfoItemsForHoisting()) {
+ aBuilder->AppendNewScrollInfoItemForHoisting(
+ MakeDisplayItem<nsDisplayScrollInfoLayer>(aBuilder, mScrolledFrame,
+ mOuter));
}
}
- if (extraContentBoxClipForNonCaretContent) {
- // The items were built while the inflated content box clip was in
- // effect, so that the caret wasn't clipped unnecessarily. We apply
- // the non-inflated clip to the non-caret items now, by intersecting
- // it with their existing clip.
- ClipListsExceptCaret(&scrolledContent, aBuilder, mScrolledFrame,
- *extraContentBoxClipForNonCaretContent);
- }
-
- if (aBuilder->IsPaintingToWindow()) {
- mIsScrollParent = idSetter.ShouldForceLayerForScrollParent();
- }
- if (idSetter.ShouldForceLayerForScrollParent() &&
- !gfxPrefs::LayoutUseContainersForRootFrames())
- {
- // Note that forcing layerization of scroll parents follows the scroll
- // handoff chain which is subject to the out-of-flow-frames caveat noted
- // above (where the idSetter variable is created).
- //
- // This is not compatible when using containes for root scrollframes.
- MOZ_ASSERT(couldBuildLayer && mScrolledFrame->GetContent() &&
- aBuilder->IsPaintingToWindow());
- if (!mWillBuildScrollableLayer) {
- // Set a displayport so next paint we don't have to force layerization
- // after the fact.
- nsLayoutUtils::SetDisplayPortMargins(mOuter->GetContent(),
- mOuter->PresShell(),
- ScreenMargin(),
- 0,
- nsLayoutUtils::RepaintMode::DoNotRepaint);
- // Call DecideScrollableLayer to recompute mWillBuildScrollableLayer and
- // recompute the current animated geometry root if needed.
- // It's too late to change the dirty rect so pass a copy.
- nsRect copyOfDirtyRect = dirtyRect;
- nsRect copyOfVisibleRect = visibleRect;
- Unused << DecideScrollableLayer(aBuilder, ©OfVisibleRect, ©OfDirtyRect,
- /* aSetBase = */ false, nullptr);
- if (mWillBuildScrollableLayer) {
- asrSetter.InsertScrollFrame(sf);
+ if (gfxPrefs::APZAllowZooming() &&
+ !gfxPrefs::LayoutUseContainersForRootFrames() &&
+ mIsRoot && mOuter->PresContext()->IsRootContentDocument()) {
+ MOZ_ASSERT(mClipAllDescendants);
+
+ // Wrap all our scrolled contents in an nsDisplayAsyncZoom. This will be
+ // the layer that gets scaled for APZ zooming. It does not have the
+ // scrolled ASR, but it does have the viewport clip applied to it. The
+ // viewport clip is also inherited into our children; effectively we are
+ // double clipping to the viewport, at potentially different async scales.
+ // The code below follows how things are done in
+ // nsFrame::BuildDisplayListForStackingContext, in order to turn the
+ // display list set into a serial display list that has the correct z-order.
+
+ set.PositionedDescendants()->SortByZOrder();
+
+ nsDisplayList resultList;
+ // Now follow the rules of http://www.w3.org/TR/CSS21/zindex.html
+ // 1,2: backgrounds and borders
+ resultList.AppendToTop(set.BorderBackground());
+ // 3: negative z-index children.
+ for (;;) {
+ nsDisplayItem* item = set.PositionedDescendants()->GetBottom();
+ if (item && item->ZIndex() < 0) {
+ set.PositionedDescendants()->RemoveBottom();
+ resultList.AppendToTop(item);
+ continue;
}
+ break;
}
+ // 4: block backgrounds
+ resultList.AppendToTop(set.BlockBorderBackgrounds());
+ // 5: floats
+ resultList.AppendToTop(set.Floats());
+ // 7: general content
+ resultList.AppendToTop(set.Content());
+ // 7.5: outlines, in content tree order.
+ if (nsIContent* content = mOuter->GetContent()) {
+ set.Outlines()->SortByContentOrder(content);
+ }
+ resultList.AppendToTop(set.Outlines());
+ // 8, 9: non-negative z-index children
+ resultList.AppendToTop(set.PositionedDescendants());
+
+ mozilla::layers::FrameMetrics::ViewID viewID =
+ nsLayoutUtils::FindOrCreateIDFor(mScrolledFrame->GetContent());
+
+ set.Content()->AppendToTop(
+ MakeDisplayItem<nsDisplayAsyncZoom>(aBuilder, mOuter, &resultList,
+ aBuilder->CurrentActiveScrolledRoot(),
+ viewID));
}
- }
-
- if (mWillBuildScrollableLayer && aBuilder->IsPaintingToWindow()) {
- aBuilder->ForceLayerForScrollParent();
- }
-
- if (couldBuildLayer) {
- // Make sure that APZ will dispatch events back to content so we can create
- // a displayport for this frame. We'll add the item later on.
- if (!mWillBuildScrollableLayer) {
- if (aBuilder->BuildCompositorHitTestInfo()) {
- CompositorHitTestInfo info = CompositorHitTestInfo::eVisibleToHitTest
- | CompositorHitTestInfo::eDispatchToContent;
- // If the scroll frame has non-default overscroll-behavior, instruct
- // APZ to require a target confirmation before processing events that
- // hit this scroll frame (that is, to drop the events if a confirmation
- // does not arrive within the timeout period). Otherwise, APZ's
- // fallback behaviour of scrolling the enclosing scroll frame would
- // violate the specified overscroll-behavior.
- ScrollbarStyles scrollbarStyles = GetScrollbarStylesFromFrame();
- if (scrollbarStyles.mOverscrollBehaviorX != StyleOverscrollBehavior::Auto ||
- scrollbarStyles.mOverscrollBehaviorY != StyleOverscrollBehavior::Auto) {
- info |= CompositorHitTestInfo::eRequiresTargetConfirmation;
- }
- nsDisplayCompositorHitTestInfo* hitInfo =
- MakeDisplayItem<nsDisplayCompositorHitTestInfo>(aBuilder, mScrolledFrame, info, 1,
- Some(mScrollPort + aBuilder->ToReferenceFrame(mOuter)));
- AppendInternalItemToTop(scrolledContent, hitInfo, Some(INT32_MAX));
- }
- if (aBuilder->IsBuildingLayerEventRegions()) {
- nsDisplayLayerEventRegions* inactiveRegionItem =
- MakeDisplayItem<nsDisplayLayerEventRegions>(aBuilder, mScrolledFrame, 1);
- inactiveRegionItem->AddInactiveScrollPort(mScrolledFrame, mScrollPort + aBuilder->ToReferenceFrame(mOuter));
- AppendInternalItemToTop(scrolledContent, inactiveRegionItem, Some(INT32_MAX));
- }
- }
-
- if (aBuilder->ShouldBuildScrollInfoItemsForHoisting()) {
- aBuilder->AppendNewScrollInfoItemForHoisting(
- MakeDisplayItem<nsDisplayScrollInfoLayer>(aBuilder, mScrolledFrame,
- mOuter));
- }
+
+ set.MoveTo(scrolledContent);
}
// Now display overlay scrollbars and the resizer, if we have one.
AppendScrollPartsTo(aBuilder, scrolledContent, createLayersForScrollbars, true);
scrolledContent.MoveTo(aLists);
}
bool