Bug 1127752 - fixes for using Skia and OMTC with GTK3. r=jrmuizel
authorLee Salzman <lsalzman@mozilla.com>
Fri, 01 May 2015 14:08:04 -0400
changeset 263166 8538bc4d2cbd90499cbd17dd12b608b5025acbf6
parent 263165 e0faf96523b2322f52c94edf2f01298c93656725
child 263167 1663d6ff4ae055c4517ac636e26b0b2e566fcf3d
push id8157
push userjlund@mozilla.com
push dateMon, 29 Jun 2015 20:36:23 +0000
treeherdermozilla-aurora@d480e05bd276 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1127752
milestone41.0a1
Bug 1127752 - fixes for using Skia and OMTC with GTK3. r=jrmuizel
gfx/2d/BorrowedContext.h
gfx/2d/DrawTargetCairo.cpp
gfx/2d/DrawTargetCairo.h
gfx/2d/DrawTargetSkia.cpp
gfx/2d/DrawTargetSkia.h
gfx/2d/moz.build
gfx/layers/basic/BasicCompositor.cpp
gfx/src/nsRegion.h
gfx/thebes/gfxContext.cpp
gfx/thebes/gfxContext.h
widget/gtk/nsNativeThemeGTK.cpp
widget/gtk/nsScreenGtk.cpp
widget/gtk/nsWindow.cpp
widget/gtk/nsWindow.h
widget/nsIWidget.h
widget/nsShmImage.cpp
widget/nsShmImage.h
--- a/gfx/2d/BorrowedContext.h
+++ b/gfx/2d/BorrowedContext.h
@@ -3,16 +3,21 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef _MOZILLA_GFX_BORROWED_CONTEXT_H
 #define _MOZILLA_GFX_BORROWED_CONTEXT_H
 
 #include "2D.h"
 
+#ifdef MOZ_X11
+#include <X11/extensions/Xrender.h>
+#include <X11/Xlib.h>
+#endif
+
 struct _cairo;
 typedef struct _cairo cairo_t;
 
 namespace mozilla {
 
 namespace gfx {
 
 /* This is a helper class that let's you borrow a cairo_t from a
@@ -64,16 +69,79 @@ public:
 
   cairo_t *mCairo;
 private:
   static cairo_t* BorrowCairoContextFromDrawTarget(DrawTarget *aDT);
   static void ReturnCairoContextToDrawTarget(DrawTarget *aDT, cairo_t *aCairo);
   DrawTarget *mDT;
 };
 
+#ifdef MOZ_X11
+/* This is a helper class that let's you borrow an Xlib drawable from
+ * a DrawTarget. This is used for drawing themed widgets.
+ *
+ * Callers should check the Xlib drawable after constructing the object
+ * to see if it succeeded. The DrawTarget should not be used while
+ * the drawable is borrowed. */
+class BorrowedXlibDrawable
+{
+public:
+  BorrowedXlibDrawable()
+    : mDT(nullptr),
+      mDisplay(nullptr),
+      mDrawable(None),
+      mScreen(nullptr),
+      mVisual(nullptr),
+      mXRenderFormat(nullptr)
+  {}
+
+  explicit BorrowedXlibDrawable(DrawTarget *aDT)
+    : mDT(nullptr),
+      mDisplay(nullptr),
+      mDrawable(None),
+      mScreen(nullptr),
+      mVisual(nullptr),
+      mXRenderFormat(nullptr)
+  {
+    Init(aDT);
+  }
+
+  // We can optionally Init after construction in
+  // case we don't know what the DT will be at construction
+  // time.
+  bool Init(DrawTarget *aDT);
+
+  // The caller needs to call Finish if drawable is non-zero when
+  // they are done with the context. This is currently explicit
+  // instead of happening implicitly in the destructor to make
+  // what's happening in the caller more clear. It also
+  // let's you resume using the DrawTarget in the same scope.
+  void Finish();
+
+  ~BorrowedXlibDrawable() {
+    MOZ_ASSERT(!mDrawable);
+  }
+
+  Display *GetDisplay() const { return mDisplay; }
+  Drawable GetDrawable() const { return mDrawable; }
+  Screen *GetScreen() const { return mScreen; }
+  Visual *GetVisual() const { return mVisual; }
+
+  XRenderPictFormat* GetXRenderFormat() const { return mXRenderFormat; }
+
+private:
+  DrawTarget *mDT;
+  Display *mDisplay;
+  Drawable mDrawable;
+  Screen *mScreen;
+  Visual *mVisual;
+  XRenderPictFormat *mXRenderFormat;
+};
+#endif
+
 #ifdef XP_MACOSX
 /* This is a helper class that let's you borrow a CGContextRef from a
  * DrawTargetCG. This is used for drawing themed widgets.
  *
  * Callers should check the cg member after constructing the object
  * to see if it succeeded. The DrawTarget should not be used while
  * the context is borrowed. */
 class BorrowedCGContext
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -1709,10 +1709,55 @@ BorrowedCairoContext::ReturnCairoContext
     return;
   }
   DrawTargetCairo* cairoDT = static_cast<DrawTargetCairo*>(aDT);
 
   cairo_restore(aCairo);
   cairoDT->mContext = aCairo;
 }
 
+#ifdef MOZ_X11
+bool
+BorrowedXlibDrawable::Init(DrawTarget* aDT)
+{
+  MOZ_ASSERT(aDT, "Caller should check for nullptr");
+  MOZ_ASSERT(!mDT, "Can't initialize twice!");
+  mDT = aDT;
+  mDrawable = None;
+
+#ifdef CAIRO_HAS_XLIB_SURFACE
+  if (aDT->GetBackendType() != BackendType::CAIRO ||
+      aDT->IsDualDrawTarget() ||
+      aDT->IsTiledDrawTarget()) {
+    return false;
+  }
+
+  DrawTargetCairo* cairoDT = static_cast<DrawTargetCairo*>(aDT);
+  cairo_surface_t* surf = cairoDT->mSurface;
+  if (cairo_surface_get_type(surf) != CAIRO_SURFACE_TYPE_XLIB) {
+    return false;
+  }
+
+  cairoDT->WillChange();
+
+  mDisplay = cairo_xlib_surface_get_display(surf);
+  mDrawable = cairo_xlib_surface_get_drawable(surf);
+  mScreen = cairo_xlib_surface_get_screen(surf);
+  mVisual = cairo_xlib_surface_get_visual(surf);
+  mXRenderFormat = cairo_xlib_surface_get_xrender_format(surf);
+
+  return true;
+#else
+  return false;
+#endif
+}
+
+void
+BorrowedXlibDrawable::Finish()
+{
+  if (mDrawable) {
+    mDrawable = None;
+  }
+}
+#endif
+
 }
 }
--- a/gfx/2d/DrawTargetCairo.h
+++ b/gfx/2d/DrawTargetCairo.h
@@ -49,16 +49,17 @@ class GradientStopsCairo : public Gradie
     ExtendMode mExtendMode;
 };
 
 class DrawTargetCairo final : public DrawTarget
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetCairo, override)
   friend class BorrowedCairoContext;
+  friend class BorrowedXlibDrawable;
 
   DrawTargetCairo();
   virtual ~DrawTargetCairo();
 
   virtual DrawTargetType GetType() const override;
   virtual BackendType GetBackendType() const override { return BackendType::CAIRO; }
   virtual TemporaryRef<SourceSurface> Snapshot() override;
   virtual IntSize GetSize() override;
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -143,16 +143,45 @@ DrawTargetSkia::Snapshot()
     mSnapshot = snapshot;
     if (!snapshot->InitFromCanvas(mCanvas.get(), mFormat, this))
       return nullptr;
   }
 
   return snapshot.forget();
 }
 
+bool
+DrawTargetSkia::LockBits(uint8_t** aData, IntSize* aSize,
+                          int32_t* aStride, SurfaceFormat* aFormat)
+{
+  const SkBitmap &bitmap = mCanvas->getDevice()->accessBitmap(false);
+  if (!bitmap.lockPixelsAreWritable()) {
+    return false;
+  }
+
+  MarkChanged();
+
+  bitmap.lockPixels();
+  *aData = reinterpret_cast<uint8_t*>(bitmap.getPixels());
+  *aSize = IntSize(bitmap.width(), bitmap.height());
+  *aStride = int32_t(bitmap.rowBytes());
+  *aFormat = SkiaColorTypeToGfxFormat(bitmap.colorType());
+  return true;
+}
+
+void
+DrawTargetSkia::ReleaseBits(uint8_t* aData)
+{
+  const SkBitmap &bitmap = mCanvas->getDevice()->accessBitmap(false);
+  MOZ_ASSERT(bitmap.lockPixelsAreWritable());
+
+  bitmap.unlockPixels();
+  bitmap.notifyPixelsChanged();
+}
+
 static void
 SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, TempBitmap& aTmpBitmap,
                 Float aAlpha = 1.0)
 {
   switch (aPattern.GetType()) {
     case PatternType::COLOR: {
       Color color = static_cast<const ColorPattern&>(aPattern).mColor;
       aPaint.setColor(ColorToSkColor(color, aAlpha));
@@ -683,20 +712,20 @@ DrawTargetSkia::CreateSourceSurfaceFromN
     if (aSurface.mSize.width <= 0 ||
         aSurface.mSize.height <= 0) {
       gfxWarning() << "Can't create a SourceSurface without a valid size";
       return nullptr;
     }
     cairo_surface_t* surf = static_cast<cairo_surface_t*>(aSurface.mSurface);
     return new SourceSurfaceCairo(surf, aSurface.mSize, aSurface.mFormat);
 #if USE_SKIA_GPU
-  } else if (aSurface.mType == NativeSurfaceType::OPENGL_TEXTURE) {
+  } else if (aSurface.mType == NativeSurfaceType::OPENGL_TEXTURE && UsingSkiaGPU()) {
     RefPtr<SourceSurfaceSkia> newSurf = new SourceSurfaceSkia();
     unsigned int texture = (unsigned int)((uintptr_t)aSurface.mSurface);
-    if (UsingSkiaGPU() && newSurf->InitFromTexture((DrawTargetSkia*)this, texture, aSurface.mSize, aSurface.mFormat)) {
+    if (newSurf->InitFromTexture((DrawTargetSkia*)this, texture, aSurface.mSize, aSurface.mFormat)) {
       return newSurf;
     }
     return nullptr;
 #endif
   }
 
   return nullptr;
 }
--- a/gfx/2d/DrawTargetSkia.h
+++ b/gfx/2d/DrawTargetSkia.h
@@ -31,16 +31,19 @@ public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetSkia, override)
   DrawTargetSkia();
   virtual ~DrawTargetSkia();
 
   virtual DrawTargetType GetType() const override;
   virtual BackendType GetBackendType() const override { return BackendType::SKIA; }
   virtual TemporaryRef<SourceSurface> Snapshot() override;
   virtual IntSize GetSize() override { return mSize; }
+  virtual bool LockBits(uint8_t** aData, IntSize* aSize,
+                        int32_t* aStride, SurfaceFormat* aFormat) override;
+  virtual void ReleaseBits(uint8_t* aData) override;
   virtual void Flush() override;
   virtual void DrawSurface(SourceSurface *aSurface,
                            const Rect &aDest,
                            const Rect &aSource,
                            const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(),
                            const DrawOptions &aOptions = DrawOptions()) override;
   virtual void DrawFilter(FilterNode *aNode,
                           const Rect &aSourceRect,
--- a/gfx/2d/moz.build
+++ b/gfx/2d/moz.build
@@ -74,16 +74,17 @@ if CONFIG['MOZ_ENABLE_SKIA']:
         'DrawTargetSkia.cpp',
         'PathSkia.cpp',
         'SourceSurfaceSkia.cpp',
     ]
     SOURCES += [
         'image_operations.cpp', # Uses _USE_MATH_DEFINES
     ]
     EXPORTS.mozilla.gfx += [
+        'HelpersCairo.h',
         'HelpersSkia.h',
     ]
 
 # Are we targeting x86 or x64?  If so, build SSE2 files.
 if CONFIG['INTEL_ARCHITECTURE']:
     SOURCES += [
         'BlurSSE2.cpp',
         'FilterProcessingSSE2.cpp',
--- a/gfx/layers/basic/BasicCompositor.cpp
+++ b/gfx/layers/basic/BasicCompositor.cpp
@@ -512,17 +512,17 @@ BasicCompositor::BeginFrame(const nsIntR
     return;
   }
 
   // Setup an intermediate render target to buffer all compositing. We will
   // copy this into mDrawTarget (the widget), and/or mTarget in EndFrame()
   RefPtr<CompositingRenderTarget> target = CreateRenderTarget(mInvalidRect, INIT_MODE_CLEAR);
   if (!target) {
     if (!mTarget) {
-      mWidget->EndRemoteDrawing();
+      mWidget->EndRemoteDrawingInRegion(mDrawTarget, mInvalidRegion);
     }
     return;
   }
   SetRenderTarget(target);
 
   // We only allocate a surface sized to the invalidated region, so we need to
   // translate future coordinates.
   mRenderTarget->mDrawTarget->SetTransform(Matrix::Translation(-invalidRect.x,
@@ -574,17 +574,17 @@ BasicCompositor::EndFrame()
   // pixels.
   nsIntRegionRectIterator iter(mInvalidRegion);
   for (const IntRect *r = iter.Next(); r; r = iter.Next()) {
     dest->CopySurface(source,
                       IntRect(r->x - mInvalidRect.x, r->y - mInvalidRect.y, r->width, r->height),
                       IntPoint(r->x - offset.x, r->y - offset.y));
   }
   if (!mTarget) {
-    mWidget->EndRemoteDrawing();
+    mWidget->EndRemoteDrawingInRegion(mDrawTarget, mInvalidRegion);
   }
 
   mDrawTarget = nullptr;
   mRenderTarget = nullptr;
 }
 
 }
 }
--- a/gfx/src/nsRegion.h
+++ b/gfx/src/nsRegion.h
@@ -710,16 +710,22 @@ public:
   }
 
   Derived& ScaleRoundOut (float aXScale, float aYScale)
   {
     mImpl.ScaleRoundOut(aXScale, aYScale);
     return This();
   }
 
+  Derived& ScaleInverseRoundOut (float aXScale, float aYScale)
+  {
+    mImpl.ScaleInverseRoundOut(aXScale, aYScale);
+    return This();
+  }
+
   Derived& Transform (const gfx3DMatrix &aTransform)
   {
     mImpl.Transform(aTransform);
     return This();
   }
 
   /**
    * Make sure the region has at most aMaxRects by adding area to it
--- a/gfx/thebes/gfxContext.cpp
+++ b/gfx/thebes/gfxContext.cpp
@@ -614,16 +614,33 @@ gfxContext::GetClipExtents()
   Matrix mat = mTransform;
   mat.Invert();
   rect = mat.TransformBounds(rect);
 
   return ThebesRect(rect);
 }
 
 bool
+gfxContext::HasComplexClip() const
+{
+  for (int i = mStateStack.Length() - 1; i >= 0; i--) {
+    for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
+      const AzureState::PushedClip &clip = mStateStack[i].pushedClips[c];
+      if (clip.path || !clip.transform.IsRectilinear()) {
+        return true;
+      }
+    }
+    if (mStateStack[i].clipWasReset) {
+      break;
+    }
+  }
+  return false;
+}
+
+bool
 gfxContext::ClipContainsRect(const gfxRect& aRect)
 {
   unsigned int lastReset = 0;
   for (int i = mStateStack.Length() - 2; i > 0; i--) {
     if (mStateStack[i].clipWasReset) {
       lastReset = i;
       break;
     }
--- a/gfx/thebes/gfxContext.h
+++ b/gfx/thebes/gfxContext.h
@@ -441,16 +441,21 @@ public:
 
     /**
      * This will return the current bounds of the clip region in user
      * space.
      */
     gfxRect GetClipExtents();
 
     /**
+     * Whether the current clip is not a simple rectangle.
+     */
+    bool HasComplexClip() const;
+
+    /**
      * Returns true if the given rectangle is fully contained in the current clip. 
      * This is conservative; it may return false even when the given rectangle is 
      * fully contained by the current clip.
      */
     bool ClipContainsRect(const gfxRect& aRect);
 
     /**
      * Groups
--- a/widget/gtk/nsNativeThemeGTK.cpp
+++ b/widget/gtk/nsNativeThemeGTK.cpp
@@ -28,16 +28,28 @@
 #include "mozilla/Services.h"
 
 #include <gdk/gdkprivate.h>
 #include <gtk/gtk.h>
 
 #include "gfxContext.h"
 #include "gfxPlatformGtk.h"
 #include "gfxGdkNativeRenderer.h"
+#include "mozilla/gfx/BorrowedContext.h"
+#include "mozilla/gfx/HelpersCairo.h"
+
+#ifdef MOZ_X11
+#  ifdef CAIRO_HAS_XLIB_SURFACE
+#    include "cairo-xlib.h"
+#  endif
+#  ifdef CAIRO_HAS_XLIB_XRENDER_SURFACE
+#    include "cairo-xlib-xrender.h"
+#  endif
+#endif
+
 #include <algorithm>
 #include <dlfcn.h>
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 
 NS_IMPL_ISUPPORTS_INHERITED(nsNativeThemeGTK, nsNativeTheme, nsITheme,
                                                              nsIObserver)
@@ -697,16 +709,168 @@ ThemeRenderer::DrawWithGDK(GdkDrawable *
   gdk_rectangle_intersect(&gdk_clip, &surfaceRect, &gdk_clip);
   
   NS_ASSERTION(numClipRects == 0, "We don't support clipping!!!");
   moz_gtk_widget_paint(mGTKWidgetType, drawable, &gdk_rect, &gdk_clip,
                        &mState, mFlags, mDirection);
 
   return NS_OK;
 }
+#else
+static void
+DrawThemeWithCairo(gfxContext* aContext, DrawTarget* aDrawTarget,
+                   GtkWidgetState aState, GtkThemeWidgetType aGTKWidgetType,
+                   gint aFlags, GtkTextDirection aDirection, gint aScaleFactor,
+                   bool aSnapped, const Point& aDrawOrigin, const nsIntSize& aDrawSize,
+                   GdkRectangle& aGDKRect, nsITheme::Transparency aTransparency)
+{
+#ifndef MOZ_TREE_CAIRO
+  // Directly use the Cairo draw target to render the widget if using system Cairo everywhere.
+  BorrowedCairoContext borrow(aDrawTarget);
+  if (borrow.mCairo) {
+    if (aSnapped) {
+      cairo_identity_matrix(borrow.mCairo);
+    }
+    if (aDrawOrigin != Point(0, 0)) {
+      cairo_translate(borrow.mCairo, aDrawOrigin.x, aDrawOrigin.y);
+    }
+    if (aScaleFactor != 1) {
+      cairo_scale(borrow.mCairo, aScaleFactor, aScaleFactor);
+    }
+
+    moz_gtk_widget_paint(aGTKWidgetType, borrow.mCairo, &aGDKRect, &aState, aFlags, aDirection);
+
+    borrow.Finish();
+    return;
+  }
+#endif
+
+  // A direct Cairo draw target is not available, so we need to create a temporary one.
+  bool needClip = !aSnapped || aContext->HasComplexClip();
+#if defined(MOZ_X11) && defined(CAIRO_HAS_XLIB_SURFACE)
+  if (!needClip) {
+    // If using a Cairo xlib surface, then try to reuse it.
+    BorrowedXlibDrawable borrow(aDrawTarget);
+    if (borrow.GetDrawable()) {
+      nsIntSize size = aDrawTarget->GetSize();
+      cairo_surface_t* surf = nullptr;
+      // Check if the surface is using XRender.
+#ifdef CAIRO_HAS_XLIB_XRENDER_SURFACE
+      if (borrow.GetXRenderFormat()) {
+        surf = cairo_xlib_surface_create_with_xrender_format(
+          borrow.GetDisplay(), borrow.GetDrawable(), borrow.GetScreen(),
+          borrow.GetXRenderFormat(), size.width, size.height);
+      } else {
+#else
+      if (! borrow.GetXRenderFormat()) {
+#endif
+        surf = cairo_xlib_surface_create(
+          borrow.GetDisplay(), borrow.GetDrawable(), borrow.GetVisual(),
+          size.width, size.height);
+      }
+      if (!NS_WARN_IF(!surf)) {
+        cairo_t* cr = cairo_create(surf);
+        if (!NS_WARN_IF(!cr)) {
+          cairo_new_path(cr);
+          cairo_rectangle(cr, aDrawOrigin.x, aDrawOrigin.y, aDrawSize.width, aDrawSize.height);
+          cairo_clip(cr);
+          if (aDrawOrigin != Point(0, 0)) {
+            cairo_translate(cr, aDrawOrigin.x, aDrawOrigin.y);
+          }
+          if (aScaleFactor != 1) {
+            cairo_scale(cr, aScaleFactor, aScaleFactor);
+          }
+
+          moz_gtk_widget_paint(aGTKWidgetType, cr, &aGDKRect, &aState, aFlags, aDirection);
+
+          cairo_destroy(cr);
+        }
+        cairo_surface_destroy(surf);
+      }
+      borrow.Finish();
+      return;
+    }
+  }
+#endif
+
+  // Check if the widget requires complex masking that must be composited.
+  // Try to directly write to the draw target's pixels if possible.
+  uint8_t* data;
+  nsIntSize size;
+  int32_t stride;
+  SurfaceFormat format;
+  if (!needClip && aDrawTarget->LockBits(&data, &size, &stride, &format)) {
+    // Create a Cairo image surface context the device rectangle.
+    cairo_surface_t* surf =
+      cairo_image_surface_create_for_data(
+        data + int32_t(aDrawOrigin.y) * stride + int32_t(aDrawOrigin.x) * BytesPerPixel(format),
+        GfxFormatToCairoFormat(format), aDrawSize.width, aDrawSize.height, stride);
+    if (!NS_WARN_IF(!surf)) {
+      cairo_t* cr = cairo_create(surf);
+      if (!NS_WARN_IF(!cr)) {
+        if (aScaleFactor != 1) {
+          cairo_scale(cr, aScaleFactor, aScaleFactor);
+        }
+
+        moz_gtk_widget_paint(aGTKWidgetType, cr, &aGDKRect, &aState, aFlags, aDirection);
+
+        cairo_destroy(cr);
+      }
+      cairo_surface_destroy(surf);
+    }
+    aDrawTarget->ReleaseBits(data);
+  } else {
+    // If the widget has any transparency, make sure to choose an alpha format.
+    format = aTransparency != nsITheme::eOpaque ? SurfaceFormat::B8G8R8A8 : aDrawTarget->GetFormat();
+    // Create a temporary data surface to render the widget into.
+    RefPtr<DataSourceSurface> dataSurface =
+      Factory::CreateDataSourceSurface(aDrawSize, format, aTransparency != nsITheme::eOpaque);
+    DataSourceSurface::MappedSurface map;
+    if (!NS_WARN_IF(!(dataSurface && dataSurface->Map(DataSourceSurface::MapType::WRITE, &map)))) {
+      // Create a Cairo image surface wrapping the data surface.
+      cairo_surface_t* surf =
+        cairo_image_surface_create_for_data(map.mData, GfxFormatToCairoFormat(format),
+                                            aDrawSize.width, aDrawSize.height, map.mStride);
+      cairo_t* cr = nullptr;
+      if (!NS_WARN_IF(!surf)) {
+        cr = cairo_create(surf);
+        if (!NS_WARN_IF(!cr)) {
+          if (aScaleFactor != 1) {
+            cairo_scale(cr, aScaleFactor, aScaleFactor);
+          }
+
+          moz_gtk_widget_paint(aGTKWidgetType, cr, &aGDKRect, &aState, aFlags, aDirection);
+        }
+      }
+
+      // Unmap the surface before using it as a source
+      dataSurface->Unmap();
+
+      if (cr) {
+        if (needClip || aTransparency != nsITheme::eOpaque) {
+          // The widget either needs to be masked or has transparency, so use the slower drawing path.
+          aDrawTarget->DrawSurface(dataSurface,
+                                   Rect(aDrawOrigin, Size(aDrawSize)),
+                                   Rect(0, 0, aDrawSize.width, aDrawSize.height));
+        } else {
+          // The widget is a simple opaque rectangle, so just copy it out.
+          aDrawTarget->CopySurface(dataSurface,
+                                   IntRect(0, 0, aDrawSize.width, aDrawSize.height),
+                                   TruncatedToInt(aDrawOrigin));
+        }
+
+        cairo_destroy(cr);
+      }
+
+      if (surf) {
+        cairo_surface_destroy(surf);
+      }
+    }
+  }
+}
 #endif
 
 bool
 nsNativeThemeGTK::GetExtraSizeForWidget(nsIFrame* aFrame, uint8_t aWidgetType,
                                         nsIntMargin* aExtra)
 {
   *aExtra = nsIntMargin(0,0,0,0);
   // Allow an extra one pixel above and below the thumb for certain
@@ -791,20 +955,16 @@ nsNativeThemeGTK::GetExtraSizeForWidget(
 
 NS_IMETHODIMP
 nsNativeThemeGTK::DrawWidgetBackground(nsRenderingContext* aContext,
                                        nsIFrame* aFrame,
                                        uint8_t aWidgetType,
                                        const nsRect& aRect,
                                        const nsRect& aDirtyRect)
 {
-#if (MOZ_WIDGET_GTK != 2)
-  DrawTarget& aDrawTarget = *aContext->GetDrawTarget();
-#endif
-
   GtkWidgetState state;
   GtkThemeWidgetType gtkWidgetType;
   GtkTextDirection direction = GetTextDirection(aFrame);
   gint flags;
   if (!GetGtkWidgetAndState(aWidgetType, aFrame, gtkWidgetType, &state,
                             &flags))
     return NS_OK;
 
@@ -814,18 +974,18 @@ nsNativeThemeGTK::DrawWidgetBackground(n
   gfxRect rect = presContext->AppUnitsToGfxUnits(aRect);
   gfxRect dirtyRect = presContext->AppUnitsToGfxUnits(aDirtyRect);
   gint scaleFactor = nsScreenGtk::GetGtkMonitorScaleFactor();
 
   // Align to device pixels where sensible
   // to provide crisper and faster drawing.
   // Don't snap if it's a non-unit scale factor. We're going to have to take
   // slow paths then in any case.
-  bool snapXY = ctx->UserToDevicePixelSnapped(rect);
-  if (snapXY) {
+  bool snapped = ctx->UserToDevicePixelSnapped(rect);
+  if (snapped) {
     // Leave rect in device coords but make dirtyRect consistent.
     dirtyRect = ctx->UserToDevice(dirtyRect);
   }
 
   // Translate the dirty rect so that it is wrt the widget top-left.
   dirtyRect.MoveBy(-rect.TopLeft());
   // Round out the dirty rect to gdk pixels to ensure that gtk draws
   // enough pixels for interpolation to device pixels.
@@ -844,69 +1004,71 @@ nsNativeThemeGTK::DrawWidgetBackground(n
   nsIntRect drawingRect(int32_t(dirtyRect.X()),
                         int32_t(dirtyRect.Y()),
                         int32_t(dirtyRect.Width()),
                         int32_t(dirtyRect.Height()));
   if (widgetRect.IsEmpty()
       || !drawingRect.IntersectRect(overflowRect, drawingRect))
     return NS_OK;
 
-  // gdk rectangles are wrt the drawing rect.
-
-  GdkRectangle gdk_rect = {-drawingRect.x/scaleFactor,
-                           -drawingRect.y/scaleFactor,
-                           widgetRect.width/scaleFactor,
-                           widgetRect.height/scaleFactor};
-
-  // translate everything so (0,0) is the top left of the drawingRect
-  gfxContextAutoSaveRestore autoSR(ctx);
-  gfxMatrix tm;
-  if (!snapXY) { // else rects are in device coords
-    tm = ctx->CurrentMatrix();
-  }
-  tm.Translate(rect.TopLeft() + gfxPoint(drawingRect.x, drawingRect.y));
-  tm.Scale(scaleFactor, scaleFactor); // Draw in GDK coords
-  ctx->SetMatrix(tm);
-
   NS_ASSERTION(!IsWidgetTypeDisabled(mDisabledWidgetTypes, aWidgetType),
                "Trying to render an unsafe widget!");
 
   bool safeState = IsWidgetStateSafe(mSafeWidgetStates, aWidgetType, &state);
   if (!safeState) {
     gLastGdkError = 0;
     gdk_error_trap_push ();
   }
 
+  Transparency transparency = GetWidgetTransparency(aFrame, aWidgetType);
+
+  // gdk rectangles are wrt the drawing rect.
+  GdkRectangle gdk_rect = {-drawingRect.x/scaleFactor,
+                           -drawingRect.y/scaleFactor,
+                           widgetRect.width/scaleFactor,
+                           widgetRect.height/scaleFactor};
+
+  // translate everything so (0,0) is the top left of the drawingRect
+  gfxPoint origin = rect.TopLeft() + drawingRect.TopLeft();
+
 #if (MOZ_WIDGET_GTK == 2)
+  gfxContextAutoSaveRestore autoSR(ctx);
+  gfxMatrix matrix;
+  if (!snapped) { // else rects are in device coords
+    matrix = ctx->CurrentMatrix();
+  }
+  matrix.Translate(origin);
+  matrix.Scale(scaleFactor, scaleFactor); // Draw in GDK coords
+  ctx->SetMatrix(matrix);
+
   // The gdk_clip is just advisory here, meaning "you don't
   // need to draw outside this rect if you don't feel like it!"
   GdkRectangle gdk_clip = {0, 0, drawingRect.width, drawingRect.height};
 
   ThemeRenderer renderer(state, gtkWidgetType, flags, direction,
                          gdk_rect, gdk_clip);
 
   // Some themes (e.g. Clearlooks) just don't clip properly to any
   // clip rect we provide, so we cannot advertise support for clipping within
   // the widget bounds.
   uint32_t rendererFlags = 0;
-  if (GetWidgetTransparency(aFrame, aWidgetType) == eOpaque) {
+  if (transparency == eOpaque) {
     rendererFlags |= gfxGdkNativeRenderer::DRAW_IS_OPAQUE;
   }
 
   // GtkStyles (used by the widget drawing backend) are created for a
   // particular colormap/visual.
   GdkColormap* colormap = moz_gtk_widget_get_colormap();
 
   renderer.Draw(ctx, drawingRect.Size(), rendererFlags, colormap);
 #else 
-  cairo_t *cairo_ctx =
-    (cairo_t*)aDrawTarget.GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT); 
-  MOZ_ASSERT(cairo_ctx);
-  moz_gtk_widget_paint(gtkWidgetType, cairo_ctx, &gdk_rect, 
-                       &state, flags, direction);
+  DrawThemeWithCairo(ctx, aContext->GetDrawTarget(),
+                     state, gtkWidgetType, flags, direction, scaleFactor,
+                     snapped, ToPoint(origin), drawingRect.Size(),
+                     gdk_rect, transparency);
 #endif
 
   if (!safeState) {
     gdk_flush();
     gLastGdkError = gdk_error_trap_pop ();
 
     if (gLastGdkError) {
 #ifdef DEBUG
--- a/widget/gtk/nsScreenGtk.cpp
+++ b/widget/gtk/nsScreenGtk.cpp
@@ -7,16 +7,17 @@
 
 #include <gdk/gdk.h>
 #ifdef MOZ_X11
 #include <gdk/gdkx.h>
 #include <X11/Xatom.h>
 #endif
 #include <gtk/gtk.h>
 #include <dlfcn.h>
+#include "gfxPlatformGtk.h"
 
 static uint32_t sScreenId = 0;
 
 
 nsScreenGtk :: nsScreenGtk (  )
   : mScreenNum(0),
     mRect(0, 0, 0, 0),
     mAvailRect(0, 0, 0, 0),
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -2045,81 +2045,62 @@ gdk_window_flash(GdkWindow *    aGdkWind
   gdk_gc_destroy(gc);
 
   gdk_region_offset(aRegion, -x, -y);
 }
 #endif /* MOZ_X11 */
 #endif // DEBUG
 #endif
 
-struct ExposeRegion
-{
-    nsIntRegion mRegion;
-
 #if (MOZ_WIDGET_GTK == 2)
-    GdkRectangle *mRects;
-    GdkRectangle *mRectsEnd;
-
-    ExposeRegion() : mRects(nullptr)
-    {
-    }
-    ~ExposeRegion()
-    {
-        g_free(mRects);
-    }
-    bool Init(GdkEventExpose *aEvent)
-    {
-        gint nrects;
-        gdk_region_get_rectangles(aEvent->region, &mRects, &nrects);
-
-        if (nrects > MAX_RECTS_IN_REGION) {
-            // Just use the bounding box
-            mRects[0] = aEvent->area;
-            nrects = 1;
-        }
-
-        mRectsEnd = mRects + nrects;
-
-        for (GdkRectangle *r = mRects; r < mRectsEnd; r++) {
-            mRegion.Or(mRegion, nsIntRect(r->x, r->y, r->width, r->height));
-            LOGDRAW(("\t%d %d %d %d\n", r->x, r->y, r->width, r->height));
-        }
-        return true;
-    }
+static bool
+ExtractExposeRegion(nsIntRegion& aRegion, GdkEventExpose* aEvent)
+{
+  GdkRectangle* rects;
+  gint nrects;
+  gdk_region_get_rectangles(aEvent->region, &rects, &nrects);
+
+  if (nrects > MAX_RECTS_IN_REGION) {
+      // Just use the bounding box
+      rects[0] = aEvent->area;
+      nrects = 1;
+  }
+
+  for (GdkRectangle* r = rects; r < rects + nrects; r++) {
+      aRegion.Or(aRegion, nsIntRect(r->x, r->y, r->width, r->height));
+      LOGDRAW(("\t%d %d %d %d\n", r->x, r->y, r->width, r->height));
+  }
+
+  g_free(rects);
+  return true;
+}
 
 #else
 # ifdef cairo_copy_clip_rectangle_list
 #  error "Looks like we're including Mozilla's cairo instead of system cairo"
 # endif
-    cairo_rectangle_list_t *mRects;
-
-    ExposeRegion() : mRects(nullptr)
-    {
-    }
-    ~ExposeRegion()
-    {
-        cairo_rectangle_list_destroy(mRects);
-    }
-    bool Init(cairo_t* cr)
-    {
-        mRects = cairo_copy_clip_rectangle_list(cr);
-        if (mRects->status != CAIRO_STATUS_SUCCESS) {
-            NS_WARNING("Failed to obtain cairo rectangle list.");
-            return false;
-        }
-
-        for (int i = 0; i < mRects->num_rectangles; i++)  {
-            const cairo_rectangle_t& r = mRects->rectangles[i];
-            mRegion.Or(mRegion, nsIntRect(r.x, r.y, r.width, r.height));
-            LOGDRAW(("\t%d %d %d %d\n", r.x, r.y, r.width, r.height));
-        }
-        return true;
-    }
+static bool
+ExtractExposeRegion(nsIntRegion& aRegion, cairo_t* cr)
+{
+  cairo_rectangle_list_t* rects = cairo_copy_clip_rectangle_list(cr);
+  if (rects->status != CAIRO_STATUS_SUCCESS) {
+      NS_WARNING("Failed to obtain cairo rectangle list.");
+      return false;
+  }
+
+  for (int i = 0; i < rects->num_rectangles; i++)  {
+      const cairo_rectangle_t& r = rects->rectangles[i];
+      aRegion.Or(aRegion, nsIntRect(r.x, r.y, r.width, r.height));
+      LOGDRAW(("\t%d %d %d %d\n", r.x, r.y, r.width, r.height));
+  }
+
+  cairo_rectangle_list_destroy(rects);
+  return true;
+}
 #endif
-};
 
 #if (MOZ_WIDGET_GTK == 2)
 gboolean
 nsWindow::OnExposeEvent(GdkEventExpose *aEvent)
 #else
 gboolean
 nsWindow::OnExposeEvent(cairo_t *cr)
 #endif
@@ -2132,27 +2113,27 @@ nsWindow::OnExposeEvent(cairo_t *cr)
     if (!mGdkWindow || mIsFullyObscured || !mHasMappedToplevel)
         return FALSE;
 
     nsIWidgetListener *listener =
         mAttachedWidgetListener ? mAttachedWidgetListener : mWidgetListener;
     if (!listener)
         return FALSE;
 
-    ExposeRegion exposeRegion;
+    nsIntRegion exposeRegion;
 #if (MOZ_WIDGET_GTK == 2)
-    if (!exposeRegion.Init(aEvent)) {
+    if (!ExtractExposeRegion(exposeRegion, aEvent)) {
 #else
-    if (!exposeRegion.Init(cr)) {
+    if (!ExtractExposeRegion(exposeRegion, cr)) {
 #endif
         return FALSE;
     }
 
     gint scale = GdkScaleFactor();
-    nsIntRegion& region = exposeRegion.mRegion;
+    nsIntRegion region = exposeRegion;
     region.ScaleRoundOut(scale, scale);
 
     ClientLayerManager *clientLayers =
         (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT)
         ? static_cast<ClientLayerManager*>(GetLayerManager())
         : nullptr;
 
     if (clientLayers && mCompositorParent) {
@@ -2235,43 +2216,21 @@ nsWindow::OnExposeEvent(cairo_t *cr)
 
     // If this widget uses OMTC...
     if (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT) {
         listener->PaintWindow(this, region);
         listener->DidPaintWindow();
         return TRUE;
     }
 
-    gfxASurface* surf;
-#if (MOZ_WIDGET_GTK == 2)
-    surf = GetThebesSurface();
-#else
-    surf = GetThebesSurface(cr);
-#endif
-
-    nsRefPtr<gfxContext> ctx;
-    if (gfxPlatform::GetPlatform()->
-            SupportsAzureContentForType(BackendType::CAIRO)) {
-        IntSize intSize(surf->GetSize().width, surf->GetSize().height);
-        RefPtr<DrawTarget> dt =
-          gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surf, intSize);
-        ctx = new gfxContext(dt);
-    } else if (gfxPlatform::GetPlatform()->
-                   SupportsAzureContentForType(BackendType::SKIA) &&
-               surf->GetType() == gfxSurfaceType::Image) {
-       gfxImageSurface* imgSurf = static_cast<gfxImageSurface*>(surf);
-       SurfaceFormat format = ImageFormatToSurfaceFormat(imgSurf->Format());
-       IntSize intSize(surf->GetSize().width, surf->GetSize().height);
-       RefPtr<DrawTarget> dt =
-         gfxPlatform::GetPlatform()->CreateDrawTargetForData(
-                        imgSurf->Data(), intSize, imgSurf->Stride(), format);
-       ctx = new gfxContext(dt);
-    } else {
-        MOZ_CRASH("Unexpected content type");
-    }
+    RefPtr<DrawTarget> dt = StartRemoteDrawing();
+    if(!dt) {
+        return FALSE;
+    }
+    nsRefPtr<gfxContext> ctx = new gfxContext(dt);
 
 #ifdef MOZ_X11
     nsIntRect boundsRect; // for shaped only
 
     ctx->NewPath();
     if (shaped) {
         // Collapse update area to the bounding box. This is so we only have to
         // call UpdateTranslucentWindowAlpha once. After we have dropped
@@ -2336,21 +2295,17 @@ nsWindow::OnExposeEvent(cairo_t *cr)
                 ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
                 ctx->SetPattern(pattern);
                 ctx->Paint();
             }
         }
     }
 #  ifdef MOZ_HAVE_SHMIMAGE
     if (mShmImage && MOZ_LIKELY(!mIsDestroyed)) {
-#if (MOZ_WIDGET_GTK == 2)
-        mShmImage->Put(mGdkWindow, exposeRegion.mRects, exposeRegion.mRectsEnd);
-#else
-        mShmImage->Put(mGdkWindow, exposeRegion.mRects);
-#endif
+        mShmImage->Put(mGdkWindow, exposeRegion);
     }
 #  endif  // MOZ_HAVE_SHMIMAGE
 #endif // MOZ_X11
 
     listener->DidPaintWindow();
 
     // Synchronously flush any new dirty areas
 #if (MOZ_WIDGET_GTK == 2)
@@ -6257,34 +6212,59 @@ nsWindow::GetSurfaceForGdkDrawable(GdkDr
 TemporaryRef<DrawTarget>
 nsWindow::StartRemoteDrawing()
 {
   gfxASurface *surf = GetThebesSurface();
   if (!surf) {
     return nullptr;
   }
 
-  IntSize size(surf->GetSize().width, surf->GetSize().height);
+  nsIntSize size = surf->GetSize();
   if (size.width <= 0 || size.height <= 0) {
     return nullptr;
   }
 
-  return gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surf, size);
+  gfxPlatform *platform = gfxPlatform::GetPlatform();
+  if (platform->SupportsAzureContentForType(BackendType::CAIRO) ||
+      surf->GetType() == gfxSurfaceType::Xlib) {
+    return platform->CreateDrawTargetForSurface(surf, size);
+  } else if (platform->SupportsAzureContentForType(BackendType::SKIA) &&
+             surf->GetType() == gfxSurfaceType::Image) {
+    gfxImageSurface* imgSurf = static_cast<gfxImageSurface*>(surf);
+    SurfaceFormat format = ImageFormatToSurfaceFormat(imgSurf->Format());
+    return platform->CreateDrawTargetForData(
+                     imgSurf->Data(), size, imgSurf->Stride(), format);
+  } else {
+    return nullptr;
+  }
+}
+
+void
+nsWindow::EndRemoteDrawingInRegion(DrawTarget* aDrawTarget, nsIntRegion& aInvalidRegion)
+{
+#ifdef MOZ_X11
+#  ifdef MOZ_HAVE_SHMIMAGE
+  if (!mGdkWindow || mIsFullyObscured || !mHasMappedToplevel || mIsDestroyed ||
+      !mShmImage)
+    return;
+
+  gint scale = GdkScaleFactor();
+  if (scale != 1) {
+    aInvalidRegion.ScaleInverseRoundOut(scale, scale);
+  }
+
+  mShmImage->Put(mGdkWindow, aInvalidRegion);
+
+#  endif // MOZ_HAVE_SHMIMAGE
+#endif // MOZ_X11
 }
 
 // return the gfxASurface for rendering to this widget
 gfxASurface*
 nsWindow::GetThebesSurface()
-#if (MOZ_WIDGET_GTK == 3)
-{
-    return GetThebesSurface(nullptr);
-}
-gfxASurface*
-nsWindow::GetThebesSurface(cairo_t *cr)
-#endif
 {
     if (!mGdkWindow)
         return nullptr;
 
 #ifdef MOZ_X11
     gint width, height;
 
 #if (MOZ_WIDGET_GTK == 2)
--- a/widget/gtk/nsWindow.h
+++ b/widget/gtk/nsWindow.h
@@ -189,17 +189,20 @@ public:
                                                GdkDragContext  *aDragContext,
                                                gint             aX,
                                                gint             aY,
                                                GtkSelectionData*aSelectionData,
                                                guint            aInfo,
                                                guint            aTime,
                                                gpointer         aData);
 
-  mozilla::TemporaryRef<mozilla::gfx::DrawTarget> StartRemoteDrawing() override;
+    virtual mozilla::TemporaryRef<mozilla::gfx::DrawTarget>
+                       StartRemoteDrawing() override;
+    virtual void       EndRemoteDrawingInRegion(mozilla::gfx::DrawTarget* aDrawTarget,
+                                                nsIntRegion& aInvalidRegion) override;
 
 private:
     void               UpdateAlpha(gfxPattern* aPattern, nsIntRect aBoundsRect);
 
     void               NativeResize(int32_t aWidth,
                                     int32_t aHeight,
                                     bool    aRepaint);
 
@@ -471,19 +474,16 @@ private:
 
     void DispatchMissedButtonReleases(GdkEventCrossing *aGdkEvent);
 
     // nsBaseWidget
     virtual LayerManager* GetLayerManager(PLayerTransactionChild* aShadowManager = nullptr,
                                           LayersBackend aBackendHint = mozilla::layers::LayersBackend::LAYERS_NONE,
                                           LayerManagerPersistence aPersistence = LAYER_MANAGER_CURRENT,
                                           bool* aAllowRetaining = nullptr) override;
-#if (MOZ_WIDGET_GTK == 3)
-    gfxASurface* GetThebesSurface(cairo_t *cr);
-#endif
 
     void CleanLayerManagerRecursive();
 
     /**
      * |mIMModule| takes all IME related stuff.
      *
      * This is owned by the top-level nsWindow or the topmost child
      * nsWindow embedded in a non-Gecko widget.
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -1651,16 +1651,19 @@ class nsIWidget : public nsISupports {
     /**
      * Ensure that what was painted into the DrawTarget returned from
      * StartRemoteDrawing reaches the screen.
      *
      * Called by BasicCompositor on the compositor thread for OMTC drawing
      * after each composition.
      */
     virtual void EndRemoteDrawing() = 0;
+    virtual void EndRemoteDrawingInRegion(mozilla::gfx::DrawTarget* aDrawTarget, nsIntRegion& aInvalidRegion) {
+      EndRemoteDrawing();
+    }
 
     /**
      * Clean up any resources used by Start/EndRemoteDrawing.
      *
      * Called by BasicCompositor on the compositor thread for OMTC drawing
      * when the compositor is destroyed.
      */
     virtual void CleanupRemoteDrawing() = 0;
--- a/widget/nsShmImage.cpp
+++ b/widget/nsShmImage.cpp
@@ -116,27 +116,28 @@ nsShmImage::AsSurface()
                             mSize,
                             mImage->bytes_per_line,
                             mFormat)
         ).forget();
 }
 
 #if (MOZ_WIDGET_GTK == 2)
 void
-nsShmImage::Put(GdkWindow* aWindow, GdkRectangle* aRects, GdkRectangle* aEnd)
+nsShmImage::Put(GdkWindow* aWindow, const nsIntRegion& aRegion)
 {
     GdkDrawable* gd;
     gint dx, dy;
     gdk_window_get_internal_paint_info(aWindow, &gd, &dx, &dy);
 
     Display* dpy = gdk_x11_get_default_xdisplay();
     Drawable d = GDK_DRAWABLE_XID(gd);
 
     GC gc = XCreateGC(dpy, d, 0, nullptr);
-    for (GdkRectangle* r = aRects; r < aEnd; r++) {
+    nsIntRegionRectIterator iter(aRegion);
+    for (const nsIntRect *r = iter.Next(); r; r = iter.Next()) {
         XShmPutImage(dpy, d, gc, mImage,
                      r->x, r->y,
                      r->x - dx, r->y - dy,
                      r->width, r->height,
                      False);
     }
     XFreeGC(dpy, gc);
 
@@ -146,30 +147,29 @@ nsShmImage::Put(GdkWindow* aWindow, GdkR
     // synchronization mechanism; other options are possible.  If this
     // XSync is shown to hurt responsiveness, we need to explore the
     // other options.
     XSync(dpy, False);
 }
 
 #elif (MOZ_WIDGET_GTK == 3)
 void
-nsShmImage::Put(GdkWindow* aWindow, cairo_rectangle_list_t* aRects)
+nsShmImage::Put(GdkWindow* aWindow, const nsIntRegion& aRegion)
 {
     Display* dpy = gdk_x11_get_default_xdisplay();
     Drawable d = GDK_WINDOW_XID(aWindow);
     int dx = 0, dy = 0;
 
     GC gc = XCreateGC(dpy, d, 0, nullptr);
-    cairo_rectangle_t r;
-    for (int i = 0; i < aRects->num_rectangles; i++) {
-        r = aRects->rectangles[i];
+    nsIntRegionRectIterator iter(aRegion);
+    for (const nsIntRect *r = iter.Next(); r; r = iter.Next()) {
         XShmPutImage(dpy, d, gc, mImage,
-                     r.x, r.y,
-                     r.x - dx, r.y - dy,
-                     r.width, r.height,
+                     r->x, r->y,
+                     r->x - dx, r->y - dy,
+                     r->width, r->height,
                      False);
     }
 
     XFreeGC(dpy, gc);
 
     // FIXME/bug 597336: we need to ensure that the shm image isn't
     // scribbled over before all its pending XShmPutImage()s complete.
     // However, XSync() is an unnecessarily heavyweight
--- a/widget/nsShmImage.h
+++ b/widget/nsShmImage.h
@@ -58,20 +58,18 @@ private:
             }
             XDestroyImage(mImage);
         }
     }
 
 public:
     already_AddRefed<gfxASurface> AsSurface();
 
-#if (MOZ_WIDGET_GTK == 2)
-    void Put(GdkWindow* aWindow, GdkRectangle* aRects, GdkRectangle* aEnd);
-#elif (MOZ_WIDGET_GTK == 3)
-    void Put(GdkWindow* aWindow, cairo_rectangle_list_t* aRects);
+#ifdef MOZ_WIDGET_GTK
+    void Put(GdkWindow* aWindow, const nsIntRegion& aRegion);
 #elif defined(MOZ_WIDGET_QT)
     void Put(QWindow* aWindow, QRect& aRect);
 #endif
 
     gfxIntSize Size() const { return mSize; }
 
 private:
     nsShmImage()