Bug 598482 part 18 - Separate NS_WILL_PAINT and NS_PAINT handling; only flush again if no NS_WILL_PAINT event has been sent by the platform. r=roc
authorMarkus Stange <mstange@themasta.com>
Fri, 23 Dec 2011 22:52:24 -0500
changeset 85757 05dff2f69c0c8cef1ed9b74438b2fd4b97d5a768
parent 85756 b9e630462207eadd7a097eae127a38f265d1e0d6
child 85758 88b93c35849925f3d5129a4f351a02d361dd98b8
push id805
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 18:17:35 +0000
treeherdermozilla-aurora@6fb3bf232436 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs598482
milestone12.0a1
Bug 598482 part 18 - Separate NS_WILL_PAINT and NS_PAINT handling; only flush again if no NS_WILL_PAINT event has been sent by the platform. r=roc
view/src/nsViewManager.cpp
widget/cocoa/nsChildView.mm
widget/nsGUIEvent.h
widget/windows/nsWindowGfx.cpp
--- a/view/src/nsViewManager.cpp
+++ b/view/src/nsViewManager.cpp
@@ -748,94 +748,84 @@ NS_IMETHODIMP nsViewManager::DispatchEve
               *aStatus = nsEventStatus_eConsumeNoDefault;
             }
           }
         }
       }
       break;
 
     case NS_WILL_PAINT:
-    case NS_PAINT:
       {
-        nsPaintEvent *event = static_cast<nsPaintEvent*>(aEvent);
-
         if (!aView || !mContext)
           break;
 
         *aStatus = nsEventStatus_eConsumeNoDefault;
 
-        if (aEvent->message == NS_PAINT && event->region.IsEmpty())
-          break;
+        nsPaintEvent *event = static_cast<nsPaintEvent*>(aEvent);
 
         NS_ASSERTION(static_cast<nsView*>(aView) ==
                        nsView::GetViewFor(event->widget),
                      "view/widget mismatch");
 
-        // The region is in device units, and it's in the coordinate space of
-        // its associated widget.
-
-        // Refresh the view
-        NS_ASSERTION(IsPaintingAllowed(),
-                     "shouldn't be receiving paint events while painting is "
-                     "disallowed!");
-        nsRefPtr<nsViewManager> rootVM = RootViewManager();
-
         // If an ancestor widget was hidden and then shown, we could
         // have a delayed resize to handle.
-        bool didResize = false;
         for (nsViewManager *vm = this; vm;
              vm = vm->mRootView->GetParent()
                     ? vm->mRootView->GetParent()->GetViewManager()
                     : nsnull) {
           if (vm->mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
               vm->mRootView->IsEffectivelyVisible() &&
               mPresShell && mPresShell->IsVisible()) {
             vm->FlushDelayedResize(true);
-
-            // Paint later.
             vm->UpdateView(vm->mRootView);
-            didResize = true;
-
-            // not sure if it's valid for us to claim that we
-            // ignored this, but we're going to do so anyway, since
-            // we didn't actually paint anything
-            *aStatus = nsEventStatus_eIgnore;
           }
         }
 
-        if (!didResize) {
-          // Notify view observers that we're about to paint.
-          // Make sure to not send WillPaint notifications while scrolling.
+        // Flush things like reflows and plugin widget geometry updates by
+        // calling WillPaint on observer presShells.
+        nsRefPtr<nsViewManager> rootVM = RootViewManager();
+        if (mPresShell) {
+          rootVM->CallWillPaintOnObservers(event->willSendDidPaint);
+        }
+        // Flush view widget geometry updates and invalidations.
+        rootVM->ProcessPendingUpdates();
+      }
+      break;
 
-          nsCOMPtr<nsIWidget> widget;
-          rootVM->GetRootWidget(getter_AddRefs(widget));
-          bool transparentWindow = false;
-          if (widget)
-              transparentWindow = widget->GetTransparencyMode() == eTransparencyTransparent;
+    case NS_PAINT:
+      {
+        if (!aView || !mContext)
+          break;
 
-          nsView* view = static_cast<nsView*>(aView);
-          if (!transparentWindow) {
-            if (mPresShell) {
-              rootVM->CallWillPaintOnObservers(event->willSendDidPaint);
-              // Get the view pointer again since the code above might have
-              // destroyed it (bug 378273).
-              view = nsView::GetViewFor(aEvent->widget);
-            }
-          }
-          // Make sure to sync up any widget geometry changes we
-          // have pending before we paint.
-          if (rootVM->mHasPendingUpdates) {
-            rootVM->ProcessPendingUpdatesForView(mRootView);
-          }
-          
-          if (view && aEvent->message == NS_PAINT) {
-            Refresh(view, event->widget, event->region);
-          }
+        *aStatus = nsEventStatus_eConsumeNoDefault;
+        nsPaintEvent *event = static_cast<nsPaintEvent*>(aEvent);
+        nsView* view = static_cast<nsView*>(aView);
+        NS_ASSERTION(view == nsView::GetViewFor(event->widget),
+                     "view/widget mismatch");
+        NS_ASSERTION(IsPaintingAllowed(),
+                     "shouldn't be receiving paint events while painting is "
+                     "disallowed!");
+
+        if (!event->didSendWillPaint) {
+          // Send NS_WILL_PAINT event ourselves.
+          nsPaintEvent willPaintEvent(true, NS_WILL_PAINT, event->widget);
+          willPaintEvent.willSendDidPaint = event->willSendDidPaint;
+          DispatchEvent(&willPaintEvent, view, aStatus);
+
+          // Get the view pointer again since NS_WILL_PAINT might have
+          // destroyed it during CallWillPaintOnObservers (bug 378273).
+          view = nsView::GetViewFor(event->widget);
         }
 
+        if (!view || event->region.IsEmpty())
+          break;
+
+        // Paint.
+        Refresh(view, event->widget, event->region);
+
         break;
       }
 
     case NS_DID_PAINT: {
       nsRefPtr<nsViewManager> rootVM = RootViewManager();
       rootVM->CallDidPaintOnObservers();
       break;
     }
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -2503,16 +2503,17 @@ NSEvent* gLastDragMouseDownEvent = nil;
            aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height, aContext,
            geckoBounds.x, geckoBounds.y, geckoBounds.width, geckoBounds.height);
 
   CGAffineTransform xform = CGContextGetCTM(aContext);
   fprintf (stderr, "  xform in: [%f %f %f %f %f %f]\n", xform.a, xform.b, xform.c, xform.d, xform.tx, xform.ty);
 #endif
   // Create the event so we can fill in its region
   nsPaintEvent paintEvent(true, NS_PAINT, mGeckoChild);
+  paintEvent.didSendWillPaint = true;
 
   nsIntRect boundingRect =
     nsIntRect(aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height);
   const NSRect *rects;
   NSInteger count, i;
   [[NSView focusView] getRectsBeingDrawn:&rects count:&count];
   if (count < MAX_RECTS_IN_REGION) {
     for (i = 0; i < count; ++i) {
--- a/widget/nsGUIEvent.h
+++ b/widget/nsGUIEvent.h
@@ -732,23 +732,25 @@ public:
  * Window repaint event
  */
 
 class nsPaintEvent : public nsGUIEvent
 {
 public:
   nsPaintEvent(bool isTrusted, PRUint32 msg, nsIWidget *w)
     : nsGUIEvent(isTrusted, msg, w, NS_PAINT_EVENT),
-      willSendDidPaint(false)
+      willSendDidPaint(false),
+      didSendWillPaint(false)
   {
   }
 
   // area that needs repainting
   nsIntRegion region;
   bool willSendDidPaint;
+  bool didSendWillPaint;
 };
 
 /**
  * Scrollbar event
  */
 
 class nsScrollbarEvent : public nsGUIEvent
 {
--- a/widget/windows/nsWindowGfx.cpp
+++ b/widget/windows/nsWindowGfx.cpp
@@ -307,16 +307,17 @@ bool nsWindow::OnPaint(HDC aDC, PRUint32
 
 #ifdef MOZ_XUL
   bool forceRepaint = aDC || (eTransparencyTransparent == mTransparencyMode);
 #else
   bool forceRepaint = NULL != aDC;
 #endif
   event.region = GetRegionToPaint(forceRepaint, ps, hDC);
   event.willSendDidPaint = true;
+  event.didSendWillPaint = true;
 
   if (!event.region.IsEmpty() && mEventCallback)
   {
     // Should probably pass in a real region here, using GetRandomRgn
     // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/clipping_4q0e.asp
 
 #ifdef WIDGET_DEBUG_OUTPUT
     debug_DumpPaintEvent(stdout,