Bug 620658: Dispatch WILL_PAINT before PAINT to allow scripts etc. to run, which may change the window bounds. r=karl sr=roc a=b
authorChris Jones <jones.chris.g@gmail.com>
Wed, 26 Jan 2011 00:26:37 -0600
changeset 61316 0f592a9724829c19e1b8fecf218c11e3b802ca78
parent 61315 23cfa8965c9e749afda622091c4ee40cee0047ce
child 61317 e0fc18b3bc41ad073fb6bd48891c005b1496a4e2
push idunknown
push userunknown
push dateunknown
reviewerskarl, roc, b
bugs620658
milestone2.0b11pre
Bug 620658: Dispatch WILL_PAINT before PAINT to allow scripts etc. to run, which may change the window bounds. r=karl sr=roc a=b
widget/src/gtk2/nsWindow.cpp
--- a/widget/src/gtk2/nsWindow.cpp
+++ b/widget/src/gtk2/nsWindow.cpp
@@ -122,16 +122,17 @@ static const char sAccessibilityKey [] =
 #include "nsAutoPtr.h"
 
 extern "C" {
 #include "pixman.h"
 }
 #include "gfxPlatformGtk.h"
 #include "gfxContext.h"
 #include "gfxImageSurface.h"
+#include "gfxUtils.h"
 #include "Layers.h"
 #include "LayerManagerOGL.h"
 #include "GLContextProvider.h"
 
 #ifdef MOZ_X11
 #include "gfxXlibSurface.h"
 #endif
 
@@ -2087,29 +2088,46 @@ gdk_window_flash(GdkWindow *    aGdkWind
   gdk_gc_destroy(gc);
 
   gdk_region_offset(aRegion, -x, -y);
 }
 #endif /* MOZ_X11 */
 #endif // DEBUG
 #endif
 
+static void
+DispatchDidPaint(nsIWidget* aWidget)
+{
+    nsEventStatus status;
+    nsPaintEvent didPaintEvent(PR_TRUE, NS_DID_PAINT, aWidget);
+    aWidget->DispatchEvent(&didPaintEvent, status);
+}
+
 gboolean
 nsWindow::OnExposeEvent(GtkWidget *aWidget, GdkEventExpose *aEvent)
 {
     if (mIsDestroyed) {
         LOG(("Expose event on destroyed window [%p] window %p\n",
              (void *)this, (void *)aEvent->window));
         return FALSE;
     }
 
     // Windows that are not visible will be painted after they become visible.
     if (!mGdkWindow || mIsFullyObscured || !mHasMappedToplevel)
         return FALSE;
 
+    // Dispatch WILL_PAINT to allow scripts etc. to run before we
+    // dispatch PAINT
+    {
+        nsEventStatus status;
+        nsPaintEvent willPaintEvent(PR_TRUE, NS_WILL_PAINT, this);
+        willPaintEvent.willSendDidPaint = PR_TRUE;
+        DispatchEvent(&willPaintEvent, status);
+    }
+
     nsPaintEvent event(PR_TRUE, NS_PAINT, this);
     event.refPoint.x = aEvent->area.x;
     event.refPoint.y = aEvent->area.y;
     event.willSendDidPaint = PR_TRUE;
 
     GdkRectangle *rects;
     gint nrects;
     gdk_region_get_rectangles(aEvent->region, &rects, &nrects);
@@ -2128,16 +2146,22 @@ nsWindow::OnExposeEvent(GtkWidget *aWidg
 
     GdkRectangle *r;
     GdkRectangle *r_end = rects + nrects;
     for (r = rects; r < r_end; ++r) {
         event.region.Or(event.region, nsIntRect(r->x, r->y, r->width, r->height));
         LOGDRAW(("\t%d %d %d %d\n", r->x, r->y, r->width, r->height));
     }
 
+    // Our bounds may have changed after dispatching WILL_PAINT.  Clip
+    // to the new bounds here.  The event region is relative to this
+    // window.
+    event.region.And(event.region,
+                     nsIntRect(0, 0, mBounds.width, mBounds.height));
+
     PRBool translucent = eTransparencyTransparent == GetTransparencyMode();
     if (!translucent) {
         GList *children =
             gdk_window_peek_children(mGdkWindow);
         while (children) {
             GdkWindow *gdkWin = GDK_WINDOW(children->data);
             nsWindow *kid = get_window_for_gdk_window(gdkWin);
             if (kid && gdk_window_is_visible(gdkWin)) {
@@ -2161,32 +2185,32 @@ nsWindow::OnExposeEvent(GtkWidget *aWidg
 
     if (GetLayerManager(nsnull)->GetBackendType() == LayerManager::LAYERS_OPENGL)
     {
         LayerManagerOGL *manager = static_cast<LayerManagerOGL*>(GetLayerManager(nsnull));
         manager->SetClippingRegion(event.region);
 
         nsEventStatus status;
         DispatchEvent(&event, status);
+
         g_free(rects);
+
+        DispatchDidPaint(this);
+
         return TRUE;
     }
             
     nsRefPtr<gfxContext> ctx = new gfxContext(GetThebesSurface());
 
 #ifdef MOZ_DFB
     gfxPlatformGtk::SetGdkDrawable(ctx->OriginalSurface(),
                                    GDK_DRAWABLE(mGdkWindow));
 
     // clip to the update region
-    ctx->NewPath();
-    for (r = rects; r < r_end; ++r) {
-        ctx->Rectangle(gfxRect(r->x, r->y, r->width, r->height));
-    }
-    ctx->Clip();
+    gfxUtils::ClipToRegion(ctx, event.region);
 
     BasicLayerManager::BufferMode layerBuffering =
         BasicLayerManager::BUFFER_NONE;
 #endif
 
 #ifdef MOZ_X11
     nsIntRect boundsRect; // for translucent only
 
@@ -2195,19 +2219,17 @@ nsWindow::OnExposeEvent(GtkWidget *aWidg
         // Collapse update area to the bounding box. This is so we only have to
         // call UpdateTranslucentWindowAlpha once. After we have dropped
         // support for non-Thebes graphics, UpdateTranslucentWindowAlpha will be
         // our private interface so we can rework things to avoid this.
         boundsRect = event.region.GetBounds();
         ctx->Rectangle(gfxRect(boundsRect.x, boundsRect.y,
                                boundsRect.width, boundsRect.height));
     } else {
-        for (r = rects; r < r_end; ++r) {
-            ctx->Rectangle(gfxRect(r->x, r->y, r->width, r->height));
-        }
+        gfxUtils::PathFromRegion(ctx, event.region);
     }
     ctx->Clip();
 
     BasicLayerManager::BufferMode layerBuffering;
     if (translucent) {
         // The double buffering is done here to extract the shape mask.
         // (The shape mask won't be necessary when a visual with an alpha
         // channel is used on compositing window managers.)
@@ -2223,17 +2245,18 @@ nsWindow::OnExposeEvent(GtkWidget *aWidg
         layerBuffering = BasicLayerManager::BUFFER_BUFFERED;
     }
 
 #if 0
     // NOTE: Paint flashing region would be wrong for cairo, since
     // cairo inflates the update region, etc.  So don't paint flash
     // for cairo.
 #ifdef DEBUG
-    if (WANT_PAINT_FLASHING && aEvent->window)
+    // XXX aEvent->region may refer to a newly-invalid area.  FIXME
+    if (0 && WANT_PAINT_FLASHING && aEvent->window)
         gdk_window_flash(aEvent->window, 1, 100, aEvent->region);
 #endif
 #endif
 
 #endif // MOZ_X11
 
     nsEventStatus status;
     {
@@ -2276,18 +2299,17 @@ nsWindow::OnExposeEvent(GtkWidget *aWidg
     if (nsShmImage::UseShm() && NS_LIKELY(!mIsDestroyed)) {
         mShmImage->Put(mGdkWindow, rects, r_end);
     }
 #  endif  // MOZ_HAVE_SHMIMAGE
 #endif // MOZ_X11
 
     g_free(rects);
 
-    nsPaintEvent didPaintEvent(PR_TRUE, NS_DID_PAINT, this);
-    DispatchEvent(&didPaintEvent, status);
+    DispatchDidPaint(this);
 
     // Synchronously flush any new dirty areas
     GdkRegion* dirtyArea = gdk_window_get_update_area(mGdkWindow);
     if (dirtyArea) {
         gdk_window_invalidate_region(mGdkWindow, dirtyArea, PR_FALSE);
         gdk_region_destroy(dirtyArea);
         gdk_window_process_updates(mGdkWindow, PR_FALSE);
     }