Bug 1778718 - Make filter/backdrop-filter wrapping work when ignoring a scroll frame. r=tnikkel

Factor the code to build the top layer and wrapping for filters into a
common function.

Differential Revision: https://phabricator.services.mozilla.com/D151475
This commit is contained in:
Emilio Cobos Álvarez 2022-07-12 09:46:01 +00:00
Родитель bc6e066129
Коммит 1b27bde4b3
4 изменённых файлов: 148 добавлений и 125 удалений

Просмотреть файл

@ -3770,6 +3770,124 @@ static int32_t MaxZIndexInListOfItemsContainedInFrame(nsDisplayList* aList,
return maxZIndex;
}
void ScrollFrameHelper::MaybeCreateTopLayerAndWrapRootItems(
nsDisplayListBuilder* aBuilder, nsDisplayListCollection& aSet,
bool aCreateAsyncZoom,
AutoContainsBlendModeCapturer* aAsyncZoomBlendCapture,
const nsRect& aAsyncZoomClipRect, nscoord* aRadii) {
if (!mIsRoot) {
return;
}
// Create any required items for the 'top layer' and check if they'll be
// opaque over the entire area of the viewport. If they are, then we can
// skip building display items for the rest of the page.
if (ViewportFrame* viewport = do_QueryFrame(mOuter->GetParent())) {
bool topLayerIsOpaque = false;
if (nsDisplayWrapList* topLayerWrapList =
viewport->BuildDisplayListForTopLayer(aBuilder,
&topLayerIsOpaque)) {
// If the top layer content is opaque, and we're the root content document
// in the process, we can drop the display items behind it. We only
// support doing this for the root content document in the process, since
// the top layer content might have fixed position items that have a
// scrolltarget referencing the APZ data for the document. APZ builds this
// data implicitly for the root content document in the process, but
// subdocuments etc need their display items to generate it, so we can't
// cull those.
if (topLayerIsOpaque &&
mOuter->PresContext()->IsRootContentDocumentInProcess()) {
aSet.DeleteAll(aBuilder);
}
aSet.PositionedDescendants()->AppendToTop(topLayerWrapList);
}
}
nsDisplayList rootResultList(aBuilder);
bool serializedList = false;
auto SerializeList = [&] {
if (!serializedList) {
serializedList = true;
aSet.SerializeWithCorrectZOrder(&rootResultList, mOuter->GetContent());
}
};
if (nsIFrame* rootStyleFrame = GetFrameForStyle()) {
bool usingBackdropFilter =
rootStyleFrame->StyleEffects()->HasBackdropFilters() &&
rootStyleFrame->IsVisibleForPainting();
if (rootStyleFrame->StyleEffects()->HasFilters()) {
SerializeList();
rootResultList.AppendNewToTop<nsDisplayFilters>(
aBuilder, mOuter, &rootResultList, rootStyleFrame,
usingBackdropFilter);
}
if (usingBackdropFilter) {
SerializeList();
DisplayListClipState::AutoSaveRestore clipState(aBuilder);
nsRect backdropRect =
mOuter->GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(mOuter);
rootResultList.AppendNewToTop<nsDisplayBackdropFilters>(
aBuilder, mOuter, &rootResultList, backdropRect, rootStyleFrame);
}
}
if (aCreateAsyncZoom) {
MOZ_ASSERT(mIsRoot);
// 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 composition bounds clip applied to
// it. The children have the layout viewport clip applied to them (above).
// Effectively we are double clipping to the viewport, at potentially
// different async scales.
SerializeList();
if (aAsyncZoomBlendCapture->CaptureContainsBlendMode()) {
// The async zoom contents contain a mix-blend mode, so let's wrap all
// those contents into a blend container, and then wrap the blend
// container in the async zoom container. Otherwise the blend container
// ends up outside the zoom container which results in blend failure for
// WebRender.
nsDisplayItem* blendContainer =
nsDisplayBlendContainer::CreateForMixBlendMode(
aBuilder, mOuter, &rootResultList,
aBuilder->CurrentActiveScrolledRoot());
rootResultList.AppendToTop(blendContainer);
// Blend containers can be created or omitted during partial updates
// depending on the dirty rect. So we basically can't do partial updates
// if there's a blend container involved. There is equivalent code to this
// in the BuildDisplayListForStackingContext function as well, with a more
// detailed comment explaining things better.
if (aBuilder->IsRetainingDisplayList()) {
if (aBuilder->IsPartialUpdate()) {
aBuilder->SetPartialBuildFailed(true);
} else {
aBuilder->SetDisablePartialUpdates(true);
}
}
}
mozilla::layers::FrameMetrics::ViewID viewID =
nsLayoutUtils::FindOrCreateIDFor(mScrolledFrame->GetContent());
DisplayListClipState::AutoSaveRestore clipState(aBuilder);
clipState.ClipContentDescendants(aAsyncZoomClipRect, aRadii);
rootResultList.AppendNewToTop<nsDisplayAsyncZoom>(
aBuilder, mOuter, &rootResultList,
aBuilder->CurrentActiveScrolledRoot(), viewID);
}
if (serializedList) {
aSet.Content()->AppendToTop(&rootResultList);
}
}
void ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsDisplayListSet& aLists) {
SetAndNullOnExit<const nsIFrame> tmpBuilder(
@ -3868,10 +3986,9 @@ void ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
mOuter->BuildDisplayListForChild(aBuilder, mScrolledFrame, set);
}
if (nsDisplayWrapList* topLayerWrapList =
MaybeCreateTopLayerItems(aBuilder, nullptr)) {
set.PositionedDescendants()->AppendToTop(topLayerWrapList);
}
MaybeCreateTopLayerAndWrapRootItems(aBuilder, set,
/* aCreateAsyncZoom = */ false, nullptr,
nsRect(), nullptr);
if (addScrollBars) {
// Add overlay scrollbars.
@ -4007,7 +4124,8 @@ void ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
DisplayListClipState::AutoSaveRestore clipState(aBuilder);
// If we're building an async zoom container, clip the contents inside
// to the layout viewport (scrollPortClip). The composition bounds clip
// (clipRect) will be applied to the zoom container itself below.
// (clipRect) will be applied to the zoom container itself in
// MaybeCreateTopLayerAndWrapRootItems.
nsRect clipRectForContents =
willBuildAsyncZoomContainer ? scrollPortClip : clipRect;
if (mIsRoot) {
@ -4173,114 +4291,14 @@ void ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
}
}
// Create any required items for the 'top layer' and check if they'll be
// opaque over the entire area of the viewport. If they are, then we can
// skip building display items for the rest of the page.
bool topLayerIsOpaque = false;
if (nsDisplayWrapList* topLayerWrapList =
MaybeCreateTopLayerItems(aBuilder, &topLayerIsOpaque)) {
// If the top layer content is opaque, and we're the root content document
// in the process, we can drop the display items behind it. We only support
// doing this for the root content document in the process, since the top
// layer content might have fixed position items that have a scrolltarget
// referencing the APZ data for the document. APZ builds this data
// implicitly for the root content document in the process, but subdocuments
// etc need their display items to generate it, so we can't cull those.
if (topLayerIsOpaque &&
mOuter->PresContext()->IsRootContentDocumentInProcess()) {
set.DeleteAll(aBuilder);
}
set.PositionedDescendants()->AppendToTop(topLayerWrapList);
}
nsDisplayList rootResultList(aBuilder);
bool serializedList = false;
auto SerializeList = [&] {
if (!serializedList) {
serializedList = true;
set.SerializeWithCorrectZOrder(&rootResultList, mOuter->GetContent());
}
};
if (mIsRoot) {
if (nsIFrame* rootStyleFrame = GetFrameForStyle()) {
bool usingBackdropFilter =
rootStyleFrame->StyleEffects()->HasBackdropFilters() &&
rootStyleFrame->IsVisibleForPainting();
if (rootStyleFrame->StyleEffects()->HasFilters()) {
SerializeList();
rootResultList.AppendNewToTop<nsDisplayFilters>(
aBuilder, mOuter, &rootResultList, rootStyleFrame,
usingBackdropFilter);
}
if (usingBackdropFilter) {
SerializeList();
DisplayListClipState::AutoSaveRestore clipState(aBuilder);
nsRect backdropRect = mOuter->GetRectRelativeToSelf() +
aBuilder->ToReferenceFrame(mOuter);
rootResultList.AppendNewToTop<nsDisplayBackdropFilters>(
aBuilder, mOuter, &rootResultList, backdropRect, rootStyleFrame);
}
}
}
if (willBuildAsyncZoomContainer) {
MOZ_ASSERT(mIsRoot);
// 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 composition bounds clip applied to
// it. The children have the layout viewport clip applied to them (above).
// Effectively we are double clipping to the viewport, at potentially
// different async scales.
SerializeList();
if (blendCapture.CaptureContainsBlendMode()) {
// The async zoom contents contain a mix-blend mode, so let's wrap all
// those contents into a blend container, and then wrap the blend
// container in the async zoom container. Otherwise the blend container
// ends up outside the zoom container which results in blend failure for
// WebRender.
nsDisplayItem* blendContainer =
nsDisplayBlendContainer::CreateForMixBlendMode(
aBuilder, mOuter, &rootResultList,
aBuilder->CurrentActiveScrolledRoot());
rootResultList.AppendToTop(blendContainer);
// Blend containers can be created or omitted during partial updates
// depending on the dirty rect. So we basically can't do partial updates
// if there's a blend container involved. There is equivalent code to this
// in the BuildDisplayListForStackingContext function as well, with a more
// detailed comment explaining things better.
if (aBuilder->IsRetainingDisplayList()) {
if (aBuilder->IsPartialUpdate()) {
aBuilder->SetPartialBuildFailed(true);
} else {
aBuilder->SetDisablePartialUpdates(true);
}
}
}
mozilla::layers::FrameMetrics::ViewID viewID =
nsLayoutUtils::FindOrCreateIDFor(mScrolledFrame->GetContent());
DisplayListClipState::AutoSaveRestore clipState(aBuilder);
clipState.ClipContentDescendants(clipRect, haveRadii ? radii : nullptr);
rootResultList.AppendNewToTop<nsDisplayAsyncZoom>(
aBuilder, mOuter, &rootResultList,
aBuilder->CurrentActiveScrolledRoot(), viewID);
}
if (serializedList) {
set.Content()->AppendToTop(&rootResultList);
}
if (mWillBuildScrollableLayer && aBuilder->IsPaintingToWindow()) {
aBuilder->ForceLayerForScrollParent();
}
MaybeCreateTopLayerAndWrapRootItems(
aBuilder, set, willBuildAsyncZoomContainer, &blendCapture, clipRect,
haveRadii ? radii : nullptr);
// We want to call SetContainsNonMinimalDisplayPort if
// mWillBuildScrollableLayer is true for any reason other than having a
// minimal display port.
@ -4353,20 +4371,6 @@ void ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
set.MoveTo(aLists);
}
nsDisplayWrapList* ScrollFrameHelper::MaybeCreateTopLayerItems(
nsDisplayListBuilder* aBuilder, bool* aIsOpaque) {
if (!mIsRoot) {
return nullptr;
}
ViewportFrame* viewport = do_QueryFrame(mOuter->GetParent());
if (!viewport) {
return nullptr;
}
return viewport->BuildDisplayListForTopLayer(aBuilder, aIsOpaque);
}
nsRect ScrollFrameHelper::RestrictToRootDisplayPort(
const nsRect& aDisplayportBase) {
// This function clips aDisplayportBase so that it is no larger than the

Просмотреть файл

@ -32,6 +32,7 @@ class nsPresContext;
class nsIContent;
class nsAtom;
class nsIScrollPositionListener;
class AutoContainsBlendModeCapturer;
namespace mozilla {
class PresShell;
@ -121,8 +122,10 @@ class ScrollFrameHelper : public nsIReflowCallback {
// wrapped in the async zoom container, if we're building one.
// It should not be called with an ASR setter on the stack, as the
// top-layer items handle setting up their own ASRs.
nsDisplayWrapList* MaybeCreateTopLayerItems(nsDisplayListBuilder* aBuilder,
bool* aIsOpaque);
void MaybeCreateTopLayerAndWrapRootItems(
nsDisplayListBuilder*, nsDisplayListCollection&, bool aCreateAsyncZoom,
AutoContainsBlendModeCapturer* aAsyncZoomBlendCapture,
const nsRect& aAsyncZoomClipRect, nscoord* aRadii);
void AppendScrollPartsTo(nsDisplayListBuilder* aBuilder,
const nsDisplayListSet& aLists, bool aCreateLayer,

Просмотреть файл

@ -0,0 +1,6 @@
<!doctype html>
<meta charset="utf-8">
<title>CSS Test Reference</title>
<svg height='50px' width='50px' style='filter: hue-rotate(90deg)'>
<rect width='20px' height='20px' fill='red'></rect>
</svg>

Просмотреть файл

@ -0,0 +1,10 @@
<!doctype html>
<meta charset="utf-8">
<title>CSS Filter Effects: filters work on the root of an svg image</title>
<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1778718">
<link rel="help" href="https://drafts.fxtf.org/filter-effects-1/#FilterProperty">
<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
<link rel="author" title="Mozilla" href="https://mozilla.org">
<link rel="match" href="svg-image-root-filter-ref.html">
<img id="image"
src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' height='50px' width='50px' style='filter: hue-rotate(90deg)'><rect width='20px' height='20px' fill='red'></rect></svg>">