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 243628 8538bc4d2cbd90499cbd17dd12b608b5025acbf6
parent 243627 e0faf96523b2322f52c94edf2f01298c93656725
child 243629 1663d6ff4ae055c4517ac636e26b0b2e566fcf3d
push id28744
push userkwierso@gmail.com
push dateWed, 13 May 2015 18:12:16 +0000
treeherdermozilla-central@324c3423deaf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1127752
milestone41.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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()