Hoist scroll info items above inactive blur containers. (bug 1193557 part 1, r=mstange)

This commit is contained in:
David Anderson 2015-09-04 17:17:50 -07:00
parent 4130945bf6
commit 61b0e5d971
4 changed files with 184 additions and 39 deletions

View File

@ -613,12 +613,12 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
mCurrentAnimatedGeometryRoot(nullptr),
mDirtyRect(-1,-1,-1,-1),
mGlassDisplayItem(nullptr),
mScrollInfoItemsForHoisting(nullptr),
mPendingScrollInfoItems(nullptr),
mCommittedScrollInfoItems(nullptr),
mMode(aMode),
mCurrentScrollParentId(FrameMetrics::NULL_SCROLL_ID),
mCurrentScrollbarTarget(FrameMetrics::NULL_SCROLL_ID),
mCurrentScrollbarFlags(0),
mSVGEffectsBuildingDepth(0),
mBuildCaret(aBuildCaret),
mIgnoreSuppression(false),
mHadToIgnoreSuppression(false),
@ -1251,35 +1251,27 @@ nsDisplayListBuilder::IsInWillChangeBudget(nsIFrame* aFrame,
return onBudget;
}
void
nsDisplayListBuilder::EnterSVGEffectsContents(nsDisplayList* aHoistedItemsStorage)
nsDisplayList*
nsDisplayListBuilder::EnterScrollInfoItemHoisting(nsDisplayList* aScrollInfoItemStorage)
{
MOZ_ASSERT(mSVGEffectsBuildingDepth >= 0);
MOZ_ASSERT(aHoistedItemsStorage);
if (mSVGEffectsBuildingDepth == 0) {
MOZ_ASSERT(!mScrollInfoItemsForHoisting);
mScrollInfoItemsForHoisting = aHoistedItemsStorage;
}
mSVGEffectsBuildingDepth++;
MOZ_ASSERT(ShouldBuildScrollInfoItemsForHoisting());
nsDisplayList* old = mPendingScrollInfoItems;
mPendingScrollInfoItems = aScrollInfoItemStorage;
return old;
}
void
nsDisplayListBuilder::ExitSVGEffectsContents()
nsDisplayListBuilder::LeaveScrollInfoItemHoisting(nsDisplayList* aScrollInfoItemStorage)
{
mSVGEffectsBuildingDepth--;
MOZ_ASSERT(mSVGEffectsBuildingDepth >= 0);
MOZ_ASSERT(mScrollInfoItemsForHoisting);
if (mSVGEffectsBuildingDepth == 0) {
mScrollInfoItemsForHoisting = nullptr;
}
MOZ_ASSERT(ShouldBuildScrollInfoItemsForHoisting());
mPendingScrollInfoItems = aScrollInfoItemStorage;
}
void
nsDisplayListBuilder::AppendNewScrollInfoItemForHoisting(nsDisplayScrollInfoLayer* aScrollInfoItem)
{
MOZ_ASSERT(ShouldBuildScrollInfoItemsForHoisting());
MOZ_ASSERT(mScrollInfoItemsForHoisting);
mScrollInfoItemsForHoisting->AppendNewToTop(aScrollInfoItem);
mPendingScrollInfoItems->AppendNewToTop(aScrollInfoItem);
}
void
@ -4448,6 +4440,7 @@ nsDisplayScrollInfoLayer::nsDisplayScrollInfoLayer(
, mScrollFrame(aScrollFrame)
, mScrolledFrame(aScrolledFrame)
, mScrollParentId(aBuilder->GetCurrentScrollParentId())
, mIgnoreIfCompositorSupportsBlending(false)
{
#ifdef NS_BUILD_REFCNT_LOGGING
MOZ_COUNT_CTOR(nsDisplayScrollInfoLayer);
@ -4471,6 +4464,16 @@ nsDisplayScrollInfoLayer::BuildLayer(nsDisplayListBuilder* aBuilder,
// cannot be layerized, and so needs to scroll synchronously. To handle those
// cases, we still want to generate scrollinfo layers.
if (mIgnoreIfCompositorSupportsBlending) {
// This item was created pessimistically because, during display list
// building, we encountered a mix blend mode. If our layer manager
// supports compositing this mix blend mode, we don't actually need to
// create a scroll info layer.
if (aManager->SupportsMixBlendModes(mContainedBlendModes)) {
return nullptr;
}
}
ContainerLayerParameters params = aContainerParameters;
if (mScrolledFrame->GetContent() &&
nsLayoutUtils::GetCriticalDisplayPort(mScrolledFrame->GetContent(), nullptr)) {
@ -4512,7 +4515,24 @@ nsDisplayScrollInfoLayer::ComputeFrameMetrics(Layer* aLayer,
mScrollParentId, viewport, Nothing(), false, params)));
}
void
nsDisplayScrollInfoLayer::IgnoreIfCompositorSupportsBlending(BlendModeSet aBlendModes)
{
mContainedBlendModes += aBlendModes;
mIgnoreIfCompositorSupportsBlending = true;
}
void
nsDisplayScrollInfoLayer::UnsetIgnoreIfCompositorSupportsBlending()
{
mIgnoreIfCompositorSupportsBlending = false;
}
bool
nsDisplayScrollInfoLayer::ContainedInMixBlendMode() const
{
return mIgnoreIfCompositorSupportsBlending;
}
void
nsDisplayScrollInfoLayer::WriteDebugInfo(std::stringstream& aStream)

View File

@ -841,11 +841,26 @@ public:
const nsIFrame* aStopAtAncestor,
nsIFrame** aOutResult);
void EnterSVGEffectsContents(nsDisplayList* aHoistedItemsStorage);
void ExitSVGEffectsContents();
void SetCommittedScrollInfoItemList(nsDisplayList* aScrollInfoItemStorage) {
mCommittedScrollInfoItems = aScrollInfoItemStorage;
}
nsDisplayList* CommittedScrollInfoItems() const {
return mCommittedScrollInfoItems;
}
bool ShouldBuildScrollInfoItemsForHoisting() const {
return IsPaintingToWindow();
}
bool ShouldBuildScrollInfoItemsForHoisting() const
{ return mSVGEffectsBuildingDepth > 0; }
// When building display lists for stacking contexts, we append scroll info
// items to a temporary list. If the stacking context would create an
// inactive layer, we commit these items to the final hoisted scroll items
// list. Otherwise, we propagate these items to the parent stacking
// context's list of pending scroll info items.
//
// EnterScrollInfoItemHoisting returns the parent stacking context's pending
// item list.
nsDisplayList* EnterScrollInfoItemHoisting(nsDisplayList* aScrollInfoItemStorage);
void LeaveScrollInfoItemHoisting(nsDisplayList* aScrollInfoItemStorage);
void AppendNewScrollInfoItemForHoisting(nsDisplayScrollInfoLayer* aScrollInfoItem);
@ -954,19 +969,20 @@ private:
nsIntRegion mWindowDraggingRegion;
// The display item for the Windows window glass background, if any
nsDisplayItem* mGlassDisplayItem;
// A temporary list that we append scroll info items to while building
// display items for the contents of frames with SVG effects.
// Only non-null when ShouldBuildScrollInfoItemsForHoisting() is true.
// This is a pointer and not a real nsDisplayList value because the
// nsDisplayList class is defined below this class, so we can't use it here.
nsDisplayList* mScrollInfoItemsForHoisting;
// When encountering inactive layers, we need to hoist scroll info items
// above these layers so APZ can deliver events to content. Such scroll
// info items are considered "committed" to the final hoisting list. If
// no hoisting is needed immediately, it may be needed later if a blend
// mode is introduced in a higher stacking context, so we keep all scroll
// info items until the end of display list building.
nsDisplayList* mPendingScrollInfoItems;
nsDisplayList* mCommittedScrollInfoItems;
nsTArray<DisplayItemClip*> mDisplayItemClipsToDestroy;
Mode mMode;
ViewID mCurrentScrollParentId;
ViewID mCurrentScrollbarTarget;
uint32_t mCurrentScrollbarFlags;
BlendModeSet mContainedBlendModes;
int32_t mSVGEffectsBuildingDepth;
bool mBuildCaret;
bool mIgnoreSuppression;
bool mHadToIgnoreSuppression;
@ -3265,10 +3281,20 @@ public:
mozilla::UniquePtr<FrameMetrics> ComputeFrameMetrics(Layer* aLayer,
const ContainerLayerParameters& aContainerParameters);
void IgnoreIfCompositorSupportsBlending(BlendModeSet aBlendModes);
void UnsetIgnoreIfCompositorSupportsBlending();
bool ContainedInMixBlendMode() const;
protected:
nsIFrame* mScrollFrame;
nsIFrame* mScrolledFrame;
ViewID mScrollParentId;
// If the only reason for the ScrollInfoLayer is a blend mode, the blend
// mode may be supported in the compositor. We track it here to determine
// if so during layer construction.
BlendModeSet mContainedBlendModes;
bool mIgnoreIfCompositorSupportsBlending;
};
/**

View File

@ -3103,6 +3103,11 @@ nsLayoutUtils::PaintFrame(nsRenderingContext* aRenderingContext, nsIFrame* aFram
builder, rootScrollFrame, displayportBase, &displayport);
}
nsDisplayList hoistedScrollItemStorage;
if (aFlags & (PAINT_WIDGET_LAYERS | PAINT_TO_WINDOW)) {
builder.SetCommittedScrollInfoItemList(&hoistedScrollItemStorage);
}
nsRegion visibleRegion;
if (aFlags & PAINT_WIDGET_LAYERS) {
// This layer tree will be reused, so we'll need to calculate it

View File

@ -1911,6 +1911,87 @@ public:
}
};
class AutoHoistScrollInfoItems
{
nsDisplayListBuilder& mBuilder;
nsDisplayList* mParentPendingList;
nsDisplayList mPendingList;
public:
explicit AutoHoistScrollInfoItems(nsDisplayListBuilder& aBuilder)
: mBuilder(aBuilder),
mParentPendingList(nullptr)
{
if (!mBuilder.ShouldBuildScrollInfoItemsForHoisting()) {
return;
}
mParentPendingList = mBuilder.EnterScrollInfoItemHoisting(&mPendingList);
}
~AutoHoistScrollInfoItems() {
if (!mParentPendingList) {
// If we have no parent stacking context, we will throw out any scroll
// info items that are pending (meaning, we can safely ignore them since
// the scrollable layers they represent will not be flattened).
return;
}
mParentPendingList->AppendToTop(&mPendingList);
mBuilder.LeaveScrollInfoItemHoisting(mParentPendingList);
}
// The current stacking context will definitely be flattened, so commit all
// pending scroll info items and make sure they will not be optimized away
// in the case they were also inside a compositor-supported mix-blend-mode.
void Commit() {
nsDisplayItem* iter = nullptr;
while ((iter = mPendingList.RemoveBottom()) != nullptr) {
MOZ_ASSERT(iter->GetType() == nsDisplayItem::TYPE_SCROLL_INFO_LAYER);
auto item = static_cast<nsDisplayScrollInfoLayer*>(iter);
item->UnsetIgnoreIfCompositorSupportsBlending();
mBuilder.CommittedScrollInfoItems()->AppendToTop(item);
}
}
// The current stacking context will only be flattened if the given mix-blend
// mode is not supported in the compositor. Annotate the scroll info items
// and keep them in the pending list.
void AnnotateForBlendModes(BlendModeSet aBlendModes) {
for (nsDisplayItem* iter = mPendingList.GetBottom(); iter; iter = iter->GetAbove()) {
MOZ_ASSERT(iter->GetType() == nsDisplayItem::TYPE_SCROLL_INFO_LAYER);
auto item = static_cast<nsDisplayScrollInfoLayer*>(iter);
item->IgnoreIfCompositorSupportsBlending(aBlendModes);
}
}
bool IsRootStackingContext() {
// We're only finished building the hoisted list if we have no parent
// stacking context.
return !mParentPendingList;
}
// Any scroll info items which contain a mix-blend mode are moved into the
// parent display list.
void Finish(nsDisplayList* aResultList) {
MOZ_ASSERT(IsRootStackingContext());
nsDisplayItem* iter = nullptr;
while ((iter = mPendingList.RemoveBottom()) != nullptr) {
MOZ_ASSERT(iter->GetType() == nsDisplayItem::TYPE_SCROLL_INFO_LAYER);
nsDisplayScrollInfoLayer *item = static_cast<decltype(item)>(iter);
if (!item->ContainedInMixBlendMode()) {
// Discard the item, it was not committed for having an SVG effect nor
// was it contained with a mix-blend mode.
item->~nsDisplayScrollInfoLayer();
continue;
}
aResultList->AppendToTop(item);
}
}
};
static void
CheckForApzAwareEventHandlers(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
{
@ -1990,13 +2071,13 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
inTransform = true;
}
AutoHoistScrollInfoItems hoistedScrollInfoItems(*aBuilder);
bool usingSVGEffects = nsSVGIntegrationUtils::UsingEffectsForFrame(this);
nsRect dirtyRectOutsideSVGEffects = dirtyRect;
nsDisplayList hoistedScrollInfoItemsStorage;
if (usingSVGEffects) {
dirtyRect =
nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(this, dirtyRect);
aBuilder->EnterSVGEffectsContents(&hoistedScrollInfoItemsStorage);
}
bool useOpacity = HasVisualOpacity() && !nsSVGUtils::CanOptimizeOpacity(this);
@ -2130,10 +2211,6 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
/* List now emptied, so add the new list to the top. */
resultList.AppendNewToTop(
new (aBuilder) nsDisplaySVGEffects(aBuilder, this, &resultList));
// Also add the hoisted scroll info items. We need those for APZ scrolling
// because nsDisplaySVGEffects items can't build active layers.
aBuilder->ExitSVGEffectsContents();
resultList.AppendToTop(&hoistedScrollInfoItemsStorage);
}
/* Else, if the list is non-empty and there is CSS group opacity without SVG
* effects, wrap it up in an opacity item.
@ -2202,8 +2279,25 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
*/
if (aBuilder->ContainsBlendMode()) {
resultList.AppendNewToTop(
new (aBuilder) nsDisplayBlendContainer(aBuilder, this, &resultList, aBuilder->ContainedBlendModes()));
resultList.AppendNewToTop(
new (aBuilder) nsDisplayBlendContainer(aBuilder, this, &resultList, aBuilder->ContainedBlendModes()));
}
if (aBuilder->ShouldBuildScrollInfoItemsForHoisting()) {
if (usingSVGEffects) {
// We know this stacking context will be flattened, so hoist any scroll
// info items we created.
hoistedScrollInfoItems.Commit();
} else if (aBuilder->ContainsBlendMode()) {
hoistedScrollInfoItems.AnnotateForBlendModes(aBuilder->ContainedBlendModes());
}
if (hoistedScrollInfoItems.IsRootStackingContext()) {
// If we're the root stacking context, no more mix-blend modes can be
// introduced and it's safe to hoist scroll info items.
resultList.AppendToTop(aBuilder->CommittedScrollInfoItems());
hoistedScrollInfoItems.Finish(&resultList);
}
}
/* If there's blending, wrap up the list in a blend-mode item. Note