Bug 1305957 part 6 - Select scroll anchors when flushing notifications in PresShell. r=hiro
authorRyan Hunt <rhunt@eqrion.net>
Tue, 27 Nov 2018 15:32:47 -0600
changeset 510490 462f25bfdc6514d182cd150c8142a03fb6078316
parent 510489 b6c42df7ce091d7f2417e13203a50c60cd144571
child 510491 a4257d0470d79d33b1e45e974bb4515dc036ee2e
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershiro
bugs1305957
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1305957 part 6 - Select scroll anchors when flushing notifications in PresShell. r=hiro This commit adds a mechanism for scroll anchor containers to request an anchor node selection at a future time. Currently this is before styling so that anchor adjustment suppression will have current anchor nodes. Differential Revision: https://phabricator.services.mozilla.com/D13269
layout/base/PresShell.cpp
layout/base/PresShell.h
layout/base/RestyleManager.cpp
layout/base/nsIPresShell.h
layout/generic/ScrollAnchorContainer.cpp
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -173,16 +173,17 @@
 #include "mozilla/gfx/2D.h"
 #include "nsSubDocumentFrame.h"
 #include "nsQueryObject.h"
 #include "nsLayoutStylesheetCache.h"
 #include "mozilla/layers/InputAPZContext.h"
 #include "mozilla/layers/FocusTarget.h"
 #include "mozilla/layers/WebRenderLayerManager.h"
 #include "mozilla/layers/WebRenderUserData.h"
+#include "mozilla/layout/ScrollAnchorContainer.h"
 #include "mozilla/ServoBindings.h"
 #include "mozilla/ServoStyleSet.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/dom/ImageTracker.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsBindingManager.h"
 #include "nsClassHashtable.h"
@@ -1306,16 +1307,17 @@ void PresShell::Destroy() {
   mCurrentEventFrame = nullptr;
 
   int32_t i, count = mCurrentEventFrameStack.Length();
   for (i = 0; i < count; i++) {
     mCurrentEventFrameStack[i] = nullptr;
   }
 
   mFramesToDirty.Clear();
+  mDirtyScrollAnchorContainers.Clear();
 
   if (mViewManager) {
     // Clear the view manager's weak pointer back to |this| in case it
     // was leaked.
     mViewManager->SetPresShell(nullptr);
     mViewManager = nullptr;
   }
 
@@ -2133,16 +2135,21 @@ void PresShell::NotifyDestroyingFrame(ns
         // pop it we can still get its new frame from its content
         nsIContent* currentEventContent = aFrame->GetContent();
         mCurrentEventContentStack.ReplaceObjectAt(currentEventContent, i);
         mCurrentEventFrameStack[i] = nullptr;
       }
     }
 
     mFramesToDirty.RemoveEntry(aFrame);
+
+    nsIScrollableFrame* scrollableFrame = do_QueryFrame(aFrame);
+    if (scrollableFrame) {
+      mDirtyScrollAnchorContainers.RemoveEntry(scrollableFrame);
+    }
   }
 }
 
 already_AddRefed<nsCaret> PresShell::GetCaret() const {
   RefPtr<nsCaret> caret = mCaret;
   return caret.forget();
 }
 
@@ -2554,16 +2561,29 @@ void PresShell::VerifyHasDirtyRootAncest
   }
 
   MOZ_ASSERT_UNREACHABLE(
       "Frame has dirty bits set but isn't scheduled to be "
       "reflowed?");
 }
 #endif
 
+void PresShell::PostDirtyScrollAnchorContainer(nsIScrollableFrame* aFrame) {
+  mDirtyScrollAnchorContainers.PutEntry(aFrame);
+}
+
+void PresShell::FlushDirtyScrollAnchorContainers() {
+  for (auto iter = mDirtyScrollAnchorContainers.Iter(); !iter.Done();
+       iter.Next()) {
+    nsIScrollableFrame* scroll = iter.Get()->GetKey();
+    scroll->GetAnchor()->SelectAnchor();
+  }
+  mDirtyScrollAnchorContainers.Clear();
+}
+
 void PresShell::FrameNeedsReflow(nsIFrame* aFrame,
                                  IntrinsicDirty aIntrinsicDirty,
                                  nsFrameState aBitToAdd,
                                  ReflowRootHandling aRootHandling) {
   MOZ_ASSERT(aBitToAdd == NS_FRAME_IS_DIRTY ||
                  aBitToAdd == NS_FRAME_HAS_DIRTY_CHILDREN || !aBitToAdd,
              "Unexpected bits being added");
 
@@ -8473,16 +8493,18 @@ bool PresShell::DoReflow(nsIFrame* targe
 #ifdef MOZ_GECKO_PROFILER
   DECLARE_DOCSHELL_AND_HISTORY_ID(docShell);
   AutoProfilerTracing tracingLayoutFlush("Paint", "Reflow",
                                          std::move(mReflowCause), docShellId,
                                          docShellHistoryId);
   mReflowCause = nullptr;
 #endif
 
+  FlushDirtyScrollAnchorContainers();
+
   if (mReflowContinueTimer) {
     mReflowContinueTimer->Cancel();
     mReflowContinueTimer = nullptr;
   }
 
   const bool isRoot = target == mFrameConstructor->GetRootFrame();
 
   MOZ_ASSERT(isRoot || aOverflowTracker,
@@ -10002,17 +10024,18 @@ void PresShell::AddSizeOfIncludingThis(n
   MallocSizeOf mallocSizeOf = aSizes.mState.mMallocSizeOf;
   mFrameArena.AddSizeOfExcludingThis(aSizes);
   aSizes.mLayoutPresShellSize += mallocSizeOf(this);
   if (mCaret) {
     aSizes.mLayoutPresShellSize += mCaret->SizeOfIncludingThis(mallocSizeOf);
   }
   aSizes.mLayoutPresShellSize +=
       mApproximatelyVisibleFrames.ShallowSizeOfExcludingThis(mallocSizeOf) +
-      mFramesToDirty.ShallowSizeOfExcludingThis(mallocSizeOf);
+      mFramesToDirty.ShallowSizeOfExcludingThis(mallocSizeOf) +
+      mDirtyScrollAnchorContainers.ShallowSizeOfExcludingThis(mallocSizeOf);
 
   StyleSet()->AddSizeOfIncludingThis(aSizes);
 
   aSizes.mLayoutTextRunsSize += SizeOfTextRuns(mallocSizeOf);
 
   aSizes.mLayoutPresContextSize +=
       mPresContext->SizeOfIncludingThis(mallocSizeOf);
 
--- a/layout/base/PresShell.h
+++ b/layout/base/PresShell.h
@@ -108,16 +108,19 @@ class PresShell final : public nsIPresSh
       nscoord aOldHeight = 0,
       ResizeReflowOptions aOptions = ResizeReflowOptions::eBSizeExact) override;
   nsresult ResizeReflowIgnoreOverride(
       nscoord aWidth, nscoord aHeight, nscoord aOldWidth, nscoord aOldHeight,
       ResizeReflowOptions aOptions = ResizeReflowOptions::eBSizeExact) override;
   nsIPageSequenceFrame* GetPageSequenceFrame() const override;
   nsCanvasFrame* GetCanvasFrame() const override;
 
+  void PostDirtyScrollAnchorContainer(nsIScrollableFrame* aFrame) override;
+  void FlushDirtyScrollAnchorContainers() override;
+
   void FrameNeedsReflow(
       nsIFrame* aFrame, IntrinsicDirty aIntrinsicDirty, nsFrameState aBitToAdd,
       ReflowRootHandling aRootHandling = eInferFromBitToAdd) override;
   void FrameNeedsToContinueReflow(nsIFrame* aFrame) override;
   void CancelAllPendingReflows() override;
   void DoFlushPendingNotifications(FlushType aType) override;
   void DoFlushPendingNotifications(ChangesToFlush aType) override;
 
@@ -739,16 +742,17 @@ class PresShell final : public nsIPresSh
   layers::ScrollableLayerGuid mMouseEventTargetGuid;
 
   // mStyleSet owns it but we maintain a ref, may be null
   RefPtr<StyleSheet> mPrefStyleSheet;
 
   // Set of frames that we should mark with NS_FRAME_HAS_DIRTY_CHILDREN after
   // we finish reflowing mCurrentReflowRoot.
   nsTHashtable<nsPtrHashKey<nsIFrame>> mFramesToDirty;
+  nsTHashtable<nsPtrHashKey<nsIScrollableFrame>> mDirtyScrollAnchorContainers;
 
   nsTArray<UniquePtr<DelayedEvent>> mDelayedEvents;
 
  private:
   nsRevocableEventPtr<nsSynthMouseMoveEvent> mSynthMouseMoveEvent;
   nsCOMPtr<nsIContent> mLastAnchorScrolledTo;
   RefPtr<nsCaret> mCaret;
   RefPtr<nsCaret> mOriginalCaret;
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -2910,16 +2910,21 @@ void RestyleManager::DoProcessPendingRes
     // PresShell::FlushPendingNotifications doesn't early-return in the case
     // where the PresShell hasn't yet been initialized (and therefore we haven't
     // yet done the initial style traversal of the DOM tree). We should arguably
     // fix up the callers and assert against this case, but we just detect and
     // handle it for now.
     return;
   }
 
+  // Select scroll anchors for frames that have been scrolled. Do this
+  // before restyling so that anchor nodes are correctly marked for
+  // scroll anchor update suppressions.
+  presContext->PresShell()->FlushDirtyScrollAnchorContainers();
+
   // Create a AnimationsWithDestroyedFrame during restyling process to
   // stop animations and transitions on elements that have no frame at the end
   // of the restyling process.
   AnimationsWithDestroyedFrame animationsWithDestroyedFrame(this);
 
   ServoStyleSet* styleSet = StyleSet();
   Document* doc = presContext->Document();
 
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -449,16 +449,19 @@ class nsIPresShell : public nsStubDocume
   virtual nsIPageSequenceFrame* GetPageSequenceFrame() const = 0;
 
   /**
    * Returns the canvas frame associated with the frame hierarchy.
    * Returns nullptr if is XUL document.
    */
   virtual nsCanvasFrame* GetCanvasFrame() const = 0;
 
+  virtual void PostDirtyScrollAnchorContainer(nsIScrollableFrame* aFrame) = 0;
+  virtual void FlushDirtyScrollAnchorContainers() = 0;
+
   /**
    * Tell the pres shell that a frame needs to be marked dirty and needs
    * Reflow.  It's OK if this is an ancestor of the frame needing reflow as
    * long as the ancestor chain between them doesn't cross a reflow root.
    *
    * The bit to add should be NS_FRAME_IS_DIRTY, NS_FRAME_HAS_DIRTY_CHILDREN
    * or nsFrameState(0); passing 0 means that dirty bits won't be set on the
    * frame or its ancestors/descendants, but that intrinsic widths will still
--- a/layout/generic/ScrollAnchorContainer.cpp
+++ b/layout/generic/ScrollAnchorContainer.cpp
@@ -191,16 +191,17 @@ void ScrollAnchorContainer::InvalidateAn
   ANCHOR_LOG("Invalidating scroll anchor %p for %p.\n", mAnchorNode, this);
 
   if (mAnchorNode) {
     SetAnchorFlags(mScrollFrame->mScrolledFrame, mAnchorNode, false);
   }
   mAnchorNode = nullptr;
   mAnchorNodeIsDirty = true;
   mLastAnchorPos = nsPoint();
+  Frame()->PresShell()->PostDirtyScrollAnchorContainer(ScrollableFrame());
 }
 
 void ScrollAnchorContainer::Destroy() {
   if (mAnchorNode) {
     SetAnchorFlags(mScrollFrame->mScrolledFrame, mAnchorNode, false);
   }
   mAnchorNode = nullptr;
   mAnchorNodeIsDirty = false;