bug 529092 - seams when scrolling with position:fixed [MS Windows] r=jmathies
authorRobert O'Callahan <robert@ocallahan.org>
Sun, 29 Nov 2009 23:21:02 -0500
changeset 35277 c3514f592332bafbbe16112ceb24f1b9c4d12cbb
parent 35276 091a108677adf16e9b0bb2e3f52c0ae7bd356a13
child 35278 aabf9be2bb7d025e30fab852ba876975415722ca
push idunknown
push userunknown
push dateunknown
reviewersjmathies
bugs529092
milestone1.9.3a1pre
bug 529092 - seams when scrolling with position:fixed [MS Windows] r=jmathies
widget/src/windows/nsWindow.cpp
--- a/widget/src/windows/nsWindow.cpp
+++ b/widget/src/windows/nsWindow.cpp
@@ -2246,24 +2246,36 @@ nsWindow::Scroll(const nsIntPoint& aDelt
     NS_ASSERTION(w->GetParent() == this,
                  "Configured widget is not a child");
     if (configuration.mBounds == w->mBounds + aDelta) {
       scrolledWidgets.PutEntry(w);
     }
     w->SetWindowClipRegion(configuration.mClipRegion, PR_TRUE);
   }
 
+  // Create temporary regions
+  HRGN updateRgn = ::CreateRectRgn(0, 0, 0, 0);
+  if (!updateRgn) {
+    // OOM?
+    return;
+  }
+  HRGN destRgn = ::CreateRectRgn(0, 0, 0, 0);
+  if (!destRgn) {
+    // OOM?
+    ::DeleteObject((HGDIOBJ)updateRgn);
+    return;
+  }
+
   DWORD ourThreadID = GetWindowThreadProcessId(mWnd, NULL);
 
   for (PRUint32 i = 0; i < aDestRects.Length(); ++i) {
+    const nsIntRect& destRect = aDestRects[i];
     nsIntRect affectedRect;
-    affectedRect.UnionRect(aDestRects[i], aDestRects[i] - aDelta);
-    // We pass SW_INVALIDATE because areas that get scrolled into view
-    // from offscreen (but inside the scroll area) need to be repainted.
-    UINT flags = SW_SCROLLCHILDREN | SW_INVALIDATE;
+    affectedRect.UnionRect(destRect, destRect - aDelta);
+    UINT flags = SW_SCROLLCHILDREN;
     // Now check if any of our children would be affected by
     // SW_SCROLLCHILDREN but not supposed to scroll.
     for (nsWindow* w = static_cast<nsWindow*>(GetFirstChild()); w;
          w = static_cast<nsWindow*>(w->GetNextSibling())) {
       if (w->mBounds.Intersects(affectedRect)) {
         // This child will be affected
         nsPtrHashKey<nsWindow>* entry = scrolledWidgets.GetEntry(w);
         if (entry) {
@@ -2326,18 +2338,40 @@ nsWindow::Scroll(const nsIntPoint& aDelt
            w = static_cast<nsWindow*>(w->GetNextSibling())) {
         if (w->mBounds.Intersects(affectedRect)) {
           w->mBounds += aDelta;
         }
       }
     }
 
     RECT clip = { affectedRect.x, affectedRect.y, affectedRect.XMost(), affectedRect.YMost() };
-    ::ScrollWindowEx(mWnd, aDelta.x, aDelta.y, &clip, &clip, NULL, NULL, flags);
-  }
+    ::ScrollWindowEx(mWnd, aDelta.x, aDelta.y, &clip, &clip, updateRgn, NULL, flags);
+
+    // Areas that get scrolled into view from offscreen or under another
+    // window (but inside the scroll area) need to be repainted.
+    // ScrollWindowEx returns those areas in updateRgn, but also includes
+    // the area of the source that isn't covered by the destination.
+    // ScrollWindowEx will refuse to blit into an area that's already
+    // invalid. When we're blitting multiple adjacent rectangles, we have
+    // a problem where the source area of rectangle A overlaps the
+    // destination area of a subsequent rectangle B; the first ScrollWindowEx
+    // invalidates the source area of A that's outside of the destination
+    // area of A, and then the ScrollWindowEx for B will refuse to fully
+    // blit into B's destination. This produces nasty temporary glitches.
+    // We combat this by having ScrollWindowEx not invalidate directly,
+    // but give us back the region that needs to be invalidated,
+    // and restricting that region to the destination before invalidating
+    // it.
+    ::SetRectRgn(destRgn, destRect.x, destRect.y, destRect.XMost(), destRect.YMost());
+    ::CombineRgn(updateRgn, updateRgn, destRgn, RGN_AND);
+    ::InvalidateRgn(mWnd, updateRgn, FALSE);
+  }
+
+  ::DeleteObject((HGDIOBJ)updateRgn);
+  ::DeleteObject((HGDIOBJ)destRgn);
 
   // Now make sure all children actually get positioned, sized and clipped
   // correctly. If SW_SCROLLCHILDREN already moved widgets to their correct
   // locations, then the SetWindowPos calls this triggers will just be
   // no-ops.
   ConfigureChildren(aConfigurations);
 }