Bug 1263349 (Part 2) - Cache the current scroll parent and information about scrollports and displayports on nsDisplayListBuilder. r=botond,mstange

This commit is contained in:
Seth Fowler 2016-05-24 01:12:48 -07:00
Родитель 41d7cfb3d3
Коммит 1cb3c53e11
6 изменённых файлов: 319 добавлений и 68 удалений

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

@ -670,6 +670,72 @@ nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(Layer* aLayer,
aLayer, data, pending);
}
nsDisplayListBuilder::AutoCurrentScrollParentIdSetter::
AutoCurrentScrollParentIdSetter(nsDisplayListBuilder* aBuilder,
nsIFrame* aScrollParent)
: mBuilder(aBuilder)
, mOldScrollParent(aBuilder->mCurrentScrollParent)
, mOldDisplayPortConsideringAncestors(aBuilder->mDisplayPortConsideringAncestors)
, mOldScrollPortConsideringAncestors(aBuilder->mScrollPortConsideringAncestors)
, mOldScrollParentId(aBuilder->mCurrentScrollParentId)
, mOldForceLayer(aBuilder->mForceLayerForScrollParent)
, mChangedSubtrees(false)
{
aBuilder->UpdateCurrentScrollParent(aScrollParent);
Init();
}
nsDisplayListBuilder::AutoCurrentScrollParentIdSetter::
AutoCurrentScrollParentIdSetter(nsDisplayListBuilder* aBuilder,
const OutOfFlowDisplayData* aOutOfFlowData)
: mBuilder(aBuilder)
, mOldScrollParent(aBuilder->mCurrentScrollParent)
, mOldDisplayPortConsideringAncestors(aBuilder->mDisplayPortConsideringAncestors)
, mOldScrollPortConsideringAncestors(aBuilder->mScrollPortConsideringAncestors)
, mOldScrollParentId(aBuilder->mCurrentScrollParentId)
, mOldForceLayer(aBuilder->mForceLayerForScrollParent)
, mChangedSubtrees(true)
{
aBuilder->UpdateCurrentScrollParentForOutOfFlow(aOutOfFlowData);
Init();
}
void
nsDisplayListBuilder::AutoCurrentScrollParentIdSetter::Init()
{
// If this AutoCurrentScrollParentIdSetter has the same scrollId as the
// previous one on the stack, then that means the scrollframe that
// created this isn't actually scrollable and cannot participate in
// scroll handoff. We set mCanBeScrollParent to false to indicate this.
mCanBeScrollParent = mOldScrollParentId != mBuilder->mCurrentScrollParentId;
mBuilder->mForceLayerForScrollParent = false;
}
nsDisplayListBuilder::AutoCurrentScrollParentIdSetter::
~AutoCurrentScrollParentIdSetter()
{
mBuilder->mCurrentScrollParent = mOldScrollParent;
mBuilder->mDisplayPortConsideringAncestors = mOldDisplayPortConsideringAncestors;
mBuilder->mScrollPortConsideringAncestors = mOldScrollPortConsideringAncestors;
mBuilder->mCurrentScrollParentId = mOldScrollParentId;
if (mCanBeScrollParent || mChangedSubtrees) {
// If |mCanBeScrollParent| is set, caller code is responsible for
// having dealt with the current value of
// mBuilder->mForceLayerForScrollParent, so we can just restore the
// old value. If |mChangedSubtrees| is set, we don't propagate the
// flag because it doesn't make sense to propagate it through
// unrelated subtrees.
mBuilder->mForceLayerForScrollParent = mOldForceLayer;
} else {
// Otherwise we need to keep propagating the force-layerization flag
// upwards to the next ancestor scrollframe that does participate in
// scroll handoff.
mBuilder->mForceLayerForScrollParent |= mOldForceLayer;
}
}
nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
nsDisplayListBuilderMode aMode, bool aBuildCaret)
: mReferenceFrame(aReferenceFrame),
@ -685,6 +751,7 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
mGlassDisplayItem(nullptr),
mScrollInfoItemsForHoisting(nullptr),
mMode(aMode),
mCurrentScrollParent(nullptr),
mCurrentScrollParentId(FrameMetrics::NULL_SCROLL_ID),
mCurrentScrollbarTarget(FrameMetrics::NULL_SCROLL_ID),
mCurrentScrollbarFlags(0),
@ -732,6 +799,9 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
mFrameToAnimatedGeometryRootMap.Put(aReferenceFrame, &mRootAGR);
UpdateCurrentScrollParent(
nsLayoutUtils::GetAsyncScrollableProperAncestorFrameOrFallback(aReferenceFrame));
nsCSSRendering::BeginFrameTreesLocked();
PR_STATIC_ASSERT(nsDisplayItem::TYPE_MAX < (1 << nsDisplayItem::TYPE_BITS));
}
@ -854,7 +924,12 @@ void nsDisplayListBuilder::MarkOutOfFlowFrameForDisplay(nsIFrame* aDirtyFrame,
const DisplayItemClip* oldClip = mClipState.GetClipForContainingBlockDescendants();
const DisplayItemScrollClip* sc = mClipState.GetCurrentInnermostScrollClip();
OutOfFlowDisplayData* data = new OutOfFlowDisplayData(oldClip, sc, dirty);
OutOfFlowDisplayData* data =
new OutOfFlowDisplayData(oldClip, sc, dirty,
mCurrentScrollParent, mCurrentScrollParentId,
mDisplayPortConsideringAncestors,
mScrollPortConsideringAncestors);
aFrame->Properties().Set(nsDisplayListBuilder::OutOfFlowDisplayDataProperty(), data);
MarkFrameForDisplay(aFrame, aDirtyFrame);
@ -1221,6 +1296,132 @@ nsDisplayListBuilder::FindAnimatedGeometryRootFrameFor(nsIFrame* aFrame)
return cursor;
}
void
nsDisplayListBuilder::UpdateCurrentScrollParent(nsIFrame* aScrollParent)
{
// If the new scroll parent is the same as the old scroll parent, we don't
// have to do anything. This can happen because we sometimes instantiate an
// AutoCurrentScrollParentIdSetter unconditionally but only pass it a new
// scroll parent if certain conditions are true.
if (aScrollParent == mCurrentScrollParent) {
return;
}
// Update the current scroll parent.
nsIFrame* oldScrollParent = mCurrentScrollParent;
mCurrentScrollParent = aScrollParent;
// If there's no scroll parent now (which may be true because we're
// initializing this nsDisplayListBuilder for the first time, or because
// we're in a strange state where there's no pres shell or no root frame)
// then we just reset everything to its initial values.
if (!aScrollParent) {
mDisplayPortConsideringAncestors = nsRect();
mScrollPortConsideringAncestors = nsRect();
mCurrentScrollParentId = FrameMetrics::NULL_SCROLL_ID;
return;
}
// Compute the new scroll parent ID.
nsIContent* content = aScrollParent->GetContent();
mCurrentScrollParentId = content ? nsLayoutUtils::FindOrCreateIDFor(content)
: FrameMetrics::NULL_SCROLL_ID;
nsIScrollableFrame* newScrollParent = do_QueryFrame(aScrollParent);
if (!newScrollParent) {
// There's no scrollable parent frame, so we've been forced to fall back
// to the root frame. (Most likely this is a XUL document.) Just use its
// size for both the displayport and the scrollport.
MOZ_ASSERT(!oldScrollParent, "Falling back to the root frame when an ancestor "
"scrollable frame existed?");
nsRect frameRect(nsPoint(0, 0), aScrollParent->GetSize());
mDisplayPortConsideringAncestors = frameRect;
mScrollPortConsideringAncestors = frameRect;
return;
}
MOZ_ASSERT(mCurrentScrollParentId != FrameMetrics::NULL_SCROLL_ID,
"Couldn't get a scroll ID for a scrollable frame?");
// If we didn't have a scroll parent before, but we do now, the displayport
// and scrollport we're tracking are just those of the new scroll parent.
if (!oldScrollParent) {
mDisplayPortConsideringAncestors =
nsLayoutUtils::GetDisplayPortOrFallbackToScrollPort(newScrollParent);
mScrollPortConsideringAncestors = newScrollParent->GetScrollPortRect();
return;
}
#ifdef DEBUG
{
nsIScrollableFrame* oldScrollableParent = do_QueryFrame(oldScrollParent);
if (oldScrollableParent && oldScrollableParent->WantAsyncScroll()) {
// Normally, the old scroll parent should be the nearest async
// scrollable ancestor of the new scroll parent.
MOZ_ASSERT(nsLayoutUtils::GetAsyncScrollableProperAncestorFrame(aScrollParent) ==
oldScrollableParent);
} else {
// If the old scroll parent isn't async scrollable or isn't scrollable
// at all, (e.g., because we had to fall back to the root frame in a XUL
// document) then at least assert it's an ancestor of the new scroll parent.
MOZ_ASSERT(nsLayoutUtils::IsProperAncestorFrameCrossDoc(oldScrollParent,
aScrollParent));
}
}
#endif
// If the effective displayport, considering ancestors, is already empty, we
// can bail early as we know the intersection checks below will fail.
if (mDisplayPortConsideringAncestors.IsEmpty()) {
mDisplayPortConsideringAncestors = nsRect();
mScrollPortConsideringAncestors = nsRect();
return;
}
// The entire displayport for this subtree is the critical displayport of
// this scroll parent if any part of it is visible within the previous scroll
// parent's critical displayport. (What we're trying to do is capture
// everything that APZ could asynchronously scroll into view.) If it isn't
// visible, then the displayport for this subtree is empty and the
// scrollport, trivially, is too.
nsRect displayPort =
nsLayoutUtils::GetDisplayPortOrFallbackToScrollPort(newScrollParent);
nsRect displayPortIntersection =
nsLayoutUtils::TransformAndIntersectRect(oldScrollParent,
mDisplayPortConsideringAncestors,
aScrollParent,
displayPort);
if (displayPortIntersection.IsEmpty()) {
mDisplayPortConsideringAncestors = nsRect();
mScrollPortConsideringAncestors = nsRect();
return;
}
// See above: we take the whole thing, not the intersection with the
// previous scroll parent's critical displayport.
mDisplayPortConsideringAncestors = displayPort;
// The scrollport for this subtree is the intersection of aScrollParent's
// scrollport with all ancestor scrollports.
mScrollPortConsideringAncestors =
nsLayoutUtils::TransformAndIntersectRect(oldScrollParent,
mScrollPortConsideringAncestors,
aScrollParent,
newScrollParent->GetScrollPortRect());
}
void
nsDisplayListBuilder::UpdateCurrentScrollParentForOutOfFlow(const OutOfFlowDisplayData* aOutOfFlowData)
{
MOZ_ASSERT(aOutOfFlowData);
// Just set the information from |aOutOfFlowData| unconditionally.
mCurrentScrollParent = aOutOfFlowData->mCurrentScrollParent;
mCurrentScrollParentId = aOutOfFlowData->mCurrentScrollParentId;
mDisplayPortConsideringAncestors = aOutOfFlowData->mDisplayPortConsideringAncestors;
mScrollPortConsideringAncestors = aOutOfFlowData->mScrollPortConsideringAncestors;
}
void
nsDisplayListBuilder::RecomputeCurrentAnimatedGeometryRoot()
{

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

@ -368,6 +368,30 @@ public:
* Get the scrollframe to ignore, if any.
*/
nsIFrame* GetIgnoreScrollFrame() { return mIgnoreScrollFrame; }
/**
* Get the nearest ancestor scrollframe, or the root frame if there is no
* ancestor scrollframe, or null if neither exists.
*/
nsIFrame* GetCurrentScrollParent() { return mCurrentScrollParent; }
/**
* Get the critical displayport of the nearest ancestor scrollframe, in the
* coordinate system of the frame returned by GetCurrentScrollParent(). If
* the ancestor scrollframe's critical displayport does not intersect the
* critical displayport of the scrollframe that encloses it, and so on
* transitively up the frame tree, returns the empty rect. The idea is that
* this method considers a displayport visible if its nearest ancestor is
* visible and some part of it intersects its nearest ancestor, which means
* that we capture everything that APZ could asynchronously scroll into
* view. Used for visibility tracking during painting.
*/
nsRect GetDisplayPortConsideringAncestors() { return mDisplayPortConsideringAncestors; }
/**
* Get the intersection of all ancestor scrollframes' scrollports. Used for
* visibility tracking during painting. Unlike the displayport case, for
* scrollports we use strict intersection, because we only want to detect
* frames that are actually visible in the viewport.
*/
nsRect GetScrollPortConsideringAncestors() { return mScrollPortConsideringAncestors; }
/**
* Get the ViewID of the nearest scrolling ancestor frame.
*/
@ -826,49 +850,64 @@ public:
uint32_t mCachedItemIndex;
};
struct OutOfFlowDisplayData;
/**
* A helper class to temporarily set the value of mCurrentScrollParentId.
* A helper class to temporarily set the value of mCurrentScrollParentId and
* cache information about the effective displayport and scrollport,
* considering all ancestor scrollframes. See
* GetDisplayPortConsideringAncestors() and
* GetScrollPortConsideringAncestors() for the details.
*/
class AutoCurrentScrollParentIdSetter;
friend class AutoCurrentScrollParentIdSetter;
class AutoCurrentScrollParentIdSetter {
public:
AutoCurrentScrollParentIdSetter(nsDisplayListBuilder* aBuilder, ViewID aScrollId)
: mBuilder(aBuilder)
, mOldValue(aBuilder->mCurrentScrollParentId)
, mOldForceLayer(aBuilder->mForceLayerForScrollParent) {
// If this AutoCurrentScrollParentIdSetter has the same scrollId as the
// previous one on the stack, then that means the scrollframe that
// created this isn't actually scrollable and cannot participate in
// scroll handoff. We set mCanBeScrollParent to false to indicate this.
mCanBeScrollParent = (mOldValue != aScrollId);
aBuilder->mCurrentScrollParentId = aScrollId;
aBuilder->mForceLayerForScrollParent = false;
}
/**
* Temporarily update the cached scroll parent information on @aBuilder
* from frame @aFrame. @aFrame should be a a scrollframe that is a
* descendant of @aBuilder's current scroll parent. (The exception is if
* there's no current scroll parent, in which case @aFrame does not have to
* be a scrollframe; this is fallback behavior for XUL documents, which
* may not have a root scrollframe.)
*
* If @aNewScrollParentFrom is SCROLLFRAMES_ON_PATH_TO_ROOT, @aFrame is
* interpreted as a scrollframe in a different subtree. In this case we
* have to walk up the frame tree all the way to the root to gather
* information about the effective displayport and scrollport.
*/
AutoCurrentScrollParentIdSetter(nsDisplayListBuilder* aBuilder,
nsIFrame* aScrollParent);
/**
* Temporarily updated the cached scroll parent information on @aBuilder
* from saved information on @aOutOfFlowData. This is necessary when
* traversing out-of-flow frames via their placeholder frames, since their
* scroll parent may not be the same as the scroll parent of the
* placeholder frame.
*/
AutoCurrentScrollParentIdSetter(nsDisplayListBuilder* aBuilder,
const OutOfFlowDisplayData* aOutOfFlowData);
~AutoCurrentScrollParentIdSetter();
bool ShouldForceLayerForScrollParent() const {
// Only scrollframes participating in scroll handoff can be forced to
// layerize
return mCanBeScrollParent && mBuilder->mForceLayerForScrollParent;
};
~AutoCurrentScrollParentIdSetter() {
mBuilder->mCurrentScrollParentId = mOldValue;
if (mCanBeScrollParent) {
// If this flag is set, caller code is responsible for having dealt
// with the current value of mBuilder->mForceLayerForScrollParent, so
// we can just restore the old value.
mBuilder->mForceLayerForScrollParent = mOldForceLayer;
} else {
// Otherwise we need to keep propagating the force-layerization flag
// upwards to the next ancestor scrollframe that does participate in
// scroll handoff.
mBuilder->mForceLayerForScrollParent |= mOldForceLayer;
}
}
private:
void Init();
nsDisplayListBuilder* mBuilder;
ViewID mOldValue;
bool mOldForceLayer;
bool mCanBeScrollParent;
nsIFrame* mOldScrollParent;
nsRect mOldDisplayPortConsideringAncestors;
nsRect mOldScrollPortConsideringAncestors;
ViewID mOldScrollParentId;
bool mOldForceLayer : 1;
bool mCanBeScrollParent : 1;
bool mChangedSubtrees : 1;
};
/**
@ -989,14 +1028,26 @@ public:
struct OutOfFlowDisplayData {
OutOfFlowDisplayData(const DisplayItemClip* aContainingBlockClip,
const DisplayItemScrollClip* aContainingBlockScrollClip,
const nsRect &aDirtyRect)
const nsRect& aDirtyRect,
nsIFrame* aCurrentScrollParent,
ViewID aCurrentScrollParentId,
const nsRect& aDisplayPortConsideringAncestors,
const nsRect& aScrollPortConsideringAncestors)
: mContainingBlockClip(aContainingBlockClip ? *aContainingBlockClip : DisplayItemClip())
, mContainingBlockScrollClip(aContainingBlockScrollClip)
, mDirtyRect(aDirtyRect)
, mCurrentScrollParent(aCurrentScrollParent)
, mCurrentScrollParentId(aCurrentScrollParentId)
, mDisplayPortConsideringAncestors(aDisplayPortConsideringAncestors)
, mScrollPortConsideringAncestors(aScrollPortConsideringAncestors)
{}
DisplayItemClip mContainingBlockClip;
const DisplayItemScrollClip* mContainingBlockScrollClip;
nsRect mDirtyRect;
nsIFrame* mCurrentScrollParent;
ViewID mCurrentScrollParentId;
nsRect mDisplayPortConsideringAncestors;
nsRect mScrollPortConsideringAncestors;
};
NS_DECLARE_FRAME_PROPERTY_DELETABLE(OutOfFlowDisplayDataProperty,
@ -1153,6 +1204,23 @@ private:
*/
nsIFrame* FindAnimatedGeometryRootFrameFor(nsIFrame* aFrame);
/**
* Updates cached information about the nearest ancestor scrollable frame.
* @aScrollParent should be a descendant of the previous nearest ancestor
* scrollable frame. @aScrollParent may be null, indicating that there is
* no ancestor scrollable frame.
*/
void UpdateCurrentScrollParent(nsIFrame* aScrollParent);
/**
* Updates cached information about the nearest ancestor scrollable frame.
* The new information is read from @aOutOfFlowData, and unconditionally
* replaces the cached information without reference to the current scroll
* parent, since we're descending into an out-of-flow frame that does not
* necessarily have the same scroll parent chain.
*/
void UpdateCurrentScrollParentForOutOfFlow(const OutOfFlowDisplayData* aOutOfFlowData);
friend class nsDisplayCanvasBackgroundImage;
friend class nsDisplayBackgroundImage;
friend class nsDisplayFixedPosition;
@ -1250,7 +1318,10 @@ private:
nsTArray<DisplayItemScrollClip*> mScrollClipsToDestroy;
nsTArray<DisplayItemClip*> mDisplayItemClipsToDestroy;
nsDisplayListBuilderMode mMode;
nsIFrame* mCurrentScrollParent;
ViewID mCurrentScrollParentId;
nsRect mDisplayPortConsideringAncestors;
nsRect mScrollPortConsideringAncestors;
ViewID mCurrentScrollbarTarget;
uint32_t mCurrentScrollbarFlags;
Preserves3DContext mPreserves3DCtx;

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

@ -3534,34 +3534,6 @@ nsLayoutUtils::PaintFrame(nsRenderingContext* aRenderingContext, nsIFrame* aFram
builder.EnterPresShell(aFrame);
nsRect dirtyRect = visibleRegion.GetBounds();
{
// If a scrollable container layer is created in nsDisplayList::PaintForFrame,
// it will be the scroll parent for display items that are built in the
// BuildDisplayListForStackingContext call below. We need to set the scroll
// parent on the display list builder while we build those items, so that they
// can pick up their scroll parent's id.
ViewID id = FrameMetrics::NULL_SCROLL_ID;
if (ignoreViewportScrolling && presContext->IsRootContentDocument()) {
if (nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame()) {
if (nsIContent* content = rootScrollFrame->GetContent()) {
id = nsLayoutUtils::FindOrCreateIDFor(content);
}
}
}
#if !defined(MOZ_WIDGET_ANDROID) || defined(MOZ_ANDROID_APZ)
else if (presShell->GetDocument() && presShell->GetDocument()->IsRootDisplayDocument()
&& !presShell->GetRootScrollFrame()) {
// In cases where the root document is a XUL document, we want to take
// the ViewID from the root element, as that will be the ViewID of the
// root APZC in the tree. Skip doing this in cases where we know
// nsGfxScrollFrame::BuilDisplayList will do it instead.
if (dom::Element* element = presShell->GetDocument()->GetDocumentElement()) {
id = nsLayoutUtils::FindOrCreateIDFor(element);
}
}
#endif
nsDisplayListBuilder::AutoCurrentScrollParentIdSetter idSetter(&builder, id);
PROFILER_LABEL("nsLayoutUtils", "PaintFrame::BuildDisplayList",
js::ProfileEntry::Category::GRAPHICS);

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

@ -2782,11 +2782,17 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
DisplayListClipState::AutoClipMultiple clipState(aBuilder);
CheckForApzAwareEventHandlers(aBuilder, child);
Maybe<nsDisplayListBuilder::AutoCurrentScrollParentIdSetter> idSetter;
if (savedOutOfFlowData) {
clipState.SetClipForContainingBlockDescendants(
&savedOutOfFlowData->mContainingBlockClip);
clipState.SetScrollClipForContainingBlockDescendants(
savedOutOfFlowData->mContainingBlockScrollClip);
// For out-of-flow frames, the current scroll parent is wrong, because it's
// the scroll parent for the placeholder frame rather than the actual
// out-of-flow frame, so we need to update the current scroll parent.
idSetter.emplace(aBuilder, savedOutOfFlowData);
} else if (GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO &&
isPlaceholder) {
// If we have nested out-of-flow frames and the outer one isn't visible

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

@ -3299,11 +3299,11 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
// parent. This is intentional because that is what happens for positioned children
// of scroll layers, and we want to maintain consistent behaviour between scroll layers
// and scroll info layers.
nsIContent* content = couldBuildLayer ? mScrolledFrame->GetContent()
: nullptr;
nsDisplayListBuilder::AutoCurrentScrollParentIdSetter idSetter(
aBuilder,
couldBuildLayer && mScrolledFrame->GetContent()
? nsLayoutUtils::FindOrCreateIDFor(mScrolledFrame->GetContent())
: aBuilder->GetCurrentScrollParentId());
aBuilder, content ? mOuter : aBuilder->GetCurrentScrollParent());
nsRect clipRect = mScrollPort + aBuilder->ToReferenceFrame(mOuter);
// Our override of GetBorderRadii ensures we never have a radius at

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

@ -483,11 +483,12 @@ nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
if (subdocRootFrame) {
nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
nsIContent* content = ignoreViewportScrolling && rootScrollFrame
? rootScrollFrame->GetContent()
: nullptr;
nsDisplayListBuilder::AutoCurrentScrollParentIdSetter idSetter(
aBuilder,
ignoreViewportScrolling && rootScrollFrame && rootScrollFrame->GetContent()
? nsLayoutUtils::FindOrCreateIDFor(rootScrollFrame->GetContent())
: aBuilder->GetCurrentScrollParentId());
aBuilder, content ? rootScrollFrame : aBuilder->GetCurrentScrollParent());
aBuilder->SetAncestorHasApzAwareEventHandler(false);
subdocRootFrame->