Bug 754480 - Remove PresShell members associated with mContentToScrollTo, put that data in a temporary content property instead. part=3/3 r=bz
authorMats Palmgren <matspal@gmail.com>
Wed, 16 May 2012 19:32:40 +0200
changeset 95590 3b6bafa382f6f47a0e885846aabd8e57ca858f07
parent 95589 f8d21660ac1decb2cbe59dfb2b00e401834da708
child 95591 e428a3d95bcdafae76193a5eaa3033bcf4907ab9
push idunknown
push userunknown
push dateunknown
reviewersbz
bugs754480
milestone15.0a1
Bug 754480 - Remove PresShell members associated with mContentToScrollTo, put that data in a temporary content property instead. part=3/3 r=bz
layout/base/nsPresShell.cpp
layout/base/nsPresShell.h
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -909,17 +909,20 @@ PresShell::Destroy()
 #endif // ACCESSIBILITY
 
   MaybeReleaseCapturingContent();
 
   if (gKeyDownTarget && gKeyDownTarget->OwnerDoc() == mDocument) {
     NS_RELEASE(gKeyDownTarget);
   }
 
-  mContentToScrollTo = nsnull;
+  if (mContentToScrollTo) {
+    mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling);
+    mContentToScrollTo = nsnull;
+  }
 
   if (mPresContext) {
     // We need to notify the destroying the nsPresContext to ESM for
     // suppressing to use from ESM.
     mPresContext->EventStateManager()->NotifyDestroyPresContext(mPresContext);
   }
 
   {
@@ -3144,62 +3147,76 @@ static void ScrollToShowRect(nsIScrollab
 
   // If we don't need to scroll, then don't try since it might cancel
   // a current smooth scroll operation.
   if (needToScroll) {
     aScrollFrame->ScrollTo(scrollPt, nsIScrollableFrame::INSTANT, &allowedRange);
   }
 }
 
+static void
+DeleteScrollIntoViewData(void* aObject, nsIAtom* aPropertyName,
+                         void* aPropertyValue, void* aData)
+{
+  PresShell::ScrollIntoViewData* data =
+    static_cast<PresShell::ScrollIntoViewData*>(aPropertyValue);
+  delete data;
+}
+
 nsresult
 PresShell::ScrollContentIntoView(nsIContent*              aContent,
                                  nsIPresShell::ScrollAxis aVertical,
                                  nsIPresShell::ScrollAxis aHorizontal,
                                  PRUint32                 aFlags)
 {
-  nsCOMPtr<nsIContent> content = aContent; // Keep content alive while flushing.
-  NS_ENSURE_TRUE(content, NS_ERROR_NULL_POINTER);
-  nsCOMPtr<nsIDocument> currentDoc = content->GetCurrentDoc();
+  NS_ENSURE_TRUE(aContent, NS_ERROR_NULL_POINTER);
+  nsCOMPtr<nsIDocument> currentDoc = aContent->GetCurrentDoc();
   NS_ENSURE_STATE(currentDoc);
 
   NS_ASSERTION(mDidInitialReflow, "should have done initial reflow by now");
 
+  if (mContentToScrollTo) {
+    mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling);
+  }
   mContentToScrollTo = aContent;
-  mContentScrollVAxis = aVertical;
-  mContentScrollHAxis = aHorizontal;
-  mContentToScrollToFlags = aFlags;
+  ScrollIntoViewData* data = new ScrollIntoViewData();
+  data->mContentScrollVAxis = aVertical;
+  data->mContentScrollHAxis = aHorizontal;
+  data->mContentToScrollToFlags = aFlags;
+  if (NS_FAILED(mContentToScrollTo->SetProperty(nsGkAtoms::scrolling, data,
+                                                ::DeleteScrollIntoViewData))) {
+    mContentToScrollTo = nsnull;
+  }
 
   // Flush layout and attempt to scroll in the process.
   currentDoc->SetNeedLayoutFlush();
   currentDoc->FlushPendingNotifications(Flush_InterruptibleLayout);
 
   // If mContentToScrollTo is non-null, that means we interrupted the reflow
   // (or suppressed it altogether because we're suppressing interruptible
   // flushes right now) and won't necessarily get the position correct, but do
   // a best-effort scroll here.  The other option would be to do this inside
   // FlushPendingNotifications, but I'm not sure the repeated scrolling that
   // could trigger if reflows keep getting interrupted would be more desirable
   // than a single best-effort scroll followed by one final scroll on the first
   // completed reflow.
   if (mContentToScrollTo) {
-    DoScrollContentIntoView(content, aVertical, aHorizontal, aFlags);
+    DoScrollContentIntoView();
   }
   return NS_OK;
 }
 
 void
-PresShell::DoScrollContentIntoView(nsIContent*              aContent,
-                                   nsIPresShell::ScrollAxis aVertical,
-                                   nsIPresShell::ScrollAxis aHorizontal,
-                                   PRUint32                 aFlags)
+PresShell::DoScrollContentIntoView()
 {
   NS_ASSERTION(mDidInitialReflow, "should have done initial reflow by now");
 
-  nsIFrame* frame = aContent->GetPrimaryFrame();
+  nsIFrame* frame = mContentToScrollTo->GetPrimaryFrame();
   if (!frame) {
+    mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling);
     mContentToScrollTo = nsnull;
     return;
   }
 
   if (frame->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
     // The reflow flush before this scroll got interrupted, and this frame's
     // coords and size are all zero, and it has no content showing anyway.
     // Don't bother scrolling to it.  We'll try again when we finish up layout.
@@ -3208,43 +3225,52 @@ PresShell::DoScrollContentIntoView(nsICo
 
   nsIFrame* container =
     nsLayoutUtils::GetClosestFrameOfType(frame, nsGkAtoms::scrollFrame);
   if (!container) {
     // nothing can be scrolled
     return;
   }
 
+  ScrollIntoViewData* data = static_cast<ScrollIntoViewData*>(
+    mContentToScrollTo->GetProperty(nsGkAtoms::scrolling));
+  if (NS_UNLIKELY(!data)) {
+    mContentToScrollTo = nsnull;
+    return;
+  }
+
   // This is a two-step process.
   // Step 1: Find the bounds of the rect we want to scroll into view.  For
   //         example, for an inline frame we may want to scroll in the whole
   //         line, or we may want to scroll multiple lines into view.
   // Step 2: Walk container frame and its ancestors and scroll them
   //         appropriately.
   // frameBounds is relative to container. We're assuming
   // that scrollframes don't split so every continuation of frame will
   // be a descendant of container. (Things would still mostly work
   // even if that assumption was false.)
   nsRect frameBounds;
   bool haveRect = false;
-  bool useWholeLineHeightForInlines = aVertical.mWhenToScroll != nsIPresShell::SCROLL_IF_NOT_FULLY_VISIBLE;
+  bool useWholeLineHeightForInlines =
+    data->mContentScrollVAxis.mWhenToScroll != nsIPresShell::SCROLL_IF_NOT_FULLY_VISIBLE;
   // Reuse the same line iterator across calls to AccumulateFrameBounds.  We set
   // it every time we detect a new block (stored in prevBlock).
   nsIFrame* prevBlock = nsnull;
   nsAutoLineIterator lines;
   // The last line we found a continuation on in |lines|.  We assume that later
   // continuations cannot come on earlier lines.
   PRInt32 curLine = 0;
   do {
     AccumulateFrameBounds(container, frame, useWholeLineHeightForInlines,
                           frameBounds, haveRect, prevBlock, lines, curLine);
   } while ((frame = frame->GetNextContinuation()));
 
-  ScrollFrameRectIntoView(container, frameBounds, aVertical, aHorizontal,
-                          aFlags);
+  ScrollFrameRectIntoView(container, frameBounds, data->mContentScrollVAxis,
+                          data->mContentScrollHAxis,
+                          data->mContentToScrollToFlags);
 }
 
 bool
 PresShell::ScrollFrameRectIntoView(nsIFrame*                aFrame,
                                    const nsRect&            aRect,
                                    nsIPresShell::ScrollAxis aVertical,
                                    nsIPresShell::ScrollAxis aHorizontal,
                                    PRUint32                 aFlags)
@@ -3831,21 +3857,21 @@ PresShell::FlushPendingNotifications(moz
     // be good.
 
     if (aType >= (mSuppressInterruptibleReflows ? Flush_Layout : Flush_InterruptibleLayout) &&
         !mIsDestroying) {
       mFrameConstructor->RecalcQuotesAndCounters();
       mViewManager->FlushDelayedResize(true);
       if (ProcessReflowCommands(aType < Flush_Layout) && mContentToScrollTo) {
         // We didn't get interrupted.  Go ahead and scroll to our content
-        DoScrollContentIntoView(mContentToScrollTo,
-                                mContentScrollVAxis,
-                                mContentScrollHAxis,
-                                mContentToScrollToFlags);
-        mContentToScrollTo = nsnull;
+        DoScrollContentIntoView();
+        if (mContentToScrollTo) {
+          mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling);
+          mContentToScrollTo = nsnull;
+        }
       }
     } else if (!mIsDestroying && mSuppressInterruptibleReflows &&
                aType == Flush_InterruptibleLayout) {
       // We suppressed this flush, but the document thinks it doesn't
       // need to flush anymore.  Let it know what's really going on.
       mDocument->SetNeedLayoutFlush();
     }
 
--- a/layout/base/nsPresShell.h
+++ b/layout/base/nsPresShell.h
@@ -357,16 +357,24 @@ public:
 
   void SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf,
                            size_t *aArenasSize,
                            size_t *aStyleSetsSize,
                            size_t *aTextRunsSize,
                            size_t *aPresContextSize) const;
   size_t SizeOfTextRuns(nsMallocSizeOfFun aMallocSizeOf) const;
 
+  // This data is stored as a content property (nsGkAtoms::scrolling) on
+  // mContentToScrollTo when we have a pending ScrollIntoView.
+  struct ScrollIntoViewData {
+    ScrollAxis mContentScrollVAxis;
+    ScrollAxis mContentScrollHAxis;
+    PRUint32   mContentToScrollToFlags;
+  };
+
 protected:
   virtual ~PresShell();
 
   void HandlePostedReflowCallbacks(bool aInterruptible);
   void CancelPostedReflowCallbacks();
 
   void UnsuppressAndInvalidate();
 
@@ -403,20 +411,17 @@ protected:
   // DoReflow returns whether the reflow finished without interruption
   bool DoReflow(nsIFrame* aFrame, bool aInterruptible);
 #ifdef DEBUG
   void DoVerifyReflow();
   void VerifyHasDirtyRootAncestor(nsIFrame* aFrame);
 #endif
 
   // Helper for ScrollContentIntoView
-  void DoScrollContentIntoView(nsIContent* aContent,
-                               ScrollAxis  aVertical,
-                               ScrollAxis  aHorizontal,
-                               PRUint32    aFlags);
+  void DoScrollContentIntoView();
 
   friend struct AutoRenderingStateSaveRestore;
   friend struct RenderingState;
 
   struct RenderingState {
     RenderingState(PresShell* aPresShell) 
       : mXResolution(aPresShell->mXResolution)
       , mYResolution(aPresShell->mYResolution)
@@ -769,22 +774,19 @@ protected:
   // the resume reflow event off a timer to avoid event starvation because
   // posted messages are processed before other messages when the modal
   // moving/sizing loop is running, see bug 491700 for details.
   nsCOMPtr<nsITimer>        mReflowContinueTimer;
 
   // Information needed to properly handle scrolling content into view if the
   // pre-scroll reflow flush can be interrupted.  mContentToScrollTo is
   // non-null between the initial scroll attempt and the first time we finish
-  // processing all our dirty roots.  mContentScrollVPosition and
-  // mContentScrollHPosition are only used when it's non-null.
+  // processing all our dirty roots.  mContentToScrollTo has a content property
+  // storing the details for the scroll operation, see ScrollIntoViewData above.
   nsCOMPtr<nsIContent>      mContentToScrollTo;
-  ScrollAxis                mContentScrollVAxis;
-  ScrollAxis                mContentScrollHAxis;
-  PRUint32                  mContentToScrollToFlags;
 
   nscoord                   mLastAnchorScrollPositionY;
 
   // This is used to protect ourselves from triggering reflow while in the
   // middle of frame construction and the like... it really shouldn't be
   // needed, one hopes, but it is for now.
   PRUint16                  mChangeNestCount;