зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
bc6e066129
Коммит
1b27bde4b3
|
@ -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>">
|
Загрузка…
Ссылка в новой задаче