Bug 526394. Part 31: Move scroll implementation into nsGfxScrollFrame. r=mats
authorRobert O'Callahan <robert@ocallahan.org>
Thu, 08 Oct 2009 16:01:15 +1300
changeset 37083 4ccff5df452cf08d29a430ff40d15fa6f3920c7f
parent 37082 fada8c5cef0730f37e7540eb7d55222dcd95a0c4
child 37084 17131f693e28e31d01747fb282c06ecf39d9c916
push idunknown
push userunknown
push dateunknown
reviewersmats
bugs526394
milestone1.9.3a1pre
Bug 526394. Part 31: Move scroll implementation into nsGfxScrollFrame. r=mats
layout/base/nsPresContext.cpp
layout/base/nsPresContext.h
layout/base/nsPresShell.cpp
layout/forms/nsListControlFrame.cpp
layout/forms/nsListControlFrame.h
layout/generic/nsGfxScrollFrame.cpp
layout/generic/nsGfxScrollFrame.h
layout/generic/nsIScrollableFrame.h
view/public/nsIScrollPositionListener.h
view/public/nsIView.h
view/public/nsIViewManager.h
view/public/nsIViewObserver.h
view/src/Makefile.in
view/src/nsView.h
view/src/nsViewManager.cpp
view/src/nsViewManager.h
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -2096,16 +2096,54 @@ nsPresContext::NotifyInvalidation(const 
     mInvalidateRequests.mRequests.AppendElement();
   if (!request)
     return;
 
   request->mRect = aRect;
   request->mFlags = aFlags;
 }
 
+void
+nsPresContext::NotifyInvalidateRegion(const nsRegion& aRegion,
+                                      nsPoint aOffset, PRUint32 aFlags)
+{
+  const nsRect* r;
+  for (nsRegionRectIterator iter(aRegion); (r = iter.Next());) {
+    NotifyInvalidation(*r + aOffset, aFlags);
+  }
+}
+
+void
+nsPresContext::NotifyInvalidateForScrolling(const nsRegion& aBlitRegion,
+                                            const nsRegion& aInvalidateRegion)
+{
+  nsPresContext* pc = this;
+  PRUint32 crossDocFlags = 0;
+  nsIFrame* rootFrame = FrameManager()->GetRootFrame();
+  nsPoint offset(0,0);
+  while (pc) {
+    if (pc->MayHavePaintEventListener()) {
+      pc->NotifyInvalidateRegion(aBlitRegion, offset,
+                                 nsIFrame::INVALIDATE_REASON_SCROLL_BLIT | crossDocFlags);
+      pc->NotifyInvalidateRegion(aInvalidateRegion, offset,
+                                 nsIFrame::INVALIDATE_REASON_SCROLL_REPAINT | crossDocFlags);
+    }
+    crossDocFlags = nsIFrame::INVALIDATE_CROSS_DOC;
+
+    nsIFrame* rootParentFrame = nsLayoutUtils::GetCrossDocParentFrame(rootFrame);
+    if (!rootParentFrame)
+      break;
+
+    pc = rootParentFrame->PresContext();
+    nsIFrame* nextRootFrame = pc->PresShell()->FrameManager()->GetRootFrame();
+    offset += rootFrame->GetOffsetTo(nextRootFrame);
+    rootFrame = nextRootFrame;
+  }
+}
+
 PRBool
 nsPresContext::HasCachedStyleData()
 {
   return mShell && mShell->StyleSet()->HasCachedStyleData();
 }
 
 static PRBool sGotInterruptEnv = PR_FALSE;
 enum InterruptMode {
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -831,18 +831,19 @@ public:
   void UserFontSetUpdated();
 
   // Ensure that it is safe to hand out CSS rules outside the layout
   // engine by ensuring that all CSS style sheets have unique inners
   // and, if necessary, synchronously rebuilding all style data.
   // Returns true on success and false on failure (not safe).
   PRBool EnsureSafeToHandOutCSSRules();
 
-  PRBool MayHavePaintEventListener();
   void NotifyInvalidation(const nsRect& aRect, PRUint32 aFlags);
+  void NotifyInvalidateForScrolling(const nsRegion& aBlitRegion,
+                                    const nsRegion& aInvalidateRegion);
   void FireDOMPaintEvent();
   PRBool IsDOMPaintEventPending() {
     return !mInvalidateRequests.mRequests.IsEmpty();
   }
 
   void ClearMozAfterPaintEvents() {
     mInvalidateRequests.mRequests.Clear();
   }
@@ -950,16 +951,20 @@ protected:
   NS_HIDDEN_(void) UpdateAfterPreferencesChanged();
   static NS_HIDDEN_(void) PrefChangedUpdateTimerCallback(nsITimer *aTimer, void *aClosure);
 
   NS_HIDDEN_(void) GetUserPreferences();
   NS_HIDDEN_(void) GetFontPreferences();
 
   NS_HIDDEN_(void) UpdateCharSet(const nsAFlatCString& aCharSet);
 
+  PRBool MayHavePaintEventListener();
+  void NotifyInvalidateRegion(const nsRegion& aRegion, nsPoint aOffset,
+                              PRUint32 aFlags);
+
   void HandleRebuildUserFontSet() {
     mPostedFlushUserFontSet = PR_FALSE;
     FlushUserFontSet();
   }
 
   PRBool HavePendingInputEvent();
 
   // Can't be inline because we can't include nsStyleSet.h.
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -797,19 +797,16 @@ public:
                                       nsEvent* aEvent,
                                       nsEventStatus* aStatus);
   NS_IMETHOD HandleDOMEventWithTarget(nsIContent* aTargetContent,
                                       nsIDOMEvent* aEvent,
                                       nsEventStatus* aStatus);
   NS_IMETHOD ResizeReflow(nsIView *aView, nscoord aWidth, nscoord aHeight);
   NS_IMETHOD_(PRBool) IsVisible();
   NS_IMETHOD_(void) WillPaint();
-  NS_IMETHOD_(void) InvalidateFrameForScrolledView(nsIView *view);
-  NS_IMETHOD_(void) NotifyInvalidateForScrolledView(const nsRegion& aBlitRegion,
-                                                    const nsRegion& aInvalidateRegion);
   NS_IMETHOD_(void) DispatchSynthMouseMove(nsGUIEvent *aEvent,
                                            PRBool aFlushOnHoverChange);
   NS_IMETHOD_(void) ClearMouseCapture(nsIView* aView);
 
   // caret handling
   NS_IMETHOD GetCaret(nsCaret **aOutCaret);
   NS_IMETHOD_(void) MaybeInvalidateCaretPosition();
   NS_IMETHOD SetCaretEnabled(PRBool aInEnable);
@@ -4357,64 +4354,16 @@ PresShell::GetSelectionForCopy(nsISelect
     rv = NS_OK;
   }
 
   *outSelection = sel;
   NS_IF_ADDREF(*outSelection);
   return rv;
 }
 
-void
-PresShell::InvalidateFrameForScrolledView(nsIView *aView)
-{
-  nsIFrame* frame = nsLayoutUtils::GetFrameFor(aView);
-  if (!frame)
-    return;
-  frame->InvalidateWithFlags(frame->GetOverflowRect(),
-                             nsIFrame::INVALIDATE_REASON_SCROLL_REPAINT);
-}
-
-static void
-NotifyInvalidateRegion(nsPresContext* aPresContext, const nsRegion& aRegion,
-                       nsPoint aOffset, PRUint32 aFlags)
-{
-  const nsRect* r;
-  for (nsRegionRectIterator iter(aRegion); (r = iter.Next());) {
-    aPresContext->NotifyInvalidation(*r + aOffset, aFlags);
-  }
-}
-
-void
-PresShell::NotifyInvalidateForScrolledView(const nsRegion& aBlitRegion,
-                                           const nsRegion& aInvalidateRegion)
-{
-  nsPresContext* pc = GetPresContext();
-  PRUint32 crossDocFlags = 0;
-  nsIFrame* rootFrame = FrameManager()->GetRootFrame();
-  nsPoint offset(0,0);
-  while (pc) {
-    if (pc->MayHavePaintEventListener()) {
-      NotifyInvalidateRegion(pc, aBlitRegion, offset,
-                             nsIFrame::INVALIDATE_REASON_SCROLL_BLIT | crossDocFlags);
-      NotifyInvalidateRegion(pc, aInvalidateRegion, offset,
-                             nsIFrame::INVALIDATE_REASON_SCROLL_REPAINT | crossDocFlags);
-    }
-    crossDocFlags = nsIFrame::INVALIDATE_CROSS_DOC;
-
-    nsIFrame* rootParentFrame = nsLayoutUtils::GetCrossDocParentFrame(rootFrame);
-    if (!rootParentFrame)
-      break;
-
-    pc = rootParentFrame->PresContext();
-    nsIFrame* nextRootFrame = pc->PresShell()->FrameManager()->GetRootFrame();
-    offset += rootFrame->GetOffsetTo(nextRootFrame);
-    rootFrame = nextRootFrame;
-  }
-}
-
 NS_IMETHODIMP_(void)
 PresShell::DispatchSynthMouseMove(nsGUIEvent *aEvent,
                                   PRBool aFlushOnHoverChange)
 {
   PRUint32 hoverGenerationBefore = mFrameConstructor->GetHoverGeneration();
   nsEventStatus status;
   nsIView* targetView;
   targetView = nsIView::GetViewFor(aEvent->widget);
--- a/layout/forms/nsListControlFrame.cpp
+++ b/layout/forms/nsListControlFrame.cpp
@@ -349,17 +349,17 @@ void nsListControlFrame::PaintFocus(nsIR
   nsRect fRect;
   if (childframe) {
     // get the child rect
     fRect = childframe->GetRect();
     // get it into our coordinates
     fRect.MoveBy(childframe->GetParent()->GetOffsetTo(this));
   } else {
     fRect.x = fRect.y = 0;
-    fRect.width = GetScrollPortSize().width;
+    fRect.width = GetScrollPortRect().width;
     fRect.height = CalcFallbackRowHeight();
     fRect.MoveBy(containerFrame->GetOffsetTo(this));
   }
   fRect += aPt;
   
   PRBool lastItemIsSelected = PR_FALSE;
   if (focusedContent) {
     nsCOMPtr<nsIDOMHTMLOptionElement> domOpt =
@@ -386,17 +386,17 @@ nsListControlFrame::InvalidateFocus()
     return;
 
   nsIFrame* containerFrame = GetOptionsContainer();
   if (containerFrame) {
     // Invalidating from the containerFrame because that's where our focus
     // is drawn.
     // The origin of the scrollport is the origin of containerFrame.
     nsRect invalidateArea = containerFrame->GetOverflowRect();
-    nsRect emptyFallbackArea(0, 0, GetScrollPortSize().width, CalcFallbackRowHeight());
+    nsRect emptyFallbackArea(0, 0, GetScrollPortRect().width, CalcFallbackRowHeight());
     invalidateArea.UnionRect(invalidateArea, emptyFallbackArea);
     containerFrame->Invalidate(invalidateArea);
   }
 }
 
 NS_QUERYFRAME_HEAD(nsListControlFrame)
   NS_QUERYFRAME_ENTRY(nsIFormControlFrame)
   NS_QUERYFRAME_ENTRY(nsIListControlFrame)
--- a/layout/forms/nsListControlFrame.h
+++ b/layout/forms/nsListControlFrame.h
@@ -267,16 +267,21 @@ public:
   }
 
   /**
    * Return whether the list is in dropdown mode.
    */
   PRBool IsInDropDownMode() const;
 
   /**
+   * Dropdowns need views
+   */
+  virtual PRBool NeedsView() { return IsInDropDownMode(); }
+
+  /**
    * Frees statics owned by this class.
    */
   static void Shutdown();
 
 #ifdef ACCESSIBILITY
   /**
    * Post a custom DOM event for the change, so that accessibility can
    * fire a native focus event for accessibility 
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -39,17 +39,16 @@
 
 /* rendering object to wrap rendering objects that should be scrollable */
 
 #include "nsCOMPtr.h"
 #include "nsHTMLParts.h"
 #include "nsPresContext.h"
 #include "nsIServiceManager.h"
 #include "nsIView.h"
-#include "nsIScrollableView.h"
 #include "nsIScrollable.h"
 #include "nsIViewManager.h"
 #include "nsHTMLContainerFrame.h"
 #include "nsGfxScrollFrame.h"
 #include "nsGkAtoms.h"
 #include "nsINameSpaceManager.h"
 #include "nsIDocument.h"
 #include "nsIFontMetrics.h"
@@ -112,22 +111,17 @@ nsHTMLScrollFrame::DestroyFrom(nsIFrame*
   nsHTMLContainerFrame::DestroyFrom(aDestructRoot);
 }
 
 NS_IMETHODIMP
 nsHTMLScrollFrame::SetInitialChildList(nsIAtom*     aListName,
                                        nsFrameList& aChildList)
 {
   nsresult rv = nsHTMLContainerFrame::SetInitialChildList(aListName, aChildList);
-  mInner.CreateScrollableView();
   mInner.ReloadChildFrames();
-
-  // listen for scroll events.
-  mInner.GetScrollableView()->AddScrollPositionListener(&mInner);
-
   return rv;
 }
 
 
 NS_IMETHODIMP
 nsHTMLScrollFrame::AppendFrames(nsIAtom*  aListName,
                                 nsFrameList& aFrameList)
 {
@@ -183,17 +177,17 @@ nsHTMLScrollFrame::InvalidateInternal(co
                                       nscoord aX, nscoord aY, nsIFrame* aForChild,
                                       PRUint32 aFlags)
 {
   if (aForChild) {
     if (aForChild == mInner.mScrolledFrame) {
       // restrict aDamageRect to the scrollable view's bounds
       nsRect damage = aDamageRect + nsPoint(aX, aY);
       nsRect r;
-      if (r.IntersectRect(damage, mInner.mScrollableView->View()->GetBounds())) {
+      if (r.IntersectRect(damage, mInner.mScrollPort)) {
         nsHTMLContainerFrame::InvalidateInternal(r, 0, 0, aForChild, aFlags);
       }
       if (mInner.mIsRoot && r != damage) {
         // Make sure we notify our prescontext about invalidations outside
         // viewport clipping.
         // This is important for things that are snapshotting the viewport,
         // possibly outside the scrolled bounds.
         // We don't need to propagate this any further up, though. Anyone who
@@ -230,22 +224,21 @@ nsHTMLScrollFrame::InvalidateInternal(co
 
 struct ScrollReflowState {
   const nsHTMLReflowState& mReflowState;
   nsBoxLayoutState mBoxState;
   nsGfxScrollFrameInner::ScrollbarStyles mStyles;
   nsMargin mComputedBorder;
 
   // === Filled in by ReflowScrolledFrame ===
+  nsRect mContentsOverflowArea;
   PRPackedBool mReflowedContentsWithHScrollbar;
   PRPackedBool mReflowedContentsWithVScrollbar;
 
   // === Filled in when TryLayout succeeds ===
-  // The area of the scrollport, in coordinates relative to the scrollframe
-  nsRect mScrollPortRect;
   // The size of the inside-border area
   nsSize mInsideBorderSize;
   // Whether we decided to show the horizontal scrollbar
   PRPackedBool mShowHScrollbar;
   // Whether we decided to show the vertical scrollbar
   PRPackedBool mShowVScrollbar;
 
   ScrollReflowState(nsIScrollableFrame* aFrame,
@@ -374,17 +367,18 @@ nsHTMLScrollFrame::TryLayout(ScrollReflo
   desiredInsideBorderSize.height = hScrollbarDesiredHeight +
     NS_MAX(aKidMetrics->height, vScrollbarMinHeight);
   aState->mInsideBorderSize =
     ComputeInsideBorderSize(aState, desiredInsideBorderSize);
   nsSize scrollPortSize = nsSize(NS_MAX(0, aState->mInsideBorderSize.width - vScrollbarDesiredWidth),
                                  NS_MAX(0, aState->mInsideBorderSize.height - hScrollbarDesiredHeight));
                                                                                 
   if (!aForce) {
-    nsRect scrolledRect = mInner.GetScrolledRect(scrollPortSize);
+    nsRect scrolledRect =
+      mInner.GetScrolledRectInternal(aState->mContentsOverflowArea, scrollPortSize);
     nscoord oneDevPixel = aState->mBoxState.PresContext()->DevPixelsToAppUnits(1);
 
     // If the style is HIDDEN then we already know that aAssumeHScroll is PR_FALSE
     if (aState->mStyles.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN) {
       PRBool wantHScrollbar =
         aState->mStyles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL ||
         scrolledRect.XMost() >= scrollPortSize.width + oneDevPixel ||
         scrolledRect.x <= -oneDevPixel;
@@ -413,17 +407,17 @@ nsHTMLScrollFrame::TryLayout(ScrollReflo
 
   aState->mShowHScrollbar = aAssumeHScroll;
   aState->mShowVScrollbar = aAssumeVScroll;
   nsPoint scrollPortOrigin(aState->mComputedBorder.left,
                            aState->mComputedBorder.top);
   if (!mInner.IsScrollbarOnRight()) {
     scrollPortOrigin.x += vScrollbarActualWidth;
   }
-  aState->mScrollPortRect = nsRect(scrollPortOrigin, scrollPortSize);
+  mInner.mScrollPort = nsRect(scrollPortOrigin, scrollPortSize);
   return PR_TRUE;
 }
 
 PRBool
 nsHTMLScrollFrame::ScrolledContentDependsOnHeight(ScrollReflowState* aState)
 {
   // Return true if ReflowScrolledFrame is going to do something different
   // based on the presence of a horizontal scrollbar.
@@ -490,17 +484,17 @@ nsHTMLScrollFrame::ReflowScrolledFrame(S
   kidReflowState.SetComputedHeight(computedHeight);
   kidReflowState.mComputedMinHeight = computedMinHeight;
   kidReflowState.mComputedMaxHeight = computedMaxHeight;
 
   nsReflowStatus status;
   nsresult rv = ReflowChild(mInner.mScrolledFrame, presContext, *aMetrics,
                             kidReflowState, 0, 0,
                             NS_FRAME_NO_MOVE_FRAME | NS_FRAME_NO_MOVE_VIEW, status);
-  // Don't resize or position the view because we're going to resize
+  // Don't resize or position the view (if any) because we're going to resize
   // it to the correct size anyway in PlaceScrollArea. Allowing it to
   // resize here would size it to the natural height of the frame,
   // which will usually be different from the scrollport height;
   // invalidating the difference will cause unnecessary repainting.
   FinishReflowChild(mInner.mScrolledFrame, presContext,
                     &kidReflowState, *aMetrics, 0, 0,
                     NS_FRAME_NO_MOVE_FRAME | NS_FRAME_NO_MOVE_VIEW | NS_FRAME_NO_SIZE_VIEW);
 
@@ -509,16 +503,17 @@ nsHTMLScrollFrame::ReflowScrolledFrame(S
   // always set mOverflowArea. In fact nsObjectFrame and nsFrameFrame don't
   // support the 'outline' property because of this. Rather than fix the world
   // right now, just fix up the overflow area if necessary. Note that we don't
   // check HasOverflowRect() because it could be set even though the
   // overflow area doesn't include the frame bounds.
   aMetrics->mOverflowArea.UnionRect(aMetrics->mOverflowArea,
                                     nsRect(0, 0, aMetrics->width, aMetrics->height));
 
+  aState->mContentsOverflowArea = aMetrics->mOverflowArea;
   aState->mReflowedContentsWithHScrollbar = aAssumeHScroll;
   aState->mReflowedContentsWithVScrollbar = aAssumeVScroll;
   
   return rv;
 }
 
 PRBool
 nsHTMLScrollFrame::GuessHScrollbarNeeded(const ScrollReflowState& aState)
@@ -611,17 +606,18 @@ nsHTMLScrollFrame::ReflowContents(Scroll
   // XXX Is this check really sufficient to catch all the incremental cases
   // where the ideal case doesn't have a scrollbar?
   if ((aState->mReflowedContentsWithHScrollbar || aState->mReflowedContentsWithVScrollbar) &&
       aState->mStyles.mVertical != NS_STYLE_OVERFLOW_SCROLL &&
       aState->mStyles.mHorizontal != NS_STYLE_OVERFLOW_SCROLL) {
     nsSize insideBorderSize =
       ComputeInsideBorderSize(aState,
                               nsSize(kidDesiredSize.width, kidDesiredSize.height));
-    nsRect scrolledRect = mInner.GetScrolledRect(insideBorderSize);
+    nsRect scrolledRect =
+      mInner.GetScrolledRectInternal(kidDesiredSize.mOverflowArea, insideBorderSize);
     if (nsRect(nsPoint(0, 0), insideBorderSize).Contains(scrolledRect)) {
       // Let's pretend we had no scrollbars coming in here
       rv = ReflowScrolledFrame(aState, PR_FALSE, PR_FALSE,
                                &kidDesiredSize, PR_FALSE);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
@@ -654,55 +650,52 @@ nsHTMLScrollFrame::ReflowContents(Scroll
   TryLayout(aState, &kidDesiredSize,
             aState->mStyles.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN,
             aState->mStyles.mVertical != NS_STYLE_OVERFLOW_HIDDEN,
             PR_TRUE, &rv);
   return rv;
 }
 
 void
-nsHTMLScrollFrame::PlaceScrollArea(const ScrollReflowState& aState)
+nsHTMLScrollFrame::PlaceScrollArea(const ScrollReflowState& aState,
+                                   const nsPoint& aScrollPosition)
 {
-  nsIView* scrollView = mInner.mScrollableView->View();
-  nsIViewManager* vm = scrollView->GetViewManager();
-  vm->MoveViewTo(scrollView, aState.mScrollPortRect.x, aState.mScrollPortRect.y);
-  vm->ResizeView(scrollView, nsRect(nsPoint(0, 0), aState.mScrollPortRect.Size()),
-                 PR_TRUE);
-
   nsIFrame *scrolledFrame = mInner.mScrolledFrame;
-  nsIView *scrolledView = scrolledFrame->GetView();
-  // Set the x,y of the scrolled frame to the correct value: the displacement
-  // from its origin to the origin of this frame
-  scrolledFrame->SetPosition(scrolledView->GetOffsetTo(GetView()));
+  // Set the x,y of the scrolled frame to the correct value
+  scrolledFrame->SetPosition(mInner.mScrollPort.TopLeft() - aScrollPosition);
 
   nsRect scrolledArea;
   // Preserve the width or height of empty rects
-  scrolledArea.UnionRectIncludeEmpty(mInner.GetScrolledRect(aState.mScrollPortRect.Size()),
-                                     nsRect(nsPoint(0,0), aState.mScrollPortRect.Size()));
+  nsSize portSize = mInner.mScrollPort.Size();
+  nsRect scrolledRect = mInner.GetScrolledRectInternal(aState.mContentsOverflowArea, portSize);
+  scrolledArea.UnionRectIncludeEmpty(scrolledRect,
+                                     nsRect(nsPoint(0,0), portSize));
 
   // Store the new overflow area. Note that this changes where an outline
   // of the scrolled frame would be painted, but scrolled frames can't have
   // outlines (the outline would go on this scrollframe instead).
   // Using FinishAndStoreOverflow is needed so the overflow rect
   // gets set correctly.  It also messes with the overflow rect in the
   // -moz-hidden-unscrollable case, but scrolled frames can't have
   // 'overflow' either.
   // This needs to happen before SyncFrameViewAfterReflow so
   // HasOverflowRect() will return the correct value.
   scrolledFrame->FinishAndStoreOverflow(&scrolledArea,
                                         scrolledFrame->GetSize());
 
   // Note that making the view *exactly* the size of the scrolled area
   // is critical, since the view scrolling code uses the size of the
   // scrolled view to clamp scroll requests.
+  // Normally the scrolledFrame won't have a view but in some cases it
+  // might create its own.
   nsContainerFrame::SyncFrameViewAfterReflow(scrolledFrame->PresContext(),
                                              scrolledFrame,
-                                             scrolledView,
+                                             scrolledFrame->GetView(),
                                              &scrolledArea,
-                                             NS_FRAME_NO_MOVE_VIEW);
+                                             0);
 }
 
 nscoord
 nsHTMLScrollFrame::GetIntrinsicVScrollbarWidth(nsIRenderingContext *aRenderingContext)
 {
   nsGfxScrollFrameInner::ScrollbarStyles ss = GetScrollbarStyles();
   if (ss.mVertical != NS_STYLE_OVERFLOW_SCROLL || !mInner.mVScrollbarBox)
     return 0;
@@ -779,55 +772,64 @@ nsHTMLScrollFrame::Reflow(nsPresContext*
     reflowContents = NEEDS_REFLOW(mInner.mScrolledFrame);
     reflowHScrollbar = NEEDS_REFLOW(mInner.mHScrollbarBox);
     reflowVScrollbar = NEEDS_REFLOW(mInner.mVScrollbarBox);
     reflowScrollCorner = NEEDS_REFLOW(mInner.mScrollCornerBox);
 
     #undef NEEDS_REFLOW
   }
 
-  nsRect oldScrollAreaBounds = mInner.mScrollableView->View()->GetBounds();
-  nsRect oldScrolledAreaBounds = mInner.mScrolledFrame->GetView()->GetBounds();
+  nsRect oldScrollAreaBounds = mInner.mScrollPort;
+  nsRect oldScrolledAreaBounds =
+    mInner.mScrolledFrame->GetOverflowRectRelativeToParent();
+  // Adjust to a multiple of device pixels to restore the invariant that
+  // oldScrollPosition is a multiple of device pixels. This could have been
+  // thrown out by a zoom change.
+  nsIntPoint ptDevPx;
+  nsPoint oldScrollPosition =
+    mInner.ClampAndRestrictToDevPixels(mInner.GetScrollPosition(), &ptDevPx);
+  
   state.mComputedBorder = aReflowState.mComputedBorderPadding -
     aReflowState.mComputedPadding;
 
   nsresult rv = ReflowContents(&state, aDesiredSize);
   if (NS_FAILED(rv))
     return rv;
   
-  PlaceScrollArea(state);
+  PlaceScrollArea(state, oldScrollPosition);
   if (!mInner.mPostedReflowCallback) {
     // Make sure we'll try scrolling to restored position
     PresContext()->PresShell()->PostReflowCallback(&mInner);
     mInner.mPostedReflowCallback = PR_TRUE;
   }
 
   PRBool didHaveHScrollbar = mInner.mHasHorizontalScrollbar;
   PRBool didHaveVScrollbar = mInner.mHasVerticalScrollbar;
   mInner.mHasHorizontalScrollbar = state.mShowHScrollbar;
   mInner.mHasVerticalScrollbar = state.mShowVScrollbar;
-  nsRect newScrollAreaBounds = mInner.mScrollableView->View()->GetBounds();
-  nsRect newScrolledAreaBounds = mInner.mScrolledFrame->GetView()->GetBounds();
+  nsRect newScrollAreaBounds = mInner.mScrollPort;
+  nsRect newScrolledAreaBounds =
+    mInner.mScrolledFrame->GetOverflowRectRelativeToParent();
   if (mInner.mSkippedScrollbarLayout ||
       reflowHScrollbar || reflowVScrollbar || reflowScrollCorner ||
       (GetStateBits() & NS_FRAME_IS_DIRTY) ||
       didHaveHScrollbar != state.mShowHScrollbar ||
       didHaveVScrollbar != state.mShowVScrollbar ||
       oldScrollAreaBounds != newScrollAreaBounds ||
       oldScrolledAreaBounds != newScrolledAreaBounds) {
     if (!mInner.mSupppressScrollbarUpdate) {
       mInner.mSkippedScrollbarLayout = PR_FALSE;
       mInner.SetScrollbarVisibility(mInner.mHScrollbarBox, state.mShowHScrollbar);
       mInner.SetScrollbarVisibility(mInner.mVScrollbarBox, state.mShowVScrollbar);
       // place and reflow scrollbars
       nsRect insideBorderArea =
         nsRect(nsPoint(state.mComputedBorder.left, state.mComputedBorder.top),
                state.mInsideBorderSize);
       mInner.LayoutScrollbars(state.mBoxState, insideBorderArea,
-                              oldScrollAreaBounds, state.mScrollPortRect);
+                              oldScrollAreaBounds);
     } else {
       mInner.mSkippedScrollbarLayout = PR_TRUE;
     }
   }
 
   aDesiredSize.width = state.mInsideBorderSize.width +
     state.mComputedBorder.LeftRight();
   aDesiredSize.height = state.mInsideBorderSize.height +
@@ -959,23 +961,17 @@ nsXULScrollFrame::DestroyFrom(nsIFrame* 
   nsBoxFrame::DestroyFrom(aDestructRoot);
 }
 
 NS_IMETHODIMP
 nsXULScrollFrame::SetInitialChildList(nsIAtom*        aListName,
                                       nsFrameList&    aChildList)
 {
   nsresult rv = nsBoxFrame::SetInitialChildList(aListName, aChildList);
-
-  mInner.CreateScrollableView();
   mInner.ReloadChildFrames();
-
-  // listen for scroll events.
-  mInner.GetScrollableView()->AddScrollPositionListener(&mInner);
-
   return rv;
 }
 
 
 NS_IMETHODIMP
 nsXULScrollFrame::AppendFrames(nsIAtom*        aListName,
                                nsFrameList&    aFrameList)
 {
@@ -1031,18 +1027,17 @@ nsXULScrollFrame::GetType() const
 void
 nsXULScrollFrame::InvalidateInternal(const nsRect& aDamageRect,
                                      nscoord aX, nscoord aY, nsIFrame* aForChild,
                                      PRUint32 aFlags)
 {
   if (aForChild == mInner.mScrolledFrame) {
     // restrict aDamageRect to the scrollable view's bounds
     nsRect r;
-    if (r.IntersectRect(aDamageRect + nsPoint(aX, aY),
-                        mInner.mScrollableView->View()->GetBounds())) {
+    if (r.IntersectRect(aDamageRect + nsPoint(aX, aY), mInner.mScrollPort)) {
       nsBoxFrame::InvalidateInternal(r, 0, 0, aForChild, aFlags);
     }
     return;
   }
   
   nsBoxFrame::InvalidateInternal(aDamageRect, aX, aY, aForChild, aFlags);
 }
 
@@ -1196,33 +1191,98 @@ nsXULScrollFrame::PostScrolledAreaEventF
 NS_QUERYFRAME_HEAD(nsXULScrollFrame)
   NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
   NS_QUERYFRAME_ENTRY(nsIScrollableFrame)
   NS_QUERYFRAME_ENTRY(nsIStatefulFrame)
 NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame)
  
 //-------------------- Inner ----------------------
 
+#define SMOOTH_SCROLL_MSECS_PER_FRAME 10
+#define SMOOTH_SCROLL_FRAMES    10
+
+#define SMOOTH_SCROLL_PREF_NAME "general.smoothScroll"
+
+class nsGfxScrollFrameInner::AsyncScroll {
+public:
+  AsyncScroll() {}
+  ~AsyncScroll() {
+    if (mScrollTimer) mScrollTimer->Cancel();
+  }
+
+  nsCOMPtr<nsITimer> mScrollTimer;
+  PRInt32 mVelocities[SMOOTH_SCROLL_FRAMES*2];
+  PRInt32 mFrameIndex;
+  PRPackedBool mIsSmoothScroll;
+};
+
+static void ComputeVelocities(PRInt32 aCurVelocity, nscoord aCurPos, nscoord aDstPos,
+                              PRInt32* aVelocities, PRInt32 aP2A)
+{
+  // scrolling always works in units of whole pixels. So compute velocities
+  // in pixels and then scale them up. This ensures, for example, that
+  // a 1-pixel scroll isn't broken into N frames of 1/N pixels each, each
+  // frame increment being rounded to 0 whole pixels.
+  aCurPos = NSAppUnitsToIntPixels(aCurPos, aP2A);
+  aDstPos = NSAppUnitsToIntPixels(aDstPos, aP2A);
+
+  PRInt32 i;
+  PRInt32 direction = (aCurPos < aDstPos ? 1 : -1);
+  PRInt32 absDelta = (aDstPos - aCurPos)*direction;
+  PRInt32 baseVelocity = absDelta/SMOOTH_SCROLL_FRAMES;
+
+  for (i = 0; i < SMOOTH_SCROLL_FRAMES; i++) {
+    aVelocities[i*2] = baseVelocity;
+  }
+  nscoord total = baseVelocity*SMOOTH_SCROLL_FRAMES;
+  for (i = 0; i < SMOOTH_SCROLL_FRAMES; i++) {
+    if (total < absDelta) {
+      aVelocities[i*2]++;
+      total++;
+    }
+  }
+  NS_ASSERTION(total == absDelta, "Invalid velocity sum");
+
+  PRInt32 scale = NSIntPixelsToAppUnits(direction, aP2A);
+  for (i = 0; i < SMOOTH_SCROLL_FRAMES; i++) {
+    aVelocities[i*2] *= scale;
+  }
+}
+
+static PRBool
+IsSmoothScrollingEnabled()
+{
+  nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+  if (prefs) {
+    PRBool enabled;
+    nsresult rv = prefs->GetBoolPref(SMOOTH_SCROLL_PREF_NAME, &enabled);
+    if (NS_SUCCEEDED(rv)) {
+      return enabled;
+    }
+  }
+  return PR_FALSE;
+}
+
 nsGfxScrollFrameInner::nsGfxScrollFrameInner(nsContainerFrame* aOuter,
                                              PRBool aIsRoot,
                                              PRBool aIsXUL)
-  : mScrollableView(nsnull),
-    mHScrollbarBox(nsnull),
+  : mHScrollbarBox(nsnull),
     mVScrollbarBox(nsnull),
     mScrolledFrame(nsnull),
     mScrollCornerBox(nsnull),
     mOuter(aOuter),
-    mRestoreRect(-1, -1, -1, -1),
+    mAsyncScroll(nsnull),
+    mDestination(0, 0),
+    mRestorePos(-1, -1),
     mLastPos(-1, -1),
     mNeverHasVerticalScrollbar(PR_FALSE),
     mNeverHasHorizontalScrollbar(PR_FALSE),
     mHasVerticalScrollbar(PR_FALSE), 
     mHasHorizontalScrollbar(PR_FALSE),
-    mViewInitiatedScroll(PR_FALSE),
-    mFrameInitiatedScroll(PR_FALSE),
+    mFrameIsUpdatingScrollbar(PR_FALSE),
     mDidHistoryRestore(PR_FALSE),
     mIsRoot(aIsRoot),
     mIsXUL(aIsXUL),
     mSupppressScrollbarUpdate(PR_FALSE),
     mSkippedScrollbarLayout(PR_FALSE),
     mDidLoadHistoryVScrollbarHint(PR_FALSE),
     mHistoryVScrollbarHint(PR_FALSE),
     mHadNonInitialReflow(PR_FALSE),
@@ -1231,19 +1291,508 @@ nsGfxScrollFrameInner::nsGfxScrollFrameI
     mPostedReflowCallback(PR_FALSE),
     mMayHaveDirtyFixedChildren(PR_FALSE),
     mUpdateScrollbarAttributes(PR_FALSE)
 {
 }
 
 nsGfxScrollFrameInner::~nsGfxScrollFrameInner()
 {
+  delete mAsyncScroll;
 }
 
-NS_IMPL_QUERY_INTERFACE1(nsGfxScrollFrameInner, nsIScrollPositionListener)
+static nscoord
+Clamp(nscoord aLower, nscoord aVal, nscoord aUpper)
+{
+  if (aVal < aLower)
+    return aLower;
+  if (aVal > aUpper)
+    return aUpper;
+  return aVal;
+}
+
+nsPoint
+nsGfxScrollFrameInner::ClampScrollPosition(const nsPoint& aPt) const
+{
+  nsRect range = GetScrollRange();
+  return nsPoint(Clamp(range.x, aPt.x, range.XMost()),
+                 Clamp(range.y, aPt.y, range.YMost()));
+}
+
+/*
+ * Callback function from timer used in nsGfxScrollFrameInner::ScrollTo
+ */
+void
+nsGfxScrollFrameInner::AsyncScrollCallback(nsITimer *aTimer, void* anInstance)
+{
+  nsGfxScrollFrameInner* self = static_cast<nsGfxScrollFrameInner*>(anInstance);
+  if (!self || !self->mAsyncScroll)
+    return;
+
+  if (self->mAsyncScroll->mIsSmoothScroll) {
+    // XXX this is crappy, the scroll position needs to be based on the
+    // current time
+    NS_ASSERTION(self->mAsyncScroll->mFrameIndex < SMOOTH_SCROLL_FRAMES,
+                 "Past last frame?");
+    nscoord* velocities =
+      &self->mAsyncScroll->mVelocities[self->mAsyncScroll->mFrameIndex*2];
+    nsPoint destination =
+      self->GetScrollPosition() + nsPoint(velocities[0], velocities[1]);        
+
+    self->mAsyncScroll->mFrameIndex++;
+    if (self->mAsyncScroll->mFrameIndex >= SMOOTH_SCROLL_FRAMES) {
+      delete self->mAsyncScroll;
+      self->mAsyncScroll = nsnull;
+    }
+
+    self->ScrollToImpl(destination);
+    // 'self' may be a dangling pointer here since ScrollToImpl may have destroyed it
+  } else {
+    delete self->mAsyncScroll;
+    self->mAsyncScroll = nsnull;
+
+    self->ScrollToImpl(self->mDestination);
+    // 'self' may be a dangling pointer here since ScrollToImpl may have destroyed it
+  }
+}
+
+/*
+ * this method wraps calls to ScrollToImpl(), either in one shot or incrementally,
+ *  based on the setting of the smooth scroll pref
+ */
+void
+nsGfxScrollFrameInner::ScrollTo(nsPoint aScrollPosition,
+                                nsIScrollableFrame::ScrollMode aMode)
+{
+  mDestination = ClampScrollPosition(aScrollPosition);
+
+  if (aMode == nsIScrollableFrame::INSTANT) {
+    // Asynchronous scrolling is not allowed, so we'll kill any existing
+    // async-scrolling process and do an instant scroll
+    delete mAsyncScroll;
+    mAsyncScroll = nsnull;
+    ScrollToImpl(mDestination);
+    return;
+  }
+
+  PRInt32 currentVelocityX = 0;
+  PRInt32 currentVelocityY = 0;
+  PRBool isSmoothScroll = IsSmoothScrollingEnabled();
+
+  if (mAsyncScroll) {
+    if (mAsyncScroll->mIsSmoothScroll) {
+      currentVelocityX = mAsyncScroll->mVelocities[mAsyncScroll->mFrameIndex*2];
+      currentVelocityY = mAsyncScroll->mVelocities[mAsyncScroll->mFrameIndex*2 + 1];
+    }
+  } else {
+    mAsyncScroll = new AsyncScroll;
+    if (mAsyncScroll) {
+      mAsyncScroll->mScrollTimer = do_CreateInstance("@mozilla.org/timer;1");
+      if (!mAsyncScroll->mScrollTimer) {
+        delete mAsyncScroll;
+        mAsyncScroll = nsnull;
+      }
+    }
+    if (!mAsyncScroll) {
+      // some allocation failed. Scroll the normal way.
+      ScrollToImpl(mDestination);
+      return;
+    }
+    if (isSmoothScroll) {
+      mAsyncScroll->mScrollTimer->InitWithFuncCallback(
+        AsyncScrollCallback, this, SMOOTH_SCROLL_MSECS_PER_FRAME,
+        nsITimer::TYPE_REPEATING_PRECISE);
+    } else {
+      mAsyncScroll->mScrollTimer->InitWithFuncCallback(
+        AsyncScrollCallback, this, 0, nsITimer::TYPE_ONE_SHOT);
+    }
+  }
+
+  mAsyncScroll->mFrameIndex = 0;
+  mAsyncScroll->mIsSmoothScroll = isSmoothScroll;
+
+  if (isSmoothScroll) {
+    PRInt32 p2a = mOuter->PresContext()->AppUnitsPerDevPixel();
+
+    // compute velocity vectors
+    nsPoint currentPos = GetScrollPosition();
+    ComputeVelocities(currentVelocityX, currentPos.x, mDestination.x,
+                      mAsyncScroll->mVelocities, p2a);
+    ComputeVelocities(currentVelocityY, currentPos.y, mDestination.y,
+                      mAsyncScroll->mVelocities + 1, p2a);
+  }
+}
+
+static void InvalidateWidgets(nsIView* aView)
+{
+  if (aView->HasWidget()) {
+    nsIWidget* widget = aView->GetWidget();
+    nsWindowType type;
+    widget->GetWindowType(type);
+    if (type != eWindowType_popup) {
+      // Force the widget and everything in it to repaint. We can't
+      // just use Invalidate because the widget might have child
+      // widgets and they wouldn't get updated. We can't call
+      // UpdateView(aView) because the area to be repainted might be
+      // outside aView's clipped bounds. This isn't the greatest way
+      // to achieve this, perhaps, but it works.
+      widget->Show(PR_FALSE);
+      widget->Show(PR_TRUE);
+    }
+    return;
+  }
+
+  for (nsIView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) {
+    InvalidateWidgets(v);
+  }
+}
+
+// We can't use nsContainerFrame::PositionChildViews here because
+// we don't want to invalidate views that have moved.
+// aInvalidateWidgets is set to true if we should invalidate the area
+// covered by every widget in the subtree.
+static void AdjustViewsAndWidgets(nsIFrame* aFrame,
+                                  PRBool aInvalidateWidgets)
+{
+  nsIView* view = aFrame->GetView();
+  if (view) {
+    nsPoint pt;
+    aFrame->GetParent()->GetClosestView(&pt);
+    pt += aFrame->GetPosition();
+    view->SetPosition(pt.x, pt.y);
+
+    if (aInvalidateWidgets) {
+      InvalidateWidgets(view);
+    }
+    return;
+  }
+
+  if (!(aFrame->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW)) {
+    return;
+  }
+
+  nsIAtom* childListName = nsnull;
+  PRInt32  childListIndex = 0;
+  do {
+    // Recursively walk aFrame's child frames
+    nsIFrame* childFrame = aFrame->GetFirstChild(childListName);
+    while (childFrame) {
+      AdjustViewsAndWidgets(childFrame, aInvalidateWidgets);
+
+      // Get the next sibling child frame
+      childFrame = childFrame->GetNextSibling();
+    }
+
+    // also process the additional child lists, but skip the popup list as the
+    // views for popups are not scrolled.
+    do {
+      childListName = aFrame->GetAdditionalChildListName(childListIndex++);
+    } while (childListName == nsGkAtoms::popupList);
+  } while (childListName);
+}
+
+/**
+ * Given aBlitRegion in appunits, create and return an nsRegion in
+ * device pixels that represents the device pixels whose centers are
+ * contained in aBlitRegion. Whatever appunit area was removed from
+ * aBlitRegion in that process is added to aRepaintRegion. An appunits
+ * version of the result is placed in aAppunitsBlitRegion.
+ * 
+ * We convert the blit region to pixels this way because in general
+ * frames touch the pixels whose centers are contained in the
+ * (possibly not pixel-aligned) frame bounds.
+ */
+static void
+ConvertBlitRegionToPixelRects(const nsRegion& aBlitRegion,
+                              nscoord aAppUnitsPerPixel,
+                              nsTArray<nsIntRect>* aPixelRects,
+                              nsRegion* aRepaintRegion,
+                              nsRegion* aAppunitsBlitRegion)
+{
+  const nsRect* r;
+
+  aPixelRects->Clear();
+  aAppunitsBlitRegion->SetEmpty();
+  // The rectangles in aBlitRegion don't overlap so converting them via
+  // ToNearestPixels also produces a sequence of non-overlapping rectangles
+  for (nsRegionRectIterator iter(aBlitRegion); (r = iter.Next());) {
+    nsIntRect pixRect = r->ToNearestPixels(aAppUnitsPerPixel);
+    aPixelRects->AppendElement(pixRect);
+    aAppunitsBlitRegion->Or(*aAppunitsBlitRegion,
+                            pixRect.ToAppUnits(aAppUnitsPerPixel));
+  }
+
+  nsRegion repaint;
+  repaint.Sub(aBlitRegion, *aAppunitsBlitRegion);
+  aRepaintRegion->Or(*aRepaintRegion, repaint);
+}
+
+/**
+ * An nsTArray comparator that lets us sort nsIntRects by their right edge.
+ */
+class RightEdgeComparator {
+public:
+  /** @return True if the elements are equals; false otherwise. */
+  PRBool Equals(const nsIntRect& aA, const nsIntRect& aB) const
+  {
+    return aA.XMost() == aB.XMost();
+  }
+  /** @return True if (a < b); false otherwise. */
+  PRBool LessThan(const nsIntRect& aA, const nsIntRect& aB) const
+  {
+    return aA.XMost() < aB.XMost();
+  }
+};
+
+// If aPixDelta has a negative component, flip aRect across the
+// axis in that direction. We do this so we can assume all scrolling is
+// down and to the right to simplify SortBlitRectsForCopy
+static nsIntRect
+FlipRect(const nsIntRect& aRect, nsIntPoint aPixDelta)
+{
+  nsIntRect r = aRect;
+  if (aPixDelta.x < 0) {
+    r.x = -r.XMost();
+  }
+  if (aPixDelta.y < 0) {
+    r.y = -r.YMost();
+  }
+  return r;
+}
+
+// Sort aRects so that moving rectangle aRects[i] - aPixDelta to aRects[i]
+// will not cause the rectangle to overlap any rectangles that haven't
+// moved yet.
+// See http://weblogs.mozillazine.org/roc/archives/2009/08/homework_answer.html
+static void
+SortBlitRectsForCopy(nsIntPoint aPixDelta, nsTArray<nsIntRect>* aRects)
+{
+  nsTArray<nsIntRect> rects;
+
+  for (PRUint32 i = 0; i < aRects->Length(); ++i) {
+    nsIntRect* r = &aRects->ElementAt(i);
+    nsIntRect rect =
+      FlipRect(nsIntRect(r->x, r->y, r->width, r->height), aPixDelta);
+    rects.AppendElement(rect);
+  }
+  rects.Sort(RightEdgeComparator());
+
+  aRects->Clear();
+  // This could probably be improved a bit for some worst-case scenarios.
+  // But in common cases this should be very fast, and we shouldn't
+  // make it more complex unless we really need to.
+  while (!rects.IsEmpty()) {
+    PRInt32 i = rects.Length() - 1;
+    PRBool overlappedBelow;
+    do {
+      overlappedBelow = PR_FALSE;
+      const nsIntRect& rectI = rects[i];
+      // see if any rectangle < i overlaps rectI horizontally and is below
+      // rectI
+      for (PRInt32 j = i - 1; j >= 0; --j) {
+        if (rects[j].XMost() <= rectI.x) {
+          // No rectangle with index <= j can overlap rectI horizontally
+          break;
+        }
+        // Rectangle j overlaps rectI horizontally.
+        if (rects[j].y >= rectI.y) {
+          // Rectangle j is below rectangle i. This is the rightmost such
+          // rectangle, so set i to this rectangle and continue.
+          i = j;
+          overlappedBelow = PR_TRUE;
+          break;
+        }
+      }
+    } while (overlappedBelow); 
+
+    // Rectangle i has no rectangles to the right or below.
+    // Flip it back before saving the result.
+    aRects->AppendElement(FlipRect(rects[i], aPixDelta));
+    rects.RemoveElementAt(i);
+  }
+}
+
+static PRBool
+CanScrollWithBlitting(nsIFrame* aFrame)
+{
+  for (nsIFrame* f = aFrame; f; f = f->GetParent()) {
+    if (f->GetStyleDisplay()->HasTransform()) {
+      return PR_FALSE;
+    }
+#ifdef MOZ_SVG
+    if (nsSVGIntegrationUtils::UsingEffectsForFrame(f) ||
+        f->IsFrameOfType(nsIFrame::eSVG)) {
+      return PR_FALSE;
+    }
+#endif
+  }
+  return PR_TRUE;
+}
+
+void nsGfxScrollFrameInner::ScrollVisual(nsIntPoint aPixDelta)
+{
+  nsRootPresContext* rootPresContext = mOuter->PresContext()->RootPresContext();
+
+  nsPoint offsetToView;
+  nsPoint offsetToWidget;
+  nsIWidget* nearestWidget =
+    mOuter->GetClosestView(&offsetToView)->GetNearestWidget(&offsetToWidget);
+  nsPoint nearestWidgetOffset = offsetToView + offsetToWidget;
+
+  nsTArray<nsIWidget::Configuration> configurations;
+  // Only update plugin configurations if we're going to scroll the
+  // root widget. Otherwise we must be in a popup or some other situation
+  // where we don't actually support windows plugins.
+  if (rootPresContext->FrameManager()->GetRootFrame()->GetWindow() == nearestWidget) {
+    rootPresContext->GetPluginGeometryUpdates(mOuter, &configurations);
+  }
+
+  if (!nearestWidget ||
+      nearestWidget->GetTransparencyMode() == eTransparencyTransparent ||
+      !CanScrollWithBlitting(mOuter)) {
+    // Just invalidate the frame and adjust child widgets
+    // Recall that our widget's origin is at our bounds' top-left
+    if (nearestWidget) {
+      nearestWidget->ConfigureChildren(configurations);
+    }
+    AdjustViewsAndWidgets(mScrolledFrame, PR_FALSE);
+    // We need to call this after fixing up the widget and view positions
+    // to be consistent with the view and frame hierarchy.
+    mOuter->InvalidateWithFlags(mScrollPort,
+                                nsIFrame::INVALIDATE_REASON_SCROLL_REPAINT);
+  } else {
+    nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(mOuter);
+    nsRegion blitRegion;
+    nsRegion repaintRegion;
+    nsPresContext* presContext = mOuter->PresContext();
+    nsPoint delta(presContext->DevPixelsToAppUnits(aPixDelta.x),
+                  presContext->DevPixelsToAppUnits(aPixDelta.y));
+    nsPoint offsetToDisplayRoot = mOuter->GetOffsetTo(displayRoot);
+    nscoord appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
+    nsRect scrollPort =
+      (mScrollPort + offsetToDisplayRoot).ToNearestPixels(appUnitsPerDevPixel).
+      ToAppUnits(appUnitsPerDevPixel);
+    nsresult rv =
+      nsLayoutUtils::ComputeRepaintRegionForCopy(displayRoot, mScrolledFrame,
+                                                 delta, scrollPort,
+                                                 &blitRegion,
+                                                 &repaintRegion);
+    if (NS_FAILED(rv)) {
+      nearestWidget->ConfigureChildren(configurations);
+      AdjustViewsAndWidgets(mScrolledFrame, PR_FALSE);
+      mOuter->InvalidateWithFlags(mScrollPort,
+                                  nsIFrame::INVALIDATE_REASON_SCROLL_REPAINT);
+      return;
+    }
+
+    blitRegion.MoveBy(nearestWidgetOffset - offsetToDisplayRoot);
+    repaintRegion.MoveBy(nearestWidgetOffset - offsetToDisplayRoot);
+
+    // We're going to bit-blit.  Let the viewmanager know so it can
+    // adjust dirty regions appropriately.
+    nsIView* view = displayRoot->GetView();
+    NS_ASSERTION(view, "Display root has no view?");
+    nsIViewManager* vm = view->GetViewManager();
+    vm->WillBitBlit(view, mScrollPort + offsetToDisplayRoot, -delta);
+
+    // innerPixRegion is in device pixels
+    nsTArray<nsIntRect> blitRects;
+    nsRegion blitRectsRegion;
+    ConvertBlitRegionToPixelRects(blitRegion,
+                                  appUnitsPerDevPixel,
+                                  &blitRects, &repaintRegion,
+                                  &blitRectsRegion);
+    SortBlitRectsForCopy(aPixDelta, &blitRects);
+
+    nearestWidget->Scroll(aPixDelta, blitRects, configurations);
+    AdjustViewsAndWidgets(mScrolledFrame, PR_TRUE);
+    repaintRegion.MoveBy(-nearestWidgetOffset + offsetToDisplayRoot);
+    vm->UpdateViewAfterScroll(view, repaintRegion);
+
+    nsIFrame* presContextRootFrame = presContext->FrameManager()->GetRootFrame();
+    if (nearestWidget == presContextRootFrame->GetWindow()) {
+      nsPoint offsetToPresContext = mOuter->GetOffsetTo(presContextRootFrame);
+      blitRectsRegion.MoveBy(-nearestWidgetOffset + offsetToPresContext);
+      repaintRegion.MoveBy(-offsetToDisplayRoot + offsetToPresContext);
+      presContext->NotifyInvalidateForScrolling(blitRectsRegion, repaintRegion);
+    }
+  }
+}
+
+static PRInt32
+ClampInt(nscoord aLower, nscoord aVal, nscoord aUpper, nscoord aAppUnitsPerPixel)
+{
+  PRInt32 low = NSToIntCeil(float(aLower)/aAppUnitsPerPixel);
+  PRInt32 high = NSToIntFloor(float(aUpper)/aAppUnitsPerPixel);
+  PRInt32 v = NSToIntRound(float(aVal)/aAppUnitsPerPixel);
+  NS_ASSERTION(low <= high, "No integers in range; 0 is supposed to be in range");
+  if (v < low)
+    return low;
+  if (v > high)
+    return high;
+  return v;
+}
+
+nsPoint
+nsGfxScrollFrameInner::ClampAndRestrictToDevPixels(const nsPoint& aPt,
+                                                   nsIntPoint* aPtDevPx) const
+{
+  nsPresContext* presContext = mOuter->PresContext();
+  nscoord appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
+  // Convert to device pixels so we scroll to an integer offset of device
+  // pixels. But we also need to make sure that our position remains
+  // inside the allowed region.
+  nsRect scrollRange = GetScrollRange();
+  *aPtDevPx = nsIntPoint(ClampInt(scrollRange.x, aPt.x, scrollRange.XMost(), appUnitsPerDevPixel),
+                         ClampInt(scrollRange.y, aPt.y, scrollRange.YMost(), appUnitsPerDevPixel));
+  return nsPoint(NSIntPixelsToAppUnits(aPtDevPx->x, appUnitsPerDevPixel),
+                 NSIntPixelsToAppUnits(aPtDevPx->y, appUnitsPerDevPixel));
+}
+
+void
+nsGfxScrollFrameInner::ScrollToImpl(nsPoint aPt)
+{
+  nsPresContext* presContext = mOuter->PresContext();
+  nscoord appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
+  nsIntPoint ptDevPx;
+  nsPoint pt = ClampAndRestrictToDevPixels(aPt, &ptDevPx);
+
+  nsPoint curPos = GetScrollPosition();
+  if (pt == curPos) {
+    return;
+  }
+  nsIntPoint curPosDevPx(NSAppUnitsToIntPixels(curPos.x, appUnitsPerDevPixel),
+                         NSAppUnitsToIntPixels(curPos.y, appUnitsPerDevPixel));
+  // We maintain the invariant that the scroll position is a multiple of device
+  // pixels.
+  NS_ASSERTION(curPosDevPx.x*appUnitsPerDevPixel == curPos.x,
+               "curPos.x not a multiple of device pixels");
+  NS_ASSERTION(curPosDevPx.y*appUnitsPerDevPixel == curPos.y,
+               "curPos.y not a multiple of device pixels");
+
+  // notify the listeners.
+  for (PRInt32 i = 0; i < mListeners.Count(); i++) {
+    mListeners[i]->ScrollPositionWillChange(pt.x, pt.y);
+  }
+  
+  // Update frame position for scrolling
+  mScrolledFrame->SetPosition(mScrollPort.TopLeft() - pt);
+
+  // We pass in the amount to move visually
+  ScrollVisual(curPosDevPx - ptDevPx);
+
+  presContext->PresShell()->GetViewManager()->SynthesizeMouseMove(PR_TRUE);
+  UpdateScrollbarPosition();
+  PostScrollEvent();
+
+  // notify the listeners.
+  for (PRInt32 i = 0; i < mListeners.Count(); i++) {
+    mListeners[i]->ScrollPositionDidChange(pt.x, pt.y);
+  }
+}
 
 nsresult
 nsGfxScrollFrameInner::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                         const nsRect&           aDirtyRect,
                                         const nsDisplayListSet& aLists)
 {
   nsresult rv = mOuter->DisplayBorderBackgroundOutline(aBuilder, aLists);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -1267,56 +1816,39 @@ nsGfxScrollFrameInner::BuildDisplayList(
       NS_ENSURE_SUCCESS(rv, rv);
     }
     kid = kid->GetNextSibling();
   }
 
   // Overflow clipping can never clip frames outside our subtree, so there
   // is no need to worry about whether we are a moving frame that might clip
   // non-moving frames.
-  nsRect frameClip = mScrollableView->View()->GetBounds();
   nsRect dirtyRect;
   // Not all our descendants will be clipped by overflow clipping, but all
   // the ones that aren't clipped will be out of flow frames that have already
   // had dirty rects saved for them by their parent frames calling
   // MarkOutOfFlowChildrenForDisplayList, so it's safe to restrict our
   // dirty rect here.
-  dirtyRect.IntersectRect(aDirtyRect, frameClip);
+  dirtyRect.IntersectRect(aDirtyRect, mScrollPort);
   
   nsDisplayListCollection set;
   rv = mOuter->BuildDisplayListForChild(aBuilder, mScrolledFrame, dirtyRect, set);
   NS_ENSURE_SUCCESS(rv, rv);
-  nsRect clip = frameClip + aBuilder->ToReferenceFrame(mOuter);
+  nsRect clip = mScrollPort + aBuilder->ToReferenceFrame(mOuter);
   // mScrolledFrame may have given us a background, e.g., the scrolled canvas
   // frame below the viewport. If so, we want it to be clipped. We also want
   // to end up on our BorderBackground list.
   // If we are the viewport scrollframe, then clip all our descendants (to ensure
   // that fixed-pos elements get clipped by us).
   rv = mOuter->OverflowClip(aBuilder, set, aLists, clip, PR_TRUE, mIsRoot);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
-void
-nsGfxScrollFrameInner::CreateScrollableView()
-{
-  nsIView* outerView = mOuter->GetView();
-  NS_ASSERTION(outerView, "scrollframes must have views");
-  nsIViewManager* viewManager = outerView->GetViewManager();
-  mScrollableView = viewManager->CreateScrollableView(mOuter->GetRect(), outerView);
-  if (!mScrollableView)
-    return;
-
-  nsIView* view = mScrollableView->View();
-
-  // Insert the view into the view hierarchy
-  viewManager->InsertChild(outerView, view, nsnull, PR_TRUE);
-}
-
 static void HandleScrollPref(nsIScrollable *aScrollable, PRInt32 aOrientation,
                              PRUint8& aValue)
 {
   PRInt32 pref;
   aScrollable->GetDefaultScrollbarPreferences(aOrientation, &pref);
   switch (pref) {
     case nsIScrollable::Scrollbar_Auto:
       // leave |aValue| untouched
@@ -1325,28 +1857,16 @@ static void HandleScrollPref(nsIScrollab
       aValue = NS_STYLE_OVERFLOW_HIDDEN;
       break;
     case nsIScrollable::Scrollbar_Always:
       aValue = NS_STYLE_OVERFLOW_SCROLL;
       break;
   }
 }
 
-nsIView*
-nsGfxScrollFrameInner::GetParentViewForChildFrame(nsIFrame* aFrame) const
-{
-  if (aFrame->GetContent() == mOuter->GetContent()) {
-    NS_ASSERTION(mScrollableView, "Scrollable view should have been created by now");
-    // scrolled frame, put it under our anonymous view
-    return mScrollableView->View();
-  }
-  // scrollbars and stuff; put them under our regular view
-  return mOuter->GetView();
-}
-
 nsGfxScrollFrameInner::ScrollbarStyles
 nsGfxScrollFrameInner::GetScrollbarStylesFromFrame() const
 {
   ScrollbarStyles result;
 
   nsPresContext* presContext = mOuter->PresContext();
   if (!presContext->IsDynamic() &&
       !(mIsRoot && presContext->HasPaginatedScrolling())) {
@@ -1382,172 +1902,166 @@ static nscoord
 AlignToDevPixelRoundingToZero(nscoord aVal, PRInt32 aAppUnitsPerDevPixel)
 {
   return (aVal/aAppUnitsPerDevPixel)*aAppUnitsPerDevPixel;
 }
 
 nsRect
 nsGfxScrollFrameInner::GetScrollRange() const
 {
-  nsSize scrollPortSize = GetScrollPortSize();
-  nsRect range = GetScrolledRect(scrollPortSize);
-  range.width -= scrollPortSize.width;
-  range.height -= scrollPortSize.height;
+  nsRect range = GetScrolledRect();
+  range.width -= mScrollPort.width;
+  range.height -= mScrollPort.height;
 
   nsPresContext* presContext = mOuter->PresContext();
   PRInt32 appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
   range.width =
     AlignToDevPixelRoundingToZero(range.XMost(), appUnitsPerDevPixel) - range.x;
   range.height =
     AlignToDevPixelRoundingToZero(range.YMost(), appUnitsPerDevPixel) - range.y;
   range.x = AlignToDevPixelRoundingToZero(range.x, appUnitsPerDevPixel);
   range.y = AlignToDevPixelRoundingToZero(range.y, appUnitsPerDevPixel);
   return range;
 }
 
-void
-nsGfxScrollFrameInner::ScrollTo(nsPoint aScrollPosition,
-                                nsIScrollableFrame::ScrollMode aMode)
-{
-  mScrollableView->ScrollTo(aScrollPosition.x, aScrollPosition.y,
-                            aMode == nsIScrollableFrame::SMOOTH ? NS_VMREFRESH_SMOOTHSCROLL : 0);
-}
-
 static void
 AdjustForWholeDelta(PRInt32 aDelta, nscoord* aCoord)
 {
   if (aDelta < 0) {
     *aCoord = nscoord_MIN;
   } else if (aDelta > 0) {
     *aCoord = nscoord_MAX;
   }
 }
 
 void
 nsGfxScrollFrameInner::ScrollBy(nsIntPoint aDelta,
                                 nsIScrollableFrame::ScrollUnit aUnit,
                                 nsIScrollableFrame::ScrollMode aMode,
                                 nsIntPoint* aOverflow)
 {
-  PRUint32 flags =
-    aMode == nsIScrollableFrame::SMOOTH ? NS_VMREFRESH_SMOOTHSCROLL : 0;
-  nsIntPoint overflow;
+  nsSize deltaMultiplier;
   switch (aUnit) {
   case nsIScrollableFrame::DEVICE_PIXELS: {
-    mScrollableView->ScrollByPixels(aDelta.x, aDelta.y,
-                                    overflow.x, overflow.y, flags);
+    nscoord appUnitsPerDevPixel =
+      mOuter->PresContext()->AppUnitsPerDevPixel();
+    deltaMultiplier = nsSize(appUnitsPerDevPixel, appUnitsPerDevPixel);
     break;
   }
   case nsIScrollableFrame::LINES: {
-    mScrollableView->ScrollByLinesWithOverflow(aDelta.x, aDelta.y,
-                                               overflow.x, overflow.y,
-                                               flags);
+    deltaMultiplier = GetLineScrollAmount();
     break;
   }
   case nsIScrollableFrame::PAGES: {
-    mScrollableView->ScrollByPages(aDelta.x, aDelta.y, flags);
+    deltaMultiplier = GetPageScrollAmount();
     break;
   }
   case nsIScrollableFrame::WHOLE: {
     nsPoint pos = GetScrollPosition();
     AdjustForWholeDelta(aDelta.x, &pos.x);
     AdjustForWholeDelta(aDelta.y, &pos.y);
     ScrollTo(pos, aMode);
-    break;
+    if (aOverflow) {
+      *aOverflow = nsIntPoint(0, 0);
+    }
+    return;
   }
+  default:
+    NS_ERROR("Invalid scroll mode");
+    return;
   }
 
+  nsPoint newPos = mDestination +
+    nsPoint(aDelta.x*deltaMultiplier.width, aDelta.y*deltaMultiplier.height);
+  ScrollTo(newPos, aMode);
+
   if (aOverflow) {
-    *aOverflow = overflow;
+    nsPoint clampAmount = mDestination - newPos;
+    float appUnitsPerDevPixel = mOuter->PresContext()->AppUnitsPerDevPixel();
+    *aOverflow = nsIntPoint(
+        NSAppUnitsToIntPixels(PR_ABS(clampAmount.x), appUnitsPerDevPixel),
+        NSAppUnitsToIntPixels(PR_ABS(clampAmount.y), appUnitsPerDevPixel));
   }
 }
 
 nsSize
 nsGfxScrollFrameInner::GetLineScrollAmount() const
 {
-  nscoord lineHeight;
-  mScrollableView->GetLineHeight(&lineHeight);
-  return nsSize(lineHeight, lineHeight);
+  const nsStyleFont* font = mOuter->GetStyleFont();
+  const nsFont& f = font->mFont;
+  nsCOMPtr<nsIFontMetrics> fm = mOuter->PresContext()->GetMetricsFor(f);
+  NS_ASSERTION(fm, "FontMetrics is null, assuming fontHeight == 1 appunit");
+  nscoord fontHeight = 1;
+  if (fm) {
+    fm->GetHeight(fontHeight);
+  }
+
+  return nsSize(fontHeight, fontHeight);
 }
 
 nsSize
 nsGfxScrollFrameInner::GetPageScrollAmount() const
 {
-  nsSize distances;
-  mScrollableView->GetPageScrollDistances(&distances);
-  return nsSize(distances.width, distances.height);
+  nsSize lineScrollAmount = GetLineScrollAmount();
+  // The page increment is the size of the page, minus the smaller of
+  // 10% of the size or 2 lines.
+  return nsSize(
+    mScrollPort.width - NS_MIN(mScrollPort.width/10, 2*lineScrollAmount.width),
+    mScrollPort.height - NS_MIN(mScrollPort.height/10, 2*lineScrollAmount.height));
 }
 
   /**
    * this code is resposible for restoring the scroll position back to some
    * saved position. if the user has not moved the scroll position manually
    * we keep scrolling down until we get to our original position. keep in
    * mind that content could incrementally be coming in. we only want to stop
    * when we reach our new position.
    */
 void
 nsGfxScrollFrameInner::ScrollToRestoredPosition()
 {
-  nsIScrollableView* scrollingView = GetScrollableView();
-  if (!scrollingView) {
-    return;
-  }
-  if (mRestoreRect.y == -1 || mLastPos.x == -1 || mLastPos.y == -1) {
+  if (mRestorePos.y == -1 || mLastPos.x == -1 || mLastPos.y == -1) {
     return;
   }
   // make sure our scroll position did not change for where we last put
   // it. if it does then the user must have moved it, and we no longer
   // need to restore.
-  nscoord x = 0;
-  nscoord y = 0;
-  scrollingView->GetScrollPosition(x, y);
+  nsPoint scrollPos = GetScrollPosition();
 
   // if we didn't move, we still need to restore
-  if (x == mLastPos.x && y == mLastPos.y) {
-    nsRect childRect(0, 0, 0, 0);
-    nsIView* child = nsnull;
-    nsresult rv = scrollingView->GetScrolledView(child);
-    if (NS_SUCCEEDED(rv) && child)
-      childRect = child->GetBounds();
-
-    PRInt32 cx, cy, x, y;
-    scrollingView->GetScrollPosition(cx,cy);
-
-    x = (int)mRestoreRect.x;
-    y = (int)mRestoreRect.y;
-
-    // if our position is greater than the scroll position, scroll.
+  if (scrollPos == mLastPos) {
+    // if our desired position is different to the scroll position, scroll.
     // remember that we could be incrementally loading so we may enter
     // and scroll many times.
-    if (y != cy || x != cx) {
-      scrollingView->ScrollTo(x, y, 0);
-      // scrollpostion goes from twips to pixels. this fixes any roundoff
-      // problems.
-      scrollingView->GetScrollPosition(mLastPos.x, mLastPos.y);
+    if (mRestorePos != scrollPos) {
+      ScrollTo(mRestorePos, nsIScrollableFrame::INSTANT);
+      // Re-get the scroll position, it might not be exactly equal to
+      // mRestorePos due to rounding and clamping.
+      mLastPos = GetScrollPosition();
     } else {
       // if we reached the position then stop
-      mRestoreRect.y = -1;
+      mRestorePos.y = -1;
       mLastPos.x = -1;
       mLastPos.y = -1;
     }
   } else {
     // user moved the position, so we won't need to restore
     mLastPos.x = -1;
     mLastPos.y = -1;
   }
 }
 
 nsresult
 nsGfxScrollFrameInner::FireScrollPortEvent()
 {
   mAsyncScrollPortEvent.Forget();
 
   // Keep this in sync with PostOverflowEvent().
-  nsSize scrollportSize = GetScrollPortSize();
-  nsSize childSize = GetScrolledRect(scrollportSize).Size();
+  nsSize scrollportSize = mScrollPort.Size();
+  nsSize childSize = GetScrolledRect().Size();
 
   PRBool newVerticalOverflow = childSize.height > scrollportSize.height;
   PRBool vertChanged = mVerticalOverflow != newVerticalOverflow;
 
   PRBool newHorizontalOverflow = childSize.width > scrollportSize.width;
   PRBool horizChanged = mHorizontalOverflow != newHorizontalOverflow;
 
   if (!vertChanged && !horizChanged) {
@@ -1730,155 +2244,80 @@ nsGfxScrollFrameInner::Destroy()
 
   mScrollEvent.Revoke();
   mAsyncScrollPortEvent.Revoke();
   if (mPostedReflowCallback) {
     mOuter->PresContext()->PresShell()->CancelReflowCallback(this);
     mPostedReflowCallback = PR_FALSE;
   }
   mScrolledAreaEventDispatcher.Revoke();
-  nsIScrollableView *view = GetScrollableView();
-  NS_ASSERTION(view, "unexpected null pointer");
-  if (view)
-    view->RemoveScrollPositionListener(this);
-}
-
-NS_IMETHODIMP
-nsGfxScrollFrameInner::ScrollPositionWillChange(nscoord aX, nscoord aY)
-{
-   // Do nothing.
-   return NS_OK;
 }
 
 /**
  * Called when we want to update the scrollbar position, either because scrolling happened
  * or the user moved the scrollbar position and we need to undo that (e.g., when the user
  * clicks to scroll and we're using smooth scrolling, so we need to put the thumb back
  * to its initial position for the start of the smooth sequence).
  */
 void
-nsGfxScrollFrameInner::InternalScrollPositionDidChange(nscoord aX, nscoord aY)
-{
-  if (mVScrollbarBox)
-    SetCoordAttribute(mVScrollbarBox->GetContent(), nsGkAtoms::curpos,
-                      aY - GetScrolledRect(GetScrollPortSize()).y);
-  
-  if (mHScrollbarBox)
-    SetCoordAttribute(mHScrollbarBox->GetContent(), nsGkAtoms::curpos,
-                      aX - GetScrolledRect(GetScrollPortSize()).x);
-}
-
-void
-nsGfxScrollFrameInner::ViewPositionDidChange(nsTArray<nsIWidget::Configuration>* aConfigurations)
+nsGfxScrollFrameInner::UpdateScrollbarPosition()
 {
-  // Update frame position to match view offsets
-  nsPoint childOffset = mScrolledFrame->GetView()->GetOffsetTo(mOuter->GetView());
-  mScrolledFrame->SetPosition(childOffset);
-
-  nsRootPresContext* rootPresContext = mOuter->PresContext()->RootPresContext();
-  // Only update plugin geometry if we're scrolling in the root widget.
-  // In particular if we're scrolling inside a popup widget, we don't
-  // want to update plugins since they don't belong to this widget (we
-  // don't display windowed plugins in popups).
-  if (mOuter->GetWindow() ==
-      rootPresContext->FrameManager()->GetRootFrame()->GetWindow()) {
-    rootPresContext->GetPluginGeometryUpdates(mOuter, aConfigurations);
+  mFrameIsUpdatingScrollbar = PR_TRUE;
+
+  nsPoint pt = GetScrollPosition();
+  if (mVScrollbarBox) {
+    SetCoordAttribute(mVScrollbarBox->GetContent(), nsGkAtoms::curpos,
+                      pt.y - GetScrolledRect().y);
   }
-}
-
-/**
- * Called whenever actual scrolling happens for any reason.
- */
-NS_IMETHODIMP
-nsGfxScrollFrameInner::ScrollPositionDidChange(nscoord aX, nscoord aY)
-{
-  NS_ASSERTION(!mViewInitiatedScroll, "Cannot reenter ScrollPositionDidChange");
-
-  mViewInitiatedScroll = PR_TRUE;
-  InternalScrollPositionDidChange(aX, aY);
-  mViewInitiatedScroll = PR_FALSE;
-
-  PostScrollEvent();
-
-  return NS_OK;
+  if (mHScrollbarBox) {
+    SetCoordAttribute(mHScrollbarBox->GetContent(), nsGkAtoms::curpos,
+                      pt.x - GetScrolledRect().x);
+  }
+
+  mFrameIsUpdatingScrollbar = PR_FALSE;
 }
 
 void nsGfxScrollFrameInner::CurPosAttributeChanged(nsIContent* aContent)
 {
   NS_ASSERTION(aContent, "aContent must not be null");
   NS_ASSERTION((mHScrollbarBox && mHScrollbarBox->GetContent() == aContent) ||
                (mVScrollbarBox && mVScrollbarBox->GetContent() == aContent),
                "unexpected child");
 
   // Attribute changes on the scrollbars happen in one of three ways:
   // 1) The scrollbar changed the attribute in response to some user event
   // 2) We changed the attribute in response to a ScrollPositionDidChange
   // callback from the scrolling view
   // 3) We changed the attribute to adjust the scrollbars for the start
   // of a smooth scroll operation
   //
-  // In case 2), we don't need to scroll the view because the scrolling
-  // has already happened. In case 3) we don't need to scroll because
-  // we're just adjusting the scrollbars back to the correct setting
-  // for the view.
-  //
-  // Cases 1) and 3) do not indicate that actual scrolling has happened. Only
-  // case 2) indicates actual scrolling. Therefore we do not fire onscroll
-  // here, but in ScrollPositionDidChange.
-  // 
-  // We used to detect this case implicitly because we'd compare the
-  // scrollbar attributes with the view's current scroll position and
-  // bail out if they were equal. But that approach is fragile; it can
-  // fail when, for example, the view scrolls horizontally and
-  // vertically simultaneously; we'll get here when only the vertical
-  // attribute has been set, so the attributes and the view scroll
-  // position don't yet agree, and we'd tell the view to scroll to the
-  // new vertical position and the old horizontal position! Even worse
-  // things could happen when smooth scrolling got involved ... crashes
-  // and other terrors.
-  if (mViewInitiatedScroll || mFrameInitiatedScroll) return;
-
-  nsRect scrolledRect = GetScrolledRect(GetScrollPortSize());
+  // In cases 2 and 3 we do not need to scroll because we're just
+  // updating our scrollbar.
+  if (mFrameIsUpdatingScrollbar)
+    return;
+
+  nsRect scrolledRect = GetScrolledRect();
 
   nscoord x = GetCoordAttribute(mHScrollbarBox, nsGkAtoms::curpos,
                                 -scrolledRect.x) +
               scrolledRect.x;
   nscoord y = GetCoordAttribute(mVScrollbarBox, nsGkAtoms::curpos,
                                 -scrolledRect.y) +
               scrolledRect.y;
 
-  // Make sure the scrollbars indeed moved before firing the event.
-  // I think it is OK to prevent the call to ScrollbarChanged()
-  // if we didn't actually move. The following check is the first
-  // thing ScrollbarChanged() does anyway, before deciding to move 
-  // the scrollbars. 
-  nscoord curPosX=0, curPosY=0;
-  nsIScrollableView* s = GetScrollableView();
-  if (s) {
-    s->GetScrollPosition(curPosX, curPosY);
-    if (x == curPosX && y == curPosY)
-      return;
-
-    PRBool isSmooth = aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::smooth);
-        
-    if (isSmooth) {
-      // Make sure an attribute-setting callback occurs even if the view
-      // didn't actually move yet.  We need to make sure other listeners
-      // see that the scroll position is not (yet) what they thought it
-      // was.
-
-      NS_ASSERTION(!mFrameInitiatedScroll, "Unexpected reentry");
-      // Make sure we don't do anything in when the view calls us back
-      // for this scroll operation.
-      mFrameInitiatedScroll = PR_TRUE;
-      InternalScrollPositionDidChange(curPosX, curPosY);
-      mFrameInitiatedScroll = PR_FALSE;
-    }
-    ScrollbarChanged(mOuter->PresContext(), x, y, isSmooth ? NS_VMREFRESH_SMOOTHSCROLL : 0);
+  PRBool isSmooth = aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::smooth);
+  if (isSmooth) {
+    // Make sure an attribute-setting callback occurs even if the view
+    // didn't actually move yet.  We need to make sure other listeners
+    // see that the scroll position is not (yet) what they thought it
+    // was.
+    UpdateScrollbarPosition();
   }
+  ScrollTo(nsPoint(x, y),
+           isSmooth ? nsIScrollableFrame::SMOOTH : nsIScrollableFrame::INSTANT);
 }
 
 /* ============= Scroll events ========== */
 
 NS_IMETHODIMP
 nsGfxScrollFrameInner::ScrollEvent::Run()
 {
   if (mInner)
@@ -1930,90 +2369,92 @@ nsGfxScrollFrameInner::AsyncScrollPortEv
   if (mInner) {
     mInner->mOuter->PresContext()->GetPresShell()->
       FlushPendingNotifications(Flush_InterruptibleLayout);
   }
   return mInner ? mInner->FireScrollPortEvent() : NS_OK;
 }
 
 PRBool
-nsXULScrollFrame::AddHorizontalScrollbar(nsBoxLayoutState& aState,
-                                         nsRect& aScrollAreaSize, PRBool aOnTop)
+nsXULScrollFrame::AddHorizontalScrollbar(nsBoxLayoutState& aState, PRBool aOnTop)
 {
   if (!mInner.mHScrollbarBox)
     return PR_TRUE;
 
-  return AddRemoveScrollbar(aState, aScrollAreaSize, aOnTop, PR_TRUE, PR_TRUE);
+  return AddRemoveScrollbar(aState, aOnTop, PR_TRUE, PR_TRUE);
 }
 
 PRBool
-nsXULScrollFrame::AddVerticalScrollbar(nsBoxLayoutState& aState,
-                                       nsRect& aScrollAreaSize, PRBool aOnRight)
+nsXULScrollFrame::AddVerticalScrollbar(nsBoxLayoutState& aState, PRBool aOnRight)
 {
   if (!mInner.mVScrollbarBox)
     return PR_TRUE;
 
-  return AddRemoveScrollbar(aState, aScrollAreaSize, aOnRight, PR_FALSE, PR_TRUE);
+  return AddRemoveScrollbar(aState, aOnRight, PR_FALSE, PR_TRUE);
 }
 
 void
-nsXULScrollFrame::RemoveHorizontalScrollbar(nsBoxLayoutState& aState,
-                                            nsRect& aScrollAreaSize, PRBool aOnTop)
+nsXULScrollFrame::RemoveHorizontalScrollbar(nsBoxLayoutState& aState, PRBool aOnTop)
 {
   // removing a scrollbar should always fit
 #ifdef DEBUG
   PRBool result =
 #endif
-  AddRemoveScrollbar(aState, aScrollAreaSize, aOnTop, PR_TRUE, PR_FALSE);
+  AddRemoveScrollbar(aState, aOnTop, PR_TRUE, PR_FALSE);
   NS_ASSERTION(result, "Removing horizontal scrollbar failed to fit??");
 }
 
 void
-nsXULScrollFrame::RemoveVerticalScrollbar(nsBoxLayoutState& aState,
-                                          nsRect& aScrollAreaSize, PRBool aOnRight)
+nsXULScrollFrame::RemoveVerticalScrollbar(nsBoxLayoutState& aState, PRBool aOnRight)
 {
   // removing a scrollbar should always fit
 #ifdef DEBUG
   PRBool result =
 #endif
-  AddRemoveScrollbar(aState, aScrollAreaSize, aOnRight, PR_FALSE, PR_FALSE);
+  AddRemoveScrollbar(aState, aOnRight, PR_FALSE, PR_FALSE);
   NS_ASSERTION(result, "Removing vertical scrollbar failed to fit??");
 }
 
 PRBool
-nsXULScrollFrame::AddRemoveScrollbar(nsBoxLayoutState& aState, nsRect& aScrollAreaSize,
+nsXULScrollFrame::AddRemoveScrollbar(nsBoxLayoutState& aState,
                                      PRBool aOnTop, PRBool aHorizontal, PRBool aAdd)
 {
   if (aHorizontal) {
      if (mInner.mNeverHasHorizontalScrollbar || !mInner.mHScrollbarBox)
        return PR_FALSE;
 
      nsSize hSize = mInner.mHScrollbarBox->GetPrefSize(aState);
      nsBox::AddMargin(mInner.mHScrollbarBox, hSize);
 
      mInner.SetScrollbarVisibility(mInner.mHScrollbarBox, aAdd);
 
      PRBool hasHorizontalScrollbar;
-     PRBool fit = AddRemoveScrollbar(hasHorizontalScrollbar, aScrollAreaSize.y, aScrollAreaSize.height, hSize.height, aOnTop, aAdd);
+     PRBool fit = AddRemoveScrollbar(hasHorizontalScrollbar,
+                                     mInner.mScrollPort.y,
+                                     mInner.mScrollPort.height,
+                                     hSize.height, aOnTop, aAdd);
      mInner.mHasHorizontalScrollbar = hasHorizontalScrollbar;    // because mHasHorizontalScrollbar is a PRPackedBool
      if (!fit)
         mInner.SetScrollbarVisibility(mInner.mHScrollbarBox, !aAdd);
 
      return fit;
   } else {
      if (mInner.mNeverHasVerticalScrollbar || !mInner.mVScrollbarBox)
        return PR_FALSE;
 
      nsSize vSize = mInner.mVScrollbarBox->GetPrefSize(aState);
      nsBox::AddMargin(mInner.mVScrollbarBox, vSize);
 
      mInner.SetScrollbarVisibility(mInner.mVScrollbarBox, aAdd);
 
      PRBool hasVerticalScrollbar;
-     PRBool fit = AddRemoveScrollbar(hasVerticalScrollbar, aScrollAreaSize.x, aScrollAreaSize.width, vSize.width, aOnTop, aAdd);
+     PRBool fit = AddRemoveScrollbar(hasVerticalScrollbar,
+                                     mInner.mScrollPort.x,
+                                     mInner.mScrollPort.width,
+                                     vSize.width, aOnTop, aAdd);
      mInner.mHasVerticalScrollbar = hasVerticalScrollbar;    // because mHasVerticalScrollbar is a PRPackedBool
      if (!fit)
         mInner.SetScrollbarVisibility(mInner.mVScrollbarBox, !aAdd);
 
      return fit;
   }
 }
 
@@ -2045,48 +2486,43 @@ nsXULScrollFrame::AddRemoveScrollbar(PRB
        return PR_TRUE;
    }
 
    aHasScrollbar = PR_FALSE;
    return PR_FALSE;
 }
 
 void
-nsXULScrollFrame::LayoutScrollArea(nsBoxLayoutState& aState, const nsRect& aRect)
+nsXULScrollFrame::LayoutScrollArea(nsBoxLayoutState& aState,
+                                   const nsPoint& aScrollPosition)
 {
-  nsIView* scrollView = mInner.mScrollableView->View();
-  nsIViewManager* vm = scrollView->GetViewManager();
-  vm->MoveViewTo(scrollView, aRect.x, aRect.y);
-  vm->ResizeView(scrollView, nsRect(nsPoint(0, 0), aRect.Size()), PR_TRUE);
-
   PRUint32 oldflags = aState.LayoutFlags();
-  nsPoint childOffset =
-    mInner.mScrolledFrame->GetView()->GetOffsetTo(GetView());
-  nsRect childRect = nsRect(childOffset, aRect.Size());
-
+  nsRect childRect = nsRect(mInner.mScrollPort.TopLeft() - aScrollPosition,
+                            mInner.mScrollPort.Size());
   PRInt32 flags = NS_FRAME_NO_MOVE_VIEW;
 
   nsSize minSize = mInner.mScrolledFrame->GetMinSize(aState);
   
   if (minSize.height > childRect.height)
     childRect.height = minSize.height;
   
   if (minSize.width > childRect.width)
     childRect.width = minSize.width;
 
   aState.SetLayoutFlags(flags);
   mInner.mScrolledFrame->SetBounds(aState, childRect);
   mInner.mScrolledFrame->Layout(aState);
 
   childRect = mInner.mScrolledFrame->GetRect();
 
-  if (childRect.width < aRect.width || childRect.height < aRect.height)
+  if (childRect.width < mInner.mScrollPort.width ||
+      childRect.height < mInner.mScrollPort.height)
   {
-    childRect.width = NS_MAX(childRect.width, aRect.width);
-    childRect.height = NS_MAX(childRect.height, aRect.height);
+    childRect.width = NS_MAX(childRect.width, mInner.mScrollPort.width);
+    childRect.height = NS_MAX(childRect.height, mInner.mScrollPort.height);
 
     // remove overflow area when we update the bounds,
     // because we've already accounted for it
     mInner.mScrolledFrame->SetBounds(aState, childRect);
     mInner.mScrolledFrame->ClearOverflowRect();
   }
 
   aState.SetLayoutFlags(oldflags);
@@ -2094,18 +2530,18 @@ nsXULScrollFrame::LayoutScrollArea(nsBox
 }
 
 void nsGfxScrollFrameInner::PostOverflowEvent()
 {
   if (mAsyncScrollPortEvent.IsPending())
     return;
 
   // Keep this in sync with FireScrollPortEvent().
-  nsSize scrollportSize = GetScrollPortSize();
-  nsSize childSize = GetScrolledRect(scrollportSize).Size();
+  nsSize scrollportSize = mScrollPort.Size();
+  nsSize childSize = GetScrolledRect().Size();
 
   PRBool newVerticalOverflow = childSize.height > scrollportSize.height;
   PRBool vertChanged = mVerticalOverflow != newVerticalOverflow;
 
   PRBool newHorizontalOverflow = childSize.width > scrollportSize.width;
   PRBool horizChanged = mHorizontalOverflow != newHorizontalOverflow;
 
   if (!vertChanged && !horizChanged) {
@@ -2175,18 +2611,21 @@ nsXULScrollFrame::Layout(nsBoxLayoutStat
 {
   PRBool scrollbarRight = mInner.IsScrollbarOnRight();
   PRBool scrollbarBottom = PR_TRUE;
 
   // get the content rect
   nsRect clientRect(0,0,0,0);
   GetClientRect(clientRect);
 
+  nsRect oldScrollAreaBounds = mInner.mScrollPort;
+  nsPoint oldScrollPosition = mInner.GetScrollPosition();
+
   // the scroll area size starts off as big as our content area
-  nsRect scrollAreaRect(clientRect);
+  mInner.mScrollPort = clientRect;
 
   /**************
    Our basic strategy here is to first try laying out the content with
    the scrollbars in their current state. We're hoping that that will
    just "work"; the content will overflow wherever there's a scrollbar
    already visible. If that does work, then there's no need to lay out
    the scrollarea. Otherwise we fix up the scrollbars; first we add a
    vertical one to scroll the content if necessary, or remove it if
@@ -2212,101 +2651,99 @@ nsXULScrollFrame::Layout(nsBoxLayoutStat
 
   // Look at our style do we always have vertical or horizontal scrollbars?
   if (styles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL)
      mInner.mHasHorizontalScrollbar = PR_TRUE;
   if (styles.mVertical == NS_STYLE_OVERFLOW_SCROLL)
      mInner.mHasVerticalScrollbar = PR_TRUE;
 
   if (mInner.mHasHorizontalScrollbar)
-     AddHorizontalScrollbar(aState, scrollAreaRect, scrollbarBottom);
+     AddHorizontalScrollbar(aState, scrollbarBottom);
 
   if (mInner.mHasVerticalScrollbar)
-     AddVerticalScrollbar(aState, scrollAreaRect, scrollbarRight);
+     AddVerticalScrollbar(aState, scrollbarRight);
      
-  nsRect oldScrollAreaBounds = mInner.mScrollableView->View()->GetBounds();
-
   // layout our the scroll area
-  LayoutScrollArea(aState, scrollAreaRect);
+  LayoutScrollArea(aState, oldScrollPosition);
   
   // now look at the content area and see if we need scrollbars or not
   PRBool needsLayout = PR_FALSE;
 
   // if we have 'auto' scrollbars look at the vertical case
   if (styles.mVertical != NS_STYLE_OVERFLOW_SCROLL) {
     // These are only good until the call to LayoutScrollArea.
-    nsRect scrolledRect = mInner.GetScrolledRect(scrollAreaRect.Size());
+    nsRect scrolledRect = mInner.GetScrolledRect();
     nsSize scrolledContentSize(scrolledRect.XMost(), scrolledRect.YMost());
 
     // There are two cases to consider
-      if (scrolledContentSize.height <= scrollAreaRect.height
+      if (scrolledContentSize.height <= mInner.mScrollPort.height
           || styles.mVertical != NS_STYLE_OVERFLOW_AUTO) {
         if (mInner.mHasVerticalScrollbar) {
           // We left room for the vertical scrollbar, but it's not needed;
           // remove it.
-          RemoveVerticalScrollbar(aState, scrollAreaRect, scrollbarRight);
+          RemoveVerticalScrollbar(aState, scrollbarRight);
           needsLayout = PR_TRUE;
         }
       } else {
         if (!mInner.mHasVerticalScrollbar) {
           // We didn't leave room for the vertical scrollbar, but it turns
           // out we needed it
-          if (AddVerticalScrollbar(aState, scrollAreaRect, scrollbarRight))
+          if (AddVerticalScrollbar(aState, scrollbarRight))
             needsLayout = PR_TRUE;
         }
     }
 
     // ok layout at the right size
     if (needsLayout) {
        nsBoxLayoutState resizeState(aState);
-       LayoutScrollArea(resizeState, scrollAreaRect);
+       LayoutScrollArea(resizeState, oldScrollPosition);
        needsLayout = PR_FALSE;
     }
   }
 
 
   // if scrollbars are auto look at the horizontal case
   if (styles.mHorizontal != NS_STYLE_OVERFLOW_SCROLL)
   {
     // These are only good until the call to LayoutScrollArea.
-    nsRect scrolledRect = mInner.GetScrolledRect(scrollAreaRect.Size());
+    nsRect scrolledRect = mInner.GetScrolledRect();
     nsSize scrolledContentSize(scrolledRect.XMost(), scrolledRect.YMost());
 
     // if the child is wider that the scroll area
     // and we don't have a scrollbar add one.
-    if (scrolledContentSize.width > scrollAreaRect.width
+    if (scrolledContentSize.width > mInner.mScrollPort.width
         && styles.mHorizontal == NS_STYLE_OVERFLOW_AUTO) {
 
       if (!mInner.mHasHorizontalScrollbar) {
            // no scrollbar? 
-          if (AddHorizontalScrollbar(aState, scrollAreaRect, scrollbarBottom))
+          if (AddHorizontalScrollbar(aState, scrollbarBottom))
              needsLayout = PR_TRUE;
 
            // if we added a horizontal scrollbar and we did not have a vertical
            // there is a chance that by adding the horizontal scrollbar we will
            // suddenly need a vertical scrollbar. Is a special case but its 
            // important.
            //if (!mHasVerticalScrollbar && scrolledContentSize.height > scrollAreaRect.height - sbSize.height)
            //  printf("****Gfx Scrollbar Special case hit!!*****\n");
            
       }
     } else {
         // if the area is smaller or equal to and we have a scrollbar then
         // remove it.
       if (mInner.mHasHorizontalScrollbar) {
-        RemoveHorizontalScrollbar(aState, scrollAreaRect, scrollbarBottom);
+        RemoveHorizontalScrollbar(aState, scrollbarBottom);
         needsLayout = PR_TRUE;
       }
     }
   }
 
   // we only need to set the rect. The inner child stays the same size.
   if (needsLayout) {
      nsBoxLayoutState resizeState(aState);
-     LayoutScrollArea(resizeState, scrollAreaRect);
+     LayoutScrollArea(resizeState, oldScrollPosition);
      needsLayout = PR_FALSE;
   }
     
   // get the preferred size of the scrollbars
   nsSize hMinSize(0, 0);
   if (mInner.mHScrollbarBox && mInner.mHasHorizontalScrollbar) {
     GetScrollbarMetrics(aState, mInner.mHScrollbarBox, &hMinSize, nsnull, PR_FALSE);
   }
@@ -2318,35 +2755,35 @@ nsXULScrollFrame::Layout(nsBoxLayoutStat
   // Disable scrollbars that are too small
   // Disable horizontal scrollbar first. If we have to disable only one
   // scrollbar, we'd rather keep the vertical scrollbar.
   // Note that we always give horizontal scrollbars their preferred height,
   // never their min-height. So check that there's room for the preferred height.
   if (mInner.mHasHorizontalScrollbar &&
       (hMinSize.width > clientRect.width - vMinSize.width
        || hMinSize.height > clientRect.height)) {
-    RemoveHorizontalScrollbar(aState, scrollAreaRect, scrollbarBottom);
+    RemoveHorizontalScrollbar(aState, scrollbarBottom);
     needsLayout = PR_TRUE;
   }
   // Now disable vertical scrollbar if necessary
   if (mInner.mHasVerticalScrollbar &&
       (vMinSize.height > clientRect.height - hMinSize.height
        || vMinSize.width > clientRect.width)) {
-    RemoveVerticalScrollbar(aState, scrollAreaRect, scrollbarRight);
+    RemoveVerticalScrollbar(aState, scrollbarRight);
     needsLayout = PR_TRUE;
   }
 
   // we only need to set the rect. The inner child stays the same size.
   if (needsLayout) {
     nsBoxLayoutState resizeState(aState);
-    LayoutScrollArea(resizeState, scrollAreaRect);
+    LayoutScrollArea(resizeState, oldScrollPosition);
   }
 
   if (!mInner.mSupppressScrollbarUpdate) { 
-    mInner.LayoutScrollbars(aState, clientRect, oldScrollAreaBounds, scrollAreaRect);
+    mInner.LayoutScrollbars(aState, clientRect, oldScrollAreaBounds);
   }
   if (!mInner.mPostedReflowCallback) {
     // Make sure we'll try scrolling to restored position
     PresContext()->PresShell()->PostReflowCallback(&mInner);
     mInner.mPostedReflowCallback = PR_TRUE;
   }
   if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
     mInner.mHadNonInitialReflow = PR_TRUE;
@@ -2394,78 +2831,67 @@ nsGfxScrollFrameInner::ReflowFinished()
          fixedChild; fixedChild = fixedChild->GetNextSibling()) {
       // force a reflow of the fixed child
       presContext->PresShell()->
         FrameNeedsReflow(fixedChild, nsIPresShell::eResize,
                          NS_FRAME_HAS_DIRTY_CHILDREN);
     }
   }
 
-  nsIScrollableView* scrollable = GetScrollableView();
-  nsRect scrollArea = scrollable->View()->GetBounds();
-
-  const nsStyleFont* font = mOuter->GetStyleFont();
-  const nsFont& f = font->mFont;
-  nsCOMPtr<nsIFontMetrics> fm = presContext->GetMetricsFor(f);
-  nscoord fontHeight = 1;
-  NS_ASSERTION(fm,"FontMetrics is null assuming fontHeight == 1");
-  if (fm)
-    fm->GetHeight(fontHeight);
-  scrollable->SetLineHeight(fontHeight);
-
-  nsRect scrolledContentRect = GetScrolledRect(scrollArea.Size());
+  nsRect scrolledContentRect = GetScrolledRect();
   nscoord minX = scrolledContentRect.x;
-  nscoord maxX = scrolledContentRect.XMost() - scrollArea.width;
+  nscoord maxX = scrolledContentRect.XMost() - mScrollPort.width;
   nscoord minY = scrolledContentRect.y;
-  nscoord maxY = scrolledContentRect.YMost() - scrollArea.height;
+  nscoord maxY = scrolledContentRect.YMost() - mScrollPort.height;
 
   // Suppress handling of the curpos attribute changes we make here.
-  NS_ASSERTION(!mFrameInitiatedScroll, "We shouldn't be reentering here");
-  mFrameInitiatedScroll = PR_TRUE;
+  NS_ASSERTION(!mFrameIsUpdatingScrollbar, "We shouldn't be reentering here");
+  mFrameIsUpdatingScrollbar = PR_TRUE;
 
   nsCOMPtr<nsIContent> vScroll =
     mVScrollbarBox ? mVScrollbarBox->GetContent() : nsnull;
   nsCOMPtr<nsIContent> hScroll =
     mHScrollbarBox ? mHScrollbarBox->GetContent() : nsnull;
 
   // Note, in some cases mOuter may get deleted while finishing reflow
   // for scrollbars.
   if (vScroll || hScroll) {
     nsWeakFrame weakFrame(mOuter);
-    nscoord curPosX, curPosY;
-    scrollable->GetScrollPosition(curPosX, curPosY);
+    nsPoint scrollPos = GetScrollPosition();
+    // XXX shouldn't we use GetPageScrollAmount/GetLineScrollAmount here?
     if (vScroll) {
+      nscoord fontHeight = GetLineScrollAmount().height;
       // We normally use (scrollArea.height - fontHeight) for height
       // of page scrolling.  However, it is too small when
       // fontHeight is very large. (If fontHeight is larger than
       // scrollArea.height, direction of scrolling will be opposite).
       // To avoid it, we use (float(scrollArea.height) * 0.8) as
       // lower bound value of height of page scrolling. (bug 383267)
-      nscoord pageincrement = nscoord(scrollArea.height - fontHeight);
-      nscoord pageincrementMin = nscoord(float(scrollArea.height) * 0.8);
-      FinishReflowForScrollbar(vScroll, minY, maxY, curPosY,
+      nscoord pageincrement = nscoord(mScrollPort.height - fontHeight);
+      nscoord pageincrementMin = nscoord(float(mScrollPort.height) * 0.8);
+      FinishReflowForScrollbar(vScroll, minY, maxY, scrollPos.y,
                                NS_MAX(pageincrement,pageincrementMin),
                                fontHeight);
     }
     if (hScroll) {
-      FinishReflowForScrollbar(hScroll, minX, maxX, curPosX,
-                               nscoord(float(scrollArea.width) * 0.8),
+      FinishReflowForScrollbar(hScroll, minX, maxX, scrollPos.x,
+                               nscoord(float(mScrollPort.width) * 0.8),
                                nsPresContext::CSSPixelsToAppUnits(10));
     }
     NS_ENSURE_TRUE(weakFrame.IsAlive(), PR_FALSE);
   }
 
-  mFrameInitiatedScroll = PR_FALSE;
+  mFrameIsUpdatingScrollbar = PR_FALSE;
   // We used to rely on the curpos attribute changes above to scroll the
   // view.  However, for scrolling to the left of the viewport, we
   // rescale the curpos attribute, which means that operations like
   // resizing the window while it is scrolled all the way to the left
   // hold the curpos attribute constant at 0 while still requiring
   // scrolling.  So we suppress the effect of the changes above with
-  // mFrameInitiatedScroll and call CurPosAttributeChanged here.
+  // mFrameIsUpdatingScrollbar and call CurPosAttributeChanged here.
   // (It actually even works some of the time without this, thanks to
   // nsSliderFrame::AttributeChanged's handling of maxpos, but not when
   // we hide the scrollbar on a large size change, such as
   // maximization.)
   if (!mHScrollbarBox && !mVScrollbarBox)
     return PR_FALSE;
   CurPosAttributeChanged(mVScrollbarBox ? mVScrollbarBox->GetContent()
                                         : mHScrollbarBox->GetContent());
@@ -2487,27 +2913,29 @@ static void LayoutAndInvalidate(nsBoxLay
   PRBool rectChanged = aBox->GetRect() != aRect;
   if (rectChanged)
     aBox->Invalidate(aBox->GetOverflowRect());
   nsBoxFrame::LayoutChildAt(aState, aBox, aRect);
   if (rectChanged)
     aBox->Invalidate(aBox->GetOverflowRect());
 }
 
-static void AdjustScrollbarRect(nsIView* aView, nsPresContext* aPresContext,
+static void AdjustScrollbarRect(nsIFrame* aFrame, nsPresContext* aPresContext,
                                 nsRect& aRect, PRBool aVertical)
 {
   if ((aVertical ? aRect.width : aRect.height) == 0)
     return;
 
-  nsPoint offset;
-  nsIWidget* widget = aView->GetNearestWidget(&offset);
-
+  nsPoint offsetToView;
+  nsPoint offsetToWidget;
+  nsIWidget* widget =
+    aFrame->GetClosestView(&offsetToView)->GetNearestWidget(&offsetToWidget);
+  nsPoint offset = offsetToView + offsetToWidget;
   nsIntRect widgetRect;
-  if (!widget->ShowsResizeIndicator(&widgetRect))
+  if (!widget || !widget->ShowsResizeIndicator(&widgetRect))
     return;
 
   nsRect resizerRect =
       nsRect(aPresContext->DevPixelsToAppUnits(widgetRect.x) - offset.x,
              aPresContext->DevPixelsToAppUnits(widgetRect.y) - offset.y,
              aPresContext->DevPixelsToAppUnits(widgetRect.width),
              aPresContext->DevPixelsToAppUnits(widgetRect.height));
 
@@ -2518,104 +2946,94 @@ static void AdjustScrollbarRect(nsIView*
     aRect.height = NS_MAX(0, resizerRect.y - aRect.y);
   else
     aRect.width = NS_MAX(0, resizerRect.x - aRect.x);
 }
 
 void
 nsGfxScrollFrameInner::LayoutScrollbars(nsBoxLayoutState& aState,
                                         const nsRect& aContentArea,
-                                        const nsRect& aOldScrollArea,
-                                        const nsRect& aScrollArea)
+                                        const nsRect& aOldScrollArea)
 {
   NS_ASSERTION(!mSupppressScrollbarUpdate,
                "This should have been suppressed");
-    
-  nsIView* view = mOuter->GetView();
+
   nsPresContext* presContext = mScrolledFrame->PresContext();
   if (mVScrollbarBox) {
     NS_PRECONDITION(mVScrollbarBox->IsBoxFrame(), "Must be a box frame!");
-    nsRect vRect(aScrollArea);
-    vRect.width = aContentArea.width - aScrollArea.width;
-    vRect.x = IsScrollbarOnRight() ? aScrollArea.XMost() : aContentArea.x;
+    nsRect vRect(mScrollPort);
+    vRect.width = aContentArea.width - mScrollPort.width;
+    vRect.x = IsScrollbarOnRight() ? mScrollPort.XMost() : aContentArea.x;
     nsMargin margin;
     mVScrollbarBox->GetMargin(margin);
     vRect.Deflate(margin);
-    AdjustScrollbarRect(view, presContext, vRect, PR_TRUE);
+    AdjustScrollbarRect(mOuter, presContext, vRect, PR_TRUE);
     LayoutAndInvalidate(aState, mVScrollbarBox, vRect);
   }
 
   if (mHScrollbarBox) {
     NS_PRECONDITION(mHScrollbarBox->IsBoxFrame(), "Must be a box frame!");
-    nsRect hRect(aScrollArea);
-    hRect.height = aContentArea.height - aScrollArea.height;
-    hRect.y = PR_TRUE ? aScrollArea.YMost() : aContentArea.y;
+    nsRect hRect(mScrollPort);
+    hRect.height = aContentArea.height - mScrollPort.height;
+    hRect.y = PR_TRUE ? mScrollPort.YMost() : aContentArea.y;
     nsMargin margin;
     mHScrollbarBox->GetMargin(margin);
     hRect.Deflate(margin);
-    AdjustScrollbarRect(view, presContext, hRect, PR_FALSE);
+    AdjustScrollbarRect(mOuter, presContext, hRect, PR_FALSE);
     LayoutAndInvalidate(aState, mHScrollbarBox, hRect);
   }
 
   // place the scrollcorner
   if (mScrollCornerBox) {
     NS_PRECONDITION(mScrollCornerBox->IsBoxFrame(), "Must be a box frame!");
     nsRect r(0, 0, 0, 0);
-    if (aContentArea.x != aScrollArea.x) {
+    if (aContentArea.x != mScrollPort.x) {
       // scrollbar (if any) on left
       r.x = aContentArea.x;
-      r.width = aScrollArea.x - aContentArea.x;
+      r.width = mScrollPort.x - aContentArea.x;
       NS_ASSERTION(r.width >= 0, "Scroll area should be inside client rect");
     } else {
       // scrollbar (if any) on right
-      r.x = aScrollArea.XMost();
-      r.width = aContentArea.XMost() - aScrollArea.XMost();
+      r.x = mScrollPort.XMost();
+      r.width = aContentArea.XMost() - mScrollPort.XMost();
       NS_ASSERTION(r.width >= 0, "Scroll area should be inside client rect");
     }
-    if (aContentArea.y != aScrollArea.y) {
+    if (aContentArea.y != mScrollPort.y) {
       // scrollbar (if any) on top
       r.y = aContentArea.y;
-      r.height = aScrollArea.y - aContentArea.y;
+      r.height = mScrollPort.y - aContentArea.y;
       NS_ASSERTION(r.height >= 0, "Scroll area should be inside client rect");
     } else {
       // scrollbar (if any) on bottom
-      r.y = aScrollArea.YMost();
-      r.height = aContentArea.YMost() - aScrollArea.YMost();
+      r.y = mScrollPort.YMost();
+      r.height = aContentArea.YMost() - mScrollPort.YMost();
       NS_ASSERTION(r.height >= 0, "Scroll area should be inside client rect");
     }
     LayoutAndInvalidate(aState, mScrollCornerBox, r);
   }
 
   // may need to update fixed position children of the viewport,
   // if the client area changed size because of an incremental
   // reflow of a descendant.  (If the outer frame is dirty, the fixed
   // children will be re-laid out anyway)
-  if (aOldScrollArea.Size() != aScrollArea.Size() && 
+  if (aOldScrollArea.Size() != mScrollPort.Size() && 
       !(mOuter->GetStateBits() & NS_FRAME_IS_DIRTY) &&
       mIsRoot) {
     mMayHaveDirtyFixedChildren = PR_TRUE;
   }
   
   // post reflow callback to modify scrollbar attributes
   mUpdateScrollbarAttributes = PR_TRUE;
   if (!mPostedReflowCallback) {
     aState.PresContext()->PresShell()->PostReflowCallback(this);
     mPostedReflowCallback = PR_TRUE;
   }
 }
 
 void
-nsGfxScrollFrameInner::ScrollbarChanged(nsPresContext* aPresContext, nscoord aX, nscoord aY, PRUint32 aFlags)
-{
-  nsIScrollableView* scrollable = GetScrollableView();
-  scrollable->ScrollTo(aX, aY, aFlags);
- // printf("scrolling to: %d, %d\n", aX, aY);
-}
-
-void
 nsGfxScrollFrameInner::SetScrollbarEnabled(nsIContent* aContent, nscoord aMaxPos)
 {
   if (aMaxPos) {
     aContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, PR_TRUE);
   } else {
     aContent->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled,
                       NS_LITERAL_STRING("true"), PR_TRUE);
   }
@@ -2635,21 +3053,37 @@ nsGfxScrollFrameInner::SetCoordAttribute
 
   if (aContent->AttrValueIs(kNameSpaceID_None, aAtom, newValue, eCaseMatters))
     return;
 
   aContent->SetAttr(kNameSpaceID_None, aAtom, newValue, PR_TRUE);
 }
 
 nsRect
-nsGfxScrollFrameInner::GetScrolledRect(const nsSize& aScrollPortSize) const
+nsGfxScrollFrameInner::GetScrolledRect() const
 {
-  nsRect result = mScrolledFrame->GetOverflowRect();
-  nscoord x1 = result.x, x2 = result.XMost(),
-          y1 = result.y, y2 = result.YMost();
+  nsRect result =
+    GetScrolledRectInternal(mScrolledFrame->GetOverflowRect(),
+                            mScrollPort.Size());
+
+  NS_ASSERTION(result.width >= mScrollPort.width,
+               "Scrolled rect smaller than scrollport?");
+  NS_ASSERTION(result.height >= mScrollPort.height,
+               "Scrolled rect smaller than scrollport?");
+  return result;
+}
+
+nsRect
+nsGfxScrollFrameInner::GetScrolledRectInternal(const nsRect& aScrolledFrameOverflowArea,
+                                               const nsSize& aScrollPortSize) const
+{
+  nscoord x1 = aScrolledFrameOverflowArea.x,
+          x2 = aScrolledFrameOverflowArea.XMost(),
+          y1 = aScrolledFrameOverflowArea.y,
+          y2 = aScrolledFrameOverflowArea.YMost();
   if (y1 < 0)
     y1 = 0;
   if (IsLTR() || mIsXUL) {
     if (x1 < 0)
       x1 = 0;
   } else {
     if (x2 > aScrollPortSize.width)
       x2 = aScrollPortSize.width;
@@ -2657,35 +3091,26 @@ nsGfxScrollFrameInner::GetScrolledRect(c
     // its padding alone is larger than the available width), we need to keep the
     // start-edge of the scroll frame anchored to the start-edge of the scrollport. 
     // When the scrolled frame is RTL, this means moving it in our left-based
     // coordinate system, so we need to compensate for its extra width here by
     // effectively repositioning the frame.
     nscoord extraWidth = NS_MAX(0, mScrolledFrame->GetSize().width - aScrollPortSize.width);
     x2 += extraWidth;
   }
-
-  NS_ASSERTION(x2 - x1 >= aScrollPortSize.width,
-               "Scrolled rect smaller than scrollport?");
-  NS_ASSERTION(y2 - y1 >= aScrollPortSize.height,
-               "Scrolled rect smaller than scrollport?");
   return nsRect(x1, y1, x2 - x1, y2 - y1);
 }
 
 nsMargin
 nsGfxScrollFrameInner::GetActualScrollbarSizes() const {
-  nsMargin border;
-  mOuter->GetBorder(border);
-  nsRect r(nsPoint(0,0), mOuter->GetSize());
-  r.Deflate(border);
-  nsRect scrollArea = mScrollableView->View()->GetBounds();
-
-  return nsMargin(scrollArea.x - r.x, scrollArea.y - r.y,
-                  r.XMost() - scrollArea.XMost(),
-                  r.YMost() - scrollArea.YMost());
+  nsRect r = mOuter->GetPaddingRect() - mOuter->GetPosition();
+
+  return nsMargin(mScrollPort.x - r.x, mScrollPort.y - r.y,
+                  r.XMost() - mScrollPort.XMost(),
+                  r.YMost() - mScrollPort.YMost());
 }
 
 void
 nsGfxScrollFrameInner::SetScrollbarVisibility(nsIBox* aScrollbar, PRBool aVisible)
 {
   if (!aScrollbar)
     return;
 
@@ -2794,56 +3219,40 @@ nsGfxScrollFrameInner::SaveState(nsIStat
   }
 
   nsIScrollbarMediator* mediator = do_QueryFrame(GetScrolledFrame());
   if (mediator) {
     // child handles its own scroll state, so don't bother saving state here
     return nsnull;
   }
 
-  nsIScrollableView* scrollingView = GetScrollableView();
-  PRInt32 x,y;
-  scrollingView->GetScrollPosition(x,y);
+  nsPoint scrollPos = GetScrollPosition();
   // Don't save scroll position if we are at (0,0)
-  if (!x && !y) {
+  if (scrollPos == nsPoint(0,0)) {
     return nsnull;
   }
 
-  nsIView* child = nsnull;
-  scrollingView->GetScrolledView(child);
-  if (!child) {
-    return nsnull;
-  }
-
-  nsRect childRect = child->GetBounds();
-  childRect.x = x;
-  childRect.y = y;
   nsPresState* state = new nsPresState();
   if (!state) {
     return nsnull;
   }
-
-  state->SetScrollState(childRect.TopLeft());
+ 
+  state->SetScrollState(scrollPos);
 
   return state;
 }
 
 void
 nsGfxScrollFrameInner::RestoreState(nsPresState* aState)
 {
-  mRestoreRect = nsRect(aState->GetScrollState(), nsSize(0, 0));
+  mRestorePos = aState->GetScrollState();
   mLastPos.x = -1;
   mLastPos.y = -1;
   mDidHistoryRestore = PR_TRUE;
-  nsIScrollableView* scrollingView = GetScrollableView();
-  if (scrollingView) {
-    scrollingView->GetScrollPosition(mLastPos.x, mLastPos.y);
-  } else {
-    mLastPos = nsPoint(0, 0);
-  }
+  mLastPos = mScrolledFrame ? GetScrollPosition() : nsPoint(0,0);
 }
 
 void
 nsGfxScrollFrameInner::PostScrolledAreaEvent(nsRect &aScrolledArea)
 {
   if (mScrolledAreaEventDispatcher.IsPending()) {
     mScrolledAreaEventDispatcher.get()->mScrolledArea = aScrolledArea;
     return;
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -42,70 +42,62 @@
 
 #include "nsHTMLContainerFrame.h"
 #include "nsIAnonymousContentCreator.h"
 #include "nsBoxFrame.h"
 #include "nsIScrollableFrame.h"
 #include "nsIScrollPositionListener.h"
 #include "nsIStatefulFrame.h"
 #include "nsThreadUtils.h"
-#include "nsIScrollableView.h"
-#include "nsIView.h"
 #include "nsIReflowCallback.h"
 #include "nsBoxLayoutState.h"
 #include "nsQueryFrame.h"
+#include "nsCOMArray.h"
+#ifdef MOZ_SVG
+#include "nsSVGIntegrationUtils.h"
+#endif
 
 class nsPresContext;
 class nsIPresShell;
 class nsIContent;
 class nsIAtom;
 class nsIDocument;
 class nsIScrollFrameInternal;
 class nsPresState;
 struct ScrollReflowState;
 
-class nsGfxScrollFrameInner : public nsIScrollPositionListener,
-                              public nsIReflowCallback {
+class nsGfxScrollFrameInner : public nsIReflowCallback {
 public:
-  NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr);
-  NS_IMETHOD_(nsrefcnt) AddRef(void) { return 2; }
-  NS_IMETHOD_(nsrefcnt) Release(void) { return 1; }
+  class AsyncScroll;
 
   nsGfxScrollFrameInner(nsContainerFrame* aOuter, PRBool aIsRoot,
                         PRBool aIsXUL);
   ~nsGfxScrollFrameInner();
 
   typedef nsIScrollableFrame::ScrollbarStyles ScrollbarStyles;
   ScrollbarStyles GetScrollbarStylesFromFrame() const;
 
   // If a child frame was added or removed on the scrollframe,
   // reload our child frame list.
   // We need this if a scrollbar frame is recreated.
   void ReloadChildFrames();
-  void CreateScrollableView();
 
   nsresult CreateAnonymousContent(nsTArray<nsIContent*>& aElements);
   nsresult FireScrollPortEvent();
   void PostOverflowEvent();
   void Destroy();
 
   nsresult BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                             const nsRect&           aDirtyRect,
                             const nsDisplayListSet& aLists);
 
   // nsIReflowCallback
   virtual PRBool ReflowFinished();
   virtual void ReflowCallbackCanceled();
 
-  // nsIScrollPositionListener
-
-  NS_IMETHOD ScrollPositionWillChange(nscoord aX, nscoord aY);
-  virtual void ViewPositionDidChange(nsTArray<nsIWidget::Configuration>* aConfigurations);
-  NS_IMETHOD ScrollPositionDidChange(nscoord aX, nscoord aY);
-
   // This gets called when the 'curpos' attribute on one of the scrollbars changes
   void CurPosAttributeChanged(nsIContent* aChild);
   void PostScrollEvent();
   void FireScrollEvent();
   void PostScrolledAreaEvent(nsRect &aScrolledArea);
   void FireScrolledAreaEvent(nsRect &aScrolledArea);
 
 
@@ -131,34 +123,31 @@ public:
                                        nscoord aMaxXY, nscoord aCurPosXY,
                                        nscoord aPageIncrement,
                                        nscoord aIncrement);
   static void SetScrollbarEnabled(nsIContent* aContent, nscoord aMaxPos);
   static void SetCoordAttribute(nsIContent* aContent, nsIAtom* aAtom,
                                 nscoord aSize);
   nscoord GetCoordAttribute(nsIBox* aFrame, nsIAtom* atom, nscoord defaultValue);
 
-  // Like ScrollPositionDidChange, but initiated by this frame rather than from the
-  // scrolling view
-  void InternalScrollPositionDidChange(nscoord aX, nscoord aY);
+  // Update scrollbar curpos attributes to reflect current scroll position
+  void UpdateScrollbarPosition();
 
-  nsIScrollableView* GetScrollableView() const { return mScrollableView; }
-  nsRect GetScrollPortRect() const {
-    return mScrollableView->View()->GetBounds();
-  }
+  nsRect GetScrollPortRect() const { return mScrollPort; }
   nsPoint GetScrollPosition() const {
-    nsPoint scrollPosition;
-    mScrollableView->GetScrollPosition(scrollPosition.x, scrollPosition.y);
-    return scrollPosition;
+    return mScrollPort.TopLeft() - mScrolledFrame->GetPosition();
   }
   nsRect GetScrollRange() const;
 
-  nsIView* GetParentViewForChildFrame(nsIFrame* aFrame) const;
-
+  nsPoint ClampAndRestrictToDevPixels(const nsPoint& aPt, nsIntPoint* aPtDevPx) const;
+  nsPoint ClampScrollPosition(const nsPoint& aPt) const;
+  static void AsyncScrollCallback(nsITimer *aTimer, void* anInstance);
   void ScrollTo(nsPoint aScrollPosition, nsIScrollableFrame::ScrollMode aMode);
+  void ScrollToImpl(nsPoint aScrollPosition);
+  void ScrollVisual(nsIntPoint aPixDelta);
   void ScrollBy(nsIntPoint aDelta, nsIScrollableFrame::ScrollUnit aUnit,
                 nsIScrollableFrame::ScrollMode aMode, nsIntPoint* aOverflow);
   void ScrollToRestoredPosition();
   nsSize GetLineScrollAmount() const;
   nsSize GetPageScrollAmount() const;
 
   nsPresState* SaveState(nsIStatefulFrame::SpecialStateID aStateID);
   void RestoreState(nsPresState* aState);
@@ -166,75 +155,91 @@ public:
   nsresult GetVScrollbarHintFromGlobalHistory(PRBool* aVScrollbarNeeded);
 
   nsIFrame* GetScrolledFrame() const { return mScrolledFrame; }
   nsIBox* GetScrollbarBox(PRBool aVertical) const {
     return aVertical ? mVScrollbarBox : mHScrollbarBox;
   }
 
   void AddScrollPositionListener(nsIScrollPositionListener* aListener) {
-    mScrollableView->AddScrollPositionListener(aListener);
+    mListeners.AppendObject(aListener);
   }
   void RemoveScrollPositionListener(nsIScrollPositionListener* aListener) {
-    mScrollableView->RemoveScrollPositionListener(aListener);
+    mListeners.RemoveObject(aListener);
   }
 
-  void ScrollbarChanged(nsPresContext* aPresContext, nscoord aX, nscoord aY, PRUint32 aFlags);
-
   static void SetScrollbarVisibility(nsIBox* aScrollbar, PRBool aVisible);
 
   /**
    * GetScrolledRect is designed to encapsulate deciding which
    * directions of overflow should be reachable by scrolling and which
    * should not.  Callers should NOT depend on it having any particular
    * behavior (although nsXULScrollFrame currently does).
+   * 
+   * This should only be called when the scrolled frame has been
+   * reflowed with the scroll port size given in mScrollPort.
    *
    * Currently it allows scrolling down and to the right for
    * nsHTMLScrollFrames with LTR directionality and for all
    * nsXULScrollFrames, and allows scrolling down and to the left for
    * nsHTMLScrollFrames with RTL directionality.
    */
-  nsRect GetScrolledRect(const nsSize& aScrollPortSize) const;
-  nsSize GetScrollPortSize() const
-  {
-    return mScrollableView->View()->GetBounds().Size();
-  }
+  nsRect GetScrolledRect() const;
+
+  /**
+   * GetScrolledRectInternal is designed to encapsulate deciding which
+   * directions of overflow should be reachable by scrolling and which
+   * should not.  Callers should NOT depend on it having any particular
+   * behavior (although nsXULScrollFrame currently does).
+   * 
+   * Currently it allows scrolling down and to the right for
+   * nsHTMLScrollFrames with LTR directionality and for all
+   * nsXULScrollFrames, and allows scrolling down and to the left for
+   * nsHTMLScrollFrames with RTL directionality.
+   */
+  nsRect GetScrolledRectInternal(const nsRect& aScrolledOverflowArea,
+                                 const nsSize& aScrollPortSize) const;
 
   nsMargin GetActualScrollbarSizes() const;
   nsMargin GetDesiredScrollbarSizes(nsBoxLayoutState* aState);
   PRBool IsLTR() const;
   PRBool IsScrollbarOnRight() const;
   void LayoutScrollbars(nsBoxLayoutState& aState,
                         const nsRect& aContentArea,
-                        const nsRect& aOldScrollArea,
-                        const nsRect& aScrollArea);
+                        const nsRect& aOldScrollArea);
 
   // owning references to the nsIAnonymousContentCreator-built content
   nsCOMPtr<nsIContent> mHScrollbarContent;
   nsCOMPtr<nsIContent> mVScrollbarContent;
   nsCOMPtr<nsIContent> mScrollCornerContent;
 
   nsRevocableEventPtr<ScrollEvent> mScrollEvent;
   nsRevocableEventPtr<AsyncScrollPortEvent> mAsyncScrollPortEvent;
-  nsIScrollableView* mScrollableView;
   nsIBox* mHScrollbarBox;
   nsIBox* mVScrollbarBox;
   nsIFrame* mScrolledFrame;
   nsIBox* mScrollCornerBox;
   nsContainerFrame* mOuter;
+  AsyncScroll* mAsyncScroll;
+  nsCOMArray<nsIScrollPositionListener> mListeners;
+  nsRect mScrollPort;
+  // Where we're currently scrolling to, if we're scrolling asynchronously.
+  // If we're not in the middle of an asynchronous scroll then this is
+  // just the current scroll position. ScrollBy will choose its
+  // destination based on this value.
+  nsPoint mDestination;
 
-  nsRect mRestoreRect;
+  nsPoint mRestorePos;
   nsPoint mLastPos;
 
   PRPackedBool mNeverHasVerticalScrollbar:1;
   PRPackedBool mNeverHasHorizontalScrollbar:1;
   PRPackedBool mHasVerticalScrollbar:1;
   PRPackedBool mHasHorizontalScrollbar:1;
-  PRPackedBool mViewInitiatedScroll:1;
-  PRPackedBool mFrameInitiatedScroll:1;
+  PRPackedBool mFrameIsUpdatingScrollbar:1;
   PRPackedBool mDidHistoryRestore:1;
   // Is this the scrollframe for the document's viewport?
   PRPackedBool mIsRoot:1;
   // Is mOuter an nsXULScrollFrame?
   PRPackedBool mIsXUL:1;
   // If true, don't try to layout the scrollbars in Reflow().  This can be
   // useful if multiple passes are involved, because we don't want to place the
   // scrollbars at the wrong size.
@@ -317,17 +322,18 @@ public:
   PRBool ScrolledContentDependsOnHeight(ScrollReflowState* aState);
   nsresult ReflowScrolledFrame(ScrollReflowState* aState,
                                PRBool aAssumeHScroll,
                                PRBool aAssumeVScroll,
                                nsHTMLReflowMetrics* aMetrics,
                                PRBool aFirstPass);
   nsresult ReflowContents(ScrollReflowState* aState,
                           const nsHTMLReflowMetrics& aDesiredSize);
-  void PlaceScrollArea(const ScrollReflowState& aState);
+  void PlaceScrollArea(const ScrollReflowState& aState,
+                       const nsPoint& aScrollPosition);
   nscoord GetIntrinsicVScrollbarWidth(nsIRenderingContext *aRenderingContext);
 
   virtual nscoord GetMinWidth(nsIRenderingContext *aRenderingContext);
   virtual nscoord GetPrefWidth(nsIRenderingContext *aRenderingContext);
   NS_IMETHOD GetPadding(nsMargin& aPadding);
   virtual PRBool IsCollapsed(nsBoxLayoutState& aBoxLayoutState);
   
   NS_IMETHOD Reflow(nsPresContext*          aPresContext,
@@ -348,29 +354,24 @@ public:
 
   NS_IMETHOD RemoveFrame(nsIAtom*        aListName,
                          nsIFrame*       aOldFrame);
 
   virtual nsIScrollableFrame* GetScrollTargetFrame() {
     return this;
   }
 
-  virtual nsIView* GetParentViewForChildFrame(nsIFrame* aFrame) const {
-    return mInner.GetParentViewForChildFrame(aFrame);
-  }
-
   virtual nsIFrame* GetContentInsertionFrame() {
     return mInner.GetScrolledFrame()->GetContentInsertionFrame();
   }
 
   virtual void InvalidateInternal(const nsRect& aDamageRect,
                                   nscoord aX, nscoord aY, nsIFrame* aForChild,
                                   PRUint32 aFlags);
 
-  virtual PRBool NeedsView() { return PR_TRUE; }
   virtual PRBool DoesClipChildren() { return PR_TRUE; }
   virtual nsSplittableType GetSplittableType() const;
 
   virtual nsPoint GetPositionOfChildIgnoringScrolling(nsIFrame* aChild)
   { nsPoint pt = aChild->GetPosition();
     if (aChild == mInner.GetScrolledFrame()) pt += GetScrollPosition();
     return pt;
   }
@@ -474,20 +475,16 @@ protected:
   nsHTMLScrollFrame(nsIPresShell* aShell, nsStyleContext* aContext, PRBool aIsRoot);
   virtual PRIntn GetSkipSides() const;
   
   void SetSuppressScrollbarUpdate(PRBool aSuppress) {
     mInner.mSupppressScrollbarUpdate = aSuppress;
   }
   PRBool GuessHScrollbarNeeded(const ScrollReflowState& aState);
   PRBool GuessVScrollbarNeeded(const ScrollReflowState& aState);
-  nsSize GetScrollPortSize() const
-  {
-    return mInner.GetScrollPortSize();
-  }
 
   PRBool IsScrollbarUpdateSuppressed() const {
     return mInner.mSupppressScrollbarUpdate;
   }
 
   // Return whether we're in an "initial" reflow.  Some reflows with
   // NS_FRAME_FIRST_REFLOW set are NOT "initial" as far as we're concerned.
   PRBool InInitialReflow() const;
@@ -551,29 +548,24 @@ public:
 
   NS_IMETHOD RemoveFrame(nsIAtom*        aListName,
                          nsIFrame*       aOldFrame);
 
   virtual nsIScrollableFrame* GetScrollTargetFrame() {
     return this;
   }
 
-  virtual nsIView* GetParentViewForChildFrame(nsIFrame* aFrame) const {
-    return mInner.GetParentViewForChildFrame(aFrame);
-  }
-
   virtual nsIFrame* GetContentInsertionFrame() {
     return mInner.GetScrolledFrame()->GetContentInsertionFrame();
   }
 
   virtual void InvalidateInternal(const nsRect& aDamageRect,
                                   nscoord aX, nscoord aY, nsIFrame* aForChild,
                                   PRUint32 aFlags);
 
-  virtual PRBool NeedsView() { return PR_TRUE; }
   virtual PRBool DoesClipChildren() { return PR_TRUE; }
   virtual nsSplittableType GetSplittableType() const;
 
   virtual nsPoint GetPositionOfChildIgnoringScrolling(nsIFrame* aChild)
   { nsPoint pt = aChild->GetPosition();
     if (aChild == mInner.GetScrolledFrame()) pt += GetScrollPosition();
     return pt;
   }
@@ -585,35 +577,34 @@ public:
   virtual nsSize GetPrefSize(nsBoxLayoutState& aBoxLayoutState);
   virtual nsSize GetMaxSize(nsBoxLayoutState& aBoxLayoutState);
   virtual nscoord GetBoxAscent(nsBoxLayoutState& aBoxLayoutState);
 
   NS_IMETHOD DoLayout(nsBoxLayoutState& aBoxLayoutState);
   NS_IMETHOD GetPadding(nsMargin& aPadding);
 
   nsresult Layout(nsBoxLayoutState& aState);
-  void LayoutScrollArea(nsBoxLayoutState& aState, const nsRect& aRect);
+  void LayoutScrollArea(nsBoxLayoutState& aState, const nsPoint& aScrollPosition);
 
   static PRBool AddRemoveScrollbar(PRBool& aHasScrollbar, 
                                    nscoord& aXY, 
                                    nscoord& aSize, 
                                    nscoord aSbSize, 
                                    PRBool aOnRightOrBottom, 
                                    PRBool aAdd);
   
   PRBool AddRemoveScrollbar(nsBoxLayoutState& aState, 
-                            nsRect& aScrollAreaSize, 
                             PRBool aOnTop, 
                             PRBool aHorizontal, 
                             PRBool aAdd);
   
-  PRBool AddHorizontalScrollbar (nsBoxLayoutState& aState, nsRect& aScrollAreaSize, PRBool aOnBottom);
-  PRBool AddVerticalScrollbar   (nsBoxLayoutState& aState, nsRect& aScrollAreaSize, PRBool aOnRight);
-  void RemoveHorizontalScrollbar(nsBoxLayoutState& aState, nsRect& aScrollAreaSize, PRBool aOnBottom);
-  void RemoveVerticalScrollbar  (nsBoxLayoutState& aState, nsRect& aScrollAreaSize, PRBool aOnRight);
+  PRBool AddHorizontalScrollbar (nsBoxLayoutState& aState, PRBool aOnBottom);
+  PRBool AddVerticalScrollbar   (nsBoxLayoutState& aState, PRBool aOnRight);
+  void RemoveHorizontalScrollbar(nsBoxLayoutState& aState, PRBool aOnBottom);
+  void RemoveVerticalScrollbar  (nsBoxLayoutState& aState, PRBool aOnRight);
 
   static void AdjustReflowStateForPrintPreview(nsBoxLayoutState& aState, PRBool& aSetBack);
   static void AdjustReflowStateBack(nsBoxLayoutState& aState, PRBool aSetBack);
 
   // nsIScrollableFrame
   virtual nsIFrame* GetScrolledFrame() const {
     return mInner.GetScrolledFrame();
   }
--- a/layout/generic/nsIScrollableFrame.h
+++ b/layout/generic/nsIScrollableFrame.h
@@ -144,19 +144,18 @@ public:
    */
   enum ScrollUnit { DEVICE_PIXELS, LINES, PAGES, WHOLE };
   /**
    * Modifies the current scroll position by aDelta units given by aUnit,
    * clamping it to GetScrollRange. If WHOLE is specified as the unit,
    * content is scrolled all the way in the direction(s) given by aDelta.
    * @param aOverflow if non-null, returns the amount that scrolling
    * was clamped by in each direction (how far we moved the scroll position
-   to bring it back into the legal range). This is never negative. This
-   * is only supported for LINES and DEVICE_PIXELS. The values are in
-   * device pixels.
+   * to bring it back into the legal range). This is never negative. The
+   * values are in device pixels.
    */
   virtual void ScrollBy(nsIntPoint aDelta, ScrollUnit aUnit, ScrollMode aMode,
                         nsIntPoint* aOverflow = nsnull) = 0;
   /**
    * This tells the scroll frame to try scrolling to the scroll
    * position that was restored from the history. This must be called
    * at least once after state has been restored. It is called by the
    * scrolled frame itself during reflow, but sometimes state can be
--- a/view/public/nsIScrollPositionListener.h
+++ b/view/public/nsIScrollPositionListener.h
@@ -56,17 +56,17 @@
  */
 class nsIScrollPositionListener : public nsISupports {
 public:
 	NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISCROLLPOSITIONLISTENER_IID)
 
 	NS_IMETHOD ScrollPositionWillChange(nscoord aX, nscoord aY) = 0;
 	// The scrollframe implementation of this method appends a list of widget
 	// configuration requests to aConfigurations. No other implementor
-	// should touch it.
+	// should touch it. No longer gets called!!!
 	virtual void ViewPositionDidChange(nsTArray<nsIWidget::Configuration>* aConfigurations) = 0;
 	NS_IMETHOD ScrollPositionDidChange(nscoord aX, nscoord aY) = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIScrollPositionListener,
                               NS_ISCROLLPOSITIONLISTENER_IID)
 
 #endif /* nsIScrollPositionListener_h___ */
--- a/view/public/nsIView.h
+++ b/view/public/nsIView.h
@@ -59,18 +59,18 @@ class nsIWidget;
 //        the layer's parent.
 enum nsViewVisibility {
   nsViewVisibility_kHide = 0,
   nsViewVisibility_kShow = 1
 };
 
 // IID for the nsIView interface
 #define NS_IVIEW_IID    \
-  { 0x18b5f32a, 0x921a, 0x4772, \
-    { 0xa4, 0x3d, 0xf3, 0x04, 0x5c, 0xb9, 0xc2, 0x59 } }
+  { 0x4435167c, 0xb627, 0x4073, \
+    { 0x9c, 0x92, 0xbc, 0x34, 0x39, 0xd9, 0xf8, 0xd2 } }
 
 // Public view flags are defined in this file
 #define NS_VIEW_FLAGS_PUBLIC              0x00FF
 // Private view flags are private to the view module,
 // and are defined in nsView.h
 #define NS_VIEW_FLAGS_PRIVATE             0xFF00
 
 // Public view flags
@@ -163,16 +163,22 @@ public:
    */
   nsPoint GetPosition() const {
     // Call ExternalIsRoot here so that we can get to it from other
     // components
     NS_ASSERTION(!ExternalIsRoot() || (mPosX == 0 && mPosY == 0),
                  "root views should always have explicit position of (0,0)");
     return nsPoint(mPosX, mPosY);
   }
+
+  /**
+   * Set the position of a view. This does not cause any invalidation. It
+   * does reposition any widgets in this view or its descendants.
+   */
+  virtual void SetPosition(nscoord aX, nscoord aY) = 0;
   
   /**
    * Called to get the dimensions and position of the view's bounds.
    * The view's bounds (x,y) are in the coordinate space of the parent view.
    * The view's bounds (x,y) might not be the same as the view's position,
    * if the view has content above or to the left of its origin.
    * @param aBounds out parameter for bounds
    */
--- a/view/public/nsIViewManager.h
+++ b/view/public/nsIViewManager.h
@@ -415,16 +415,51 @@ public:
 
   /**
    * Dispatch a mouse move event based on the most recent mouse
    * position.  This is used when the contents of the page moved
    * (aFromScroll is false) or scrolled (aFromScroll is true).
    */
   NS_IMETHOD SynthesizeMouseMove(PRBool aFromScroll)=0;
 
+  /**
+   * Called to inform the view manager that a view is about to bit-blit.
+   * @param aView the view that will bit-blit
+   * @param aScrollAmount how much aView will scroll by
+   * @return always returns NS_OK
+   * @note
+   * This method used to return void, but MSVC 6.0 SP5 (without the
+   * Processor Pack) and SP6, and the MS eMbedded Visual C++ 4.0 SP4
+   * (for WINCE) hit an internal compiler error when compiling this
+   * method:
+   *
+   * @par
+@verbatim
+       fatal error C1001: INTERNAL COMPILER ERROR
+                   (compiler file 'E:\8966\vc98\p2\src\P2\main.c', line 494)
+@endverbatim
+   *
+   * @par
+   * Making the method return nsresult worked around the internal
+   * compiler error.  See Bugzilla bug 281158.  (The WINCE internal
+   * compiler error was addressed by the patch in bug 291229 comment
+   * 14 although the bug report did not mention the problem.)
+   */
+  virtual nsresult WillBitBlit(nsIView* aView, const nsRect& aRect,
+                               nsPoint aScrollAmount) = 0;
+  
+  /**
+   * Called to inform the view manager that a view has scrolled via a
+   * bitblit.
+   * The view manager will invalidate any widgets which may need
+   * to be rerendered.
+   * @param aView view to paint
+   * @param aUpdateRegion ensure that this part of the view is repainted
+   */
+  virtual void UpdateViewAfterScroll(nsIView *aView, const nsRegion& aUpdateRegion) = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIViewManager, NS_IVIEWMANAGER_IID)
 
 // Paint timing mode flags
 
 // intermediate: do no special timing processing; repaint when the
 // toolkit issues an expose event (which will happen *before* PLEvent
--- a/view/public/nsIViewObserver.h
+++ b/view/public/nsIViewObserver.h
@@ -42,18 +42,18 @@
 #include "nsEvent.h"
 #include "nsColor.h"
 #include "nsRect.h"
 
 class nsIRenderingContext;
 class nsGUIEvent;
 
 #define NS_IVIEWOBSERVER_IID  \
-  { 0xc85d474d, 0x316e, 0x491c, \
-    { 0x8b, 0xc5, 0x24, 0xba, 0xb7, 0xbb, 0x68, 0x9e } }
+  { 0xba1357b6, 0xe3c7, 0x426a, \
+    { 0xb3, 0x68, 0xfe, 0xe8, 0x24, 0x8c, 0x08, 0x38 } }
 
 class nsIViewObserver : public nsISupports
 {
 public:
   
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IVIEWOBSERVER_IID)
 
   /* called when the observer needs to paint. This paints the entire
@@ -85,26 +85,16 @@ public:
    * @param aDirtyRect the rectangle to be painted, in the coordinates
    * of aRootView
    * @return error status
    */
   NS_IMETHOD PaintDefaultBackground(nsIView*             aRootView,
                                     nsIRenderingContext* aRenderingContext,
                                     const nsRect&        aDirtyRect) = 0;
 
-  /**
-   * @see nsLayoutUtils::ComputeRepaintRegionForCopy
-   */
-  NS_IMETHOD ComputeRepaintRegionForCopy(nsIView*      aRootView,
-                                         nsIView*      aMovingView,
-                                         nsPoint       aDelta,
-                                         const nsRect& aUpdateRect,
-                                         nsRegion*     aBlitRegion,
-                                         nsRegion*     aRepaintRegion) = 0;
-
   /* called when the observer needs to handle an event
    * @param aView  - where to start processing the event; the root view,
    * or the view that's currently capturing this sort of event; must be a view
    * for this presshell
    * @param aEvent - event notification
    * @param aEventStatus - out parameter for event handling
    *                       status
    * @param aHandled - whether the correct frame was found to
@@ -132,30 +122,16 @@ public:
   /**
    * Notify the observer that we're about to start painting.  This
    * gives the observer a chance to make some last-minute invalidates
    * and geometry changes if it wants to.
    */
   NS_IMETHOD_(void) WillPaint() = 0;
 
   /**
-   * Notify the observer that it should invalidate the frame bounds for
-   * the frame associated with this view, due to scrolling.
-   */
-  NS_IMETHOD_(void) InvalidateFrameForScrolledView(nsIView *aView) = 0;
-
-  /**
-   * Notify the observer that some areas of the root view have been
-   * invalidated/blitted due to scrolling. A bitblit-scroll occurred
-   * so we can be sure that rootView->NeedsInvalidateFrameOnScroll is false.
-   */
-  NS_IMETHOD_(void) NotifyInvalidateForScrolledView(const nsRegion& aBlitRegion,
-                                                    const nsRegion& aInvalidateRegion) = 0;
-
-  /**
    * Dispatch the given synthesized mouse move event, and if
    * aFlushOnHoverChange is true, flush layout if :hover changes cause
    * any restyles.
    */
   NS_IMETHOD_(void) DispatchSynthMouseMove(nsGUIEvent *aEvent,
                                            PRBool aFlushOnHoverChange) = 0;
 
   /**
--- a/view/src/Makefile.in
+++ b/view/src/Makefile.in
@@ -49,15 +49,14 @@ MODULE_NAME	= nsViewModule
 GRE_MODULE	= 1
 LIBXUL_LIBRARY	= 1
 
 
 EXTRA_DSO_LIBS = gkgfx
 
 CPPSRCS		= \
 		nsView.cpp \
-		nsScrollPortView.cpp \
 		nsViewManager.cpp \
 		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
 CXXFLAGS += $(MOZ_CAIRO_CFLAGS)
--- a/view/src/nsView.h
+++ b/view/src/nsView.h
@@ -131,16 +131,18 @@ public:
   // These are defined exactly the same in nsIView, but for now they have to be redeclared
   // here because of stupid C++ method hiding rules
 
   PRBool HasNonEmptyDirtyRegion() {
     return mDirtyRegion && !mDirtyRegion->IsEmpty();
   }
   nsRegion* GetDirtyRegion() {
     if (!mDirtyRegion) {
+      NS_ASSERTION(!mParent || GetFloating(),
+                   "Only display roots should have dirty regions");
       mDirtyRegion = new nsRegion();
       NS_ASSERTION(mDirtyRegion, "Out of memory!");
     }
     return mDirtyRegion;
   }
 
   void InsertChild(nsView *aChild, nsView *aSibling);
   void RemoveChild(nsView *aChild);
--- a/view/src/nsViewManager.cpp
+++ b/view/src/nsViewManager.cpp
@@ -576,102 +576,69 @@ NS_IMETHODIMP nsViewManager::UpdateView(
   // Mark the entire view as damaged
   nsView* view = static_cast<nsView*>(aView);
 
   nsRect bounds = view->GetBounds();
   view->ConvertFromParentCoords(&bounds.x, &bounds.y);
   return UpdateView(view, bounds, aUpdateFlags);
 }
 
-// This method accumulates the intersectons of all dirty regions attached to
-// descendants of aSourceView with the cliprect of aTargetView into the dirty
-// region of aTargetView, after offseting said intersections by aOffset.
-static void
-AccumulateIntersectionsIntoDirtyRegion(nsView* aTargetView,
-                                       nsView* aSourceView,
-                                       const nsPoint& aOffset)
-{
-  if (aSourceView->HasNonEmptyDirtyRegion()) {
-    // In most cases, aSourceView is an ancestor of aTargetView, since most
-    // commonly we have dirty rects on the root view.
-    nsPoint offset = aTargetView->GetOffsetTo(aSourceView);
-    nsRegion intersection;
-    intersection = *aSourceView->GetDirtyRegion();
-    if (!intersection.IsEmpty()) {
-      nsRegion* targetRegion = aTargetView->GetDirtyRegion();
-      if (targetRegion) {
-        intersection.MoveBy(-offset + aOffset);
-        targetRegion->Or(*targetRegion, intersection);
-        // Random simplification number...
-        targetRegion->SimplifyOutward(20);
-      }
-    }
-  }
-
-  if (aSourceView == aTargetView) {
-    // No need to do this with kids of aTargetView
-    return;
-  }
-  
-  for (nsView* kid = aSourceView->GetFirstChild();
-       kid;
-       kid = kid->GetNextSibling()) {
-    AccumulateIntersectionsIntoDirtyRegion(aTargetView, kid, aOffset);
-  }
-}
-
 nsresult
-nsViewManager::WillBitBlit(nsView* aView, nsPoint aScrollAmount)
+nsViewManager::WillBitBlit(nsIView* aView, const nsRect& aRect,
+                           nsPoint aCopyDelta)
 {
   if (!IsRootVM()) {
-    RootViewManager()->WillBitBlit(aView, aScrollAmount);
+    RootViewManager()->WillBitBlit(aView, aRect, aCopyDelta);
     return NS_OK;
   }
 
-  NS_PRECONDITION(aView, "Must have a view");
-  NS_PRECONDITION(!aView->NeedsInvalidateFrameOnScroll(), "We shouldn't be BitBlting.");
+  // aView must be a display root
+  NS_PRECONDITION(aView &&
+                  (aView == mRootView || aView->GetFloating()),
+                  "Must have a display root view");
 
   ++mScrollCnt;
   
+  nsView* v = static_cast<nsView*>(aView);
+
   // Since the view is actually moving the widget by -aScrollAmount, that's the
   // offset we want to use when accumulating dirty rects.
-  AccumulateIntersectionsIntoDirtyRegion(aView, GetRootView(), -aScrollAmount);
+  if (v->HasNonEmptyDirtyRegion()) {
+    nsRegion* dirty = v->GetDirtyRegion();
+    nsRegion intersection;
+    intersection.And(*dirty, aRect);
+    if (!intersection.IsEmpty()) {
+      dirty->Or(*dirty, intersection);
+      // Random simplification number...
+      dirty->SimplifyOutward(20);
+    }
+  }
   return NS_OK;
 }
 
 // Invalidate all widgets which overlap the view, other than the view's own widgets.
 void
-nsViewManager::UpdateViewAfterScroll(nsView *aView,
-                                     const nsRegion& aBlitRegion,
+nsViewManager::UpdateViewAfterScroll(nsIView *aView,
                                      const nsRegion& aUpdateRegion)
 {
   NS_ASSERTION(RootViewManager()->mScrollCnt > 0,
                "Someone forgot to call WillBitBlit()");
   // No need to check for empty aUpdateRegion here. We'd still need to
   // do most of the work here anyway.
 
-  nsView* displayRoot = GetDisplayRootFor(aView);
-  nsPoint offset = aView->GetOffsetTo(displayRoot);
+  nsView* view = static_cast<nsView*>(aView);
+  nsView* displayRoot = GetDisplayRootFor(view);
+  nsPoint offset = view->GetOffsetTo(displayRoot);
   nsRegion update(aUpdateRegion);
   update.MoveBy(offset);
 
   UpdateWidgetArea(displayRoot, displayRoot->GetWidget(),
                    update, nsnull);
   // FlushPendingInvalidates();
 
-  // Don't send invalidation notifications when we're scrolling in a popup
-  if (displayRoot == RootViewManager()->mRootView) {
-    nsPoint rootOffset = aView->GetOffsetTo(mRootView);
-    nsRegion blit(aBlitRegion);
-    blit.MoveBy(rootOffset);
-    update.MoveBy(rootOffset - offset);
-    
-    GetViewObserver()->NotifyInvalidateForScrolledView(blit, update);
-  }
-
   Composite();
   --RootViewManager()->mScrollCnt;
 }
 
 static PRBool
 IsWidgetDrawnByPlugin(nsIWidget* aWidget, nsIView* aView)
 {
   if (aView->GetWidget() == aWidget)
@@ -1457,54 +1424,16 @@ NS_IMETHODIMP nsViewManager::ResizeView(
   // region set, then we expect layout to update the clip region too. Thus
   // in the case where mClipRect has been optimized away to just be a null
   // pointer, and this resize is implicitly changing the clip rect, it's OK
   // because layout will change it back again if necessary.
 
   return NS_OK;
 }
 
-void nsViewManager::GetRegionsForBlit(nsView* aView, nsPoint aDelta,
-                                      nsRegion* aBlitRegion,
-                                      nsRegion* aRepaintRegion)
-{
-  NS_ASSERTION(!IsPainting(),
-               "View manager shouldn't be scrolling during a paint");
-
-  nsView* displayRoot = GetDisplayRootFor(aView);
-  nsPoint displayOffset = aView->GetParent()->GetOffsetTo(displayRoot);
-  nsRect parentBounds = aView->GetParent()->GetDimensions() + displayOffset;
-
-  // The area clipped by the scrolling view is snapped to device pixels
-  // (in nsThebesRenderingContext::SetClipRect), so snap the bound here
-  // that we use to compute the blit and repaint regions
-  PRInt32 p2a = mContext->AppUnitsPerDevPixel();
-  parentBounds = parentBounds.ToNearestPixels(p2a).ToAppUnits(p2a);
-
-  if (IsPainting() || !mObserver) {
-    // Be simple and safe
-    aBlitRegion->SetEmpty();
-    *aRepaintRegion = parentBounds;
-  } else {
-    nsresult rv =
-      mObserver->ComputeRepaintRegionForCopy(displayRoot, aView, -aDelta,
-                                             parentBounds,
-                                             aBlitRegion,
-                                             aRepaintRegion);
-    if (NS_FAILED(rv)) {
-      aBlitRegion->SetEmpty();
-      *aRepaintRegion = nsRegion(parentBounds);
-      return;
-    }
-  }
-
-  aBlitRegion->MoveBy(-displayOffset);
-  aRepaintRegion->MoveBy(-displayOffset);
-}
-
 NS_IMETHODIMP nsViewManager::SetViewFloating(nsIView *aView, PRBool aFloating)
 {
   nsView* view = static_cast<nsView*>(aView);
 
   NS_ASSERTION(!(nsnull == view), "no view");
 
   view->SetFloating(aFloating);
 
--- a/view/src/nsViewManager.h
+++ b/view/src/nsViewManager.h
@@ -268,62 +268,20 @@ private:
 
 public: // NOT in nsIViewManager, so private to the view module
   nsView* GetRootView() const { return mRootView; }
   nsViewManager* RootViewManager() const { return mRootViewManager; }
   PRBool IsRootVM() const { return this == RootViewManager(); }
 
   nsEventStatus HandleEvent(nsView* aView, nsPoint aPoint, nsGUIEvent* aEvent);
 
-  /**
-   * Called to inform the view manager that a view is about to bit-blit.
-   * @param aView the view that will bit-blit
-   * @param aScrollAmount how much aView will scroll by
-   * @return always returns NS_OK
-   * @note
-   * This method used to return void, but MSVC 6.0 SP5 (without the
-   * Processor Pack) and SP6, and the MS eMbedded Visual C++ 4.0 SP4
-   * (for WINCE) hit an internal compiler error when compiling this
-   * method:
-   *
-   * @par
-@verbatim
-       fatal error C1001: INTERNAL COMPILER ERROR
-                   (compiler file 'E:\8966\vc98\p2\src\P2\main.c', line 494)
-@endverbatim
-   *
-   * @par
-   * Making the method return nsresult worked around the internal
-   * compiler error.  See Bugzilla bug 281158.  (The WINCE internal
-   * compiler error was addressed by the patch in bug 291229 comment
-   * 14 although the bug report did not mention the problem.)
-   */
-  nsresult WillBitBlit(nsView* aView, nsPoint aScrollAmount);
-  
-  /**
-   * Called to inform the view manager that a view has scrolled via a
-   * bitblit.
-   * The view manager will invalidate any widgets which may need
-   * to be rerendered.
-   * @param aView view to paint. should be the nsScrollPortView that
-   * got scrolled.
-   * @param aBlitRegion the region that was blitted; this is just so
-   * we can notify our view observer
-   * @param aUpdateRegion ensure that this part of the view is repainted
-   */
-  void UpdateViewAfterScroll(nsView *aView, const nsRegion& aBlitRegion,
-                             const nsRegion& aUpdateRegion);
-
-  /**
-   * Given that the view aView has being moved by scrolling by aDelta
-   * (so we want to blit pixels by -aDelta), compute the regions that
-   * must be blitted and repainted to correctly update the screen.
-   */
-  void GetRegionsForBlit(nsView* aView, nsPoint aDelta,
-                         nsRegion* aBlitRegion, nsRegion* aRepaintRegion);
+  virtual nsresult WillBitBlit(nsIView* aView, const nsRect& aRect,
+                               nsPoint aScrollAmount);
+  virtual void UpdateViewAfterScroll(nsIView *aView,
+                                     const nsRegion& aUpdateRegion);
 
   nsresult CreateRegion(nsIRegion* *result);
 
   PRBool IsRefreshEnabled() { return RootViewManager()->mRefreshEnabled; }
 
   nsIViewObserver* GetViewObserver() { return mObserver; }
 
   // Call this when you need to let the viewmanager know that it now has