Bug 1159042 - p4. Make mDirtyRoots manage roots in preferred depth order - r=dbaron
When popping a dirty root, take the shallowest one first (so we reflow from outer frames first, to avoid potential duplicate reflow of inner frames). Prevent duplicate roots (to be reworked in a future bug). Differential Revision: https://phabricator.services.mozilla.com/D9490
This commit is contained in:
@@ -591,27 +591,40 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
nsIPresShell::DirtyRootsList::AppendElement(nsIFrame* aFrame)
|
nsIPresShell::DirtyRootsList::Add(nsIFrame* aFrame)
|
||||||
{
|
{
|
||||||
mList.AppendElement(aFrame);
|
// Is this root already scheduled for reflow?
|
||||||
|
// FIXME: This could possibly be changed to a uniqueness assertion, with some
|
||||||
|
// work in ResizeReflowIgnoreOverride (and maybe others?)
|
||||||
|
if (mList.Contains(aFrame)) {
|
||||||
|
// We don't expect frame to change depths.
|
||||||
|
MOZ_ASSERT(aFrame->GetDepthInFrameTree() ==
|
||||||
|
mList[mList.IndexOf(aFrame)].mDepth);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mList.InsertElementSorted(
|
||||||
|
FrameAndDepth{ aFrame, aFrame->GetDepthInFrameTree() },
|
||||||
|
FrameAndDepth::CompareByReverseDepth{});
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsIPresShell::DirtyRootsList::RemoveElement(nsIFrame* aFrame)
|
nsIPresShell::DirtyRootsList::Remove(nsIFrame* aFrame)
|
||||||
{
|
{
|
||||||
mList.RemoveElement(aFrame);
|
mList.RemoveElement(aFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
nsIFrame*
|
||||||
nsIPresShell::DirtyRootsList::RemoveElements(nsIFrame* aFrame)
|
nsIPresShell::DirtyRootsList::PopShallowestRoot()
|
||||||
{
|
{
|
||||||
mList.RemoveElementsBy([&](nsIFrame* aRoot){ return aRoot == aFrame; });
|
// List is sorted in order of decreasing depth, so there are no deeper
|
||||||
}
|
// frames than the last one.
|
||||||
|
const FrameAndDepth& lastFAD = mList.LastElement();
|
||||||
void
|
nsIFrame* frame = lastFAD.mFrame;
|
||||||
nsIPresShell::DirtyRootsList::RemoveElementAt(size_t aIndex)
|
// We don't expect frame to change depths.
|
||||||
{
|
MOZ_ASSERT(frame->GetDepthInFrameTree() == lastFAD.mDepth);
|
||||||
return mList.RemoveElementAt(aIndex);
|
mList.RemoveLastElement();
|
||||||
|
return frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -632,10 +645,24 @@ nsIPresShell::DirtyRootsList::IsEmpty() const
|
|||||||
return mList.IsEmpty();
|
return mList.IsEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
bool
|
||||||
nsIPresShell::DirtyRootsList::Length() const
|
nsIPresShell::DirtyRootsList::FrameIsAncestorOfDirtyRoot(nsIFrame* aFrame) const
|
||||||
{
|
{
|
||||||
return mList.Length();
|
MOZ_ASSERT(aFrame);
|
||||||
|
|
||||||
|
// Look for a path from any dirty roots to aFrame, following GetParent().
|
||||||
|
// This check mirrors what FrameNeedsReflow() would have done if the reflow
|
||||||
|
// root didn't get in the way.
|
||||||
|
for (nsIFrame* dirtyFrame : mList) {
|
||||||
|
do {
|
||||||
|
if (dirtyFrame == aFrame) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
dirtyFrame = dirtyFrame->GetParent();
|
||||||
|
} while (dirtyFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PresShell::sDisableNonTestMouseEvents = false;
|
bool PresShell::sDisableNonTestMouseEvents = false;
|
||||||
@@ -2069,7 +2096,7 @@ PresShell::ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight,
|
|||||||
AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
|
AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
|
||||||
nsViewManager::AutoDisableRefresh refreshBlocker(viewManager);
|
nsViewManager::AutoDisableRefresh refreshBlocker(viewManager);
|
||||||
|
|
||||||
mDirtyRoots.RemoveElement(rootFrame);
|
mDirtyRoots.Remove(rootFrame);
|
||||||
DoReflow(rootFrame, true, nullptr);
|
DoReflow(rootFrame, true, nullptr);
|
||||||
|
|
||||||
if (shrinkToFit) {
|
if (shrinkToFit) {
|
||||||
@@ -2205,7 +2232,7 @@ PresShell::NotifyDestroyingFrame(nsIFrame* aFrame)
|
|||||||
|
|
||||||
mFrameConstructor->NotifyDestroyingFrame(aFrame);
|
mFrameConstructor->NotifyDestroyingFrame(aFrame);
|
||||||
|
|
||||||
mDirtyRoots.RemoveElements(aFrame);
|
mDirtyRoots.Remove(aFrame);
|
||||||
|
|
||||||
// Remove frame properties
|
// Remove frame properties
|
||||||
aFrame->DeleteAllProperties();
|
aFrame->DeleteAllProperties();
|
||||||
@@ -2863,7 +2890,7 @@ PresShell::FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty,
|
|||||||
if (FRAME_IS_REFLOW_ROOT(f) || !f->GetParent()) {
|
if (FRAME_IS_REFLOW_ROOT(f) || !f->GetParent()) {
|
||||||
// we've hit a reflow root or the root frame
|
// we've hit a reflow root or the root frame
|
||||||
if (!wasDirty) {
|
if (!wasDirty) {
|
||||||
mDirtyRoots.AppendElement(f);
|
mDirtyRoots.Add(f);
|
||||||
SetNeedLayoutFlush();
|
SetNeedLayoutFlush();
|
||||||
}
|
}
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
@@ -4671,22 +4698,7 @@ PresShell::NotifyCounterStylesAreDirty()
|
|||||||
bool
|
bool
|
||||||
nsIPresShell::FrameIsAncestorOfDirtyRoot(nsIFrame* aFrame) const
|
nsIPresShell::FrameIsAncestorOfDirtyRoot(nsIFrame* aFrame) const
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(aFrame);
|
return mDirtyRoots.FrameIsAncestorOfDirtyRoot(aFrame);
|
||||||
|
|
||||||
// Look for a path from any dirty roots to aFrame, following GetParent().
|
|
||||||
// This check mirrors what FrameNeedsReflow() would have done if the reflow
|
|
||||||
// root didn't get in the way.
|
|
||||||
for (nsIFrame* dirtyFrame : mDirtyRoots) {
|
|
||||||
while (dirtyFrame) {
|
|
||||||
if (dirtyFrame == aFrame) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
dirtyFrame = dirtyFrame->GetParent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -9140,7 +9152,7 @@ PresShell::DoReflow(nsIFrame* target, bool aInterruptible,
|
|||||||
}
|
}
|
||||||
|
|
||||||
NS_ASSERTION(NS_SUBTREE_DIRTY(target), "Why is the target not dirty?");
|
NS_ASSERTION(NS_SUBTREE_DIRTY(target), "Why is the target not dirty?");
|
||||||
mDirtyRoots.AppendElement(target);
|
mDirtyRoots.Add(target);
|
||||||
SetNeedLayoutFlush();
|
SetNeedLayoutFlush();
|
||||||
|
|
||||||
// Clear mFramesToDirty after we've done the NS_SUBTREE_DIRTY(target)
|
// Clear mFramesToDirty after we've done the NS_SUBTREE_DIRTY(target)
|
||||||
@@ -9237,9 +9249,7 @@ PresShell::ProcessReflowCommands(bool aInterruptible)
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
// Send an incremental reflow notification to the target frame.
|
// Send an incremental reflow notification to the target frame.
|
||||||
int32_t idx = mDirtyRoots.Length() - 1;
|
nsIFrame *target = mDirtyRoots.PopShallowestRoot();
|
||||||
nsIFrame *target = mDirtyRoots[idx];
|
|
||||||
mDirtyRoots.RemoveElementAt(idx);
|
|
||||||
|
|
||||||
if (!NS_SUBTREE_DIRTY(target)) {
|
if (!NS_SUBTREE_DIRTY(target)) {
|
||||||
// It's not dirty anymore, which probably means the notification
|
// It's not dirty anymore, which probably means the notification
|
||||||
|
|||||||
@@ -1787,22 +1787,48 @@ protected:
|
|||||||
class DirtyRootsList
|
class DirtyRootsList
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void AppendElement(nsIFrame* aFrame);
|
// Add a dirty root.
|
||||||
void RemoveElement(nsIFrame* aFrame);
|
void Add(nsIFrame* aFrame);
|
||||||
void RemoveElements(nsIFrame* aFrame);
|
// Remove this frame if present.
|
||||||
void RemoveElementAt(size_t aIndex);
|
void Remove(nsIFrame* aFrame);
|
||||||
|
// Remove and return one of the shallowest dirty roots from the list.
|
||||||
|
// (If two roots are at the same depth, order is indeterminate.)
|
||||||
|
nsIFrame* PopShallowestRoot();
|
||||||
|
// Remove all dirty roots.
|
||||||
void Clear();
|
void Clear();
|
||||||
|
// Is this frame one of the dirty roots?
|
||||||
bool Contains(nsIFrame* aFrame) const;
|
bool Contains(nsIFrame* aFrame) const;
|
||||||
|
// Are there no dirty roots?
|
||||||
bool IsEmpty() const;
|
bool IsEmpty() const;
|
||||||
size_t Length() const;
|
// Is the given frame an ancestor of any dirty root?
|
||||||
auto begin() const { return mList.begin(); }
|
bool FrameIsAncestorOfDirtyRoot(nsIFrame* aFrame) const;
|
||||||
auto begin() { return mList.begin(); }
|
|
||||||
auto end() const { return mList.end(); }
|
|
||||||
auto end() { return mList.end(); }
|
|
||||||
auto& operator[](size_t i) { return mList[i]; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
nsTArray<nsIFrame*> mList;
|
struct FrameAndDepth
|
||||||
|
{
|
||||||
|
nsIFrame* mFrame;
|
||||||
|
const uint32_t mDepth;
|
||||||
|
|
||||||
|
// Easy conversion to nsIFrame*, as it's the most likely need.
|
||||||
|
operator nsIFrame*() const { return mFrame; }
|
||||||
|
|
||||||
|
// Used to sort by reverse depths, i.e., deeper < shallower.
|
||||||
|
class CompareByReverseDepth
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool Equals(const FrameAndDepth& aA, const FrameAndDepth& aB) const
|
||||||
|
{
|
||||||
|
return aA.mDepth == aB.mDepth;
|
||||||
|
}
|
||||||
|
bool LessThan(const FrameAndDepth& aA, const FrameAndDepth& aB) const
|
||||||
|
{
|
||||||
|
// Reverse depth! So '>' instead of '<'.
|
||||||
|
return aA.mDepth > aB.mDepth;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
// List of all known dirty roots, sorted by decreasing depths.
|
||||||
|
nsTArray<FrameAndDepth> mList;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Reflow roots that need to be reflowed.
|
// Reflow roots that need to be reflowed.
|
||||||
|
|||||||
Reference in New Issue
Block a user