Bug 776217: Support gecko-implemented screen rotation with omtc. r=roc
authorChris Jones <jones.chris.g@gmail.com>
Tue, 24 Jul 2012 12:01:09 -0700
changeset 103438 97040ffd8e31765ca1ffc6f8c8e3ec12709123a5
parent 103437 f47a908ed9d2d189379be85f045d2bd82bfd09f0
child 103439 ac2d2c7ccb1b3d1b7ef446effc44b55a215f7e65
push id1989
push userakeybl@mozilla.com
push dateTue, 28 Aug 2012 00:20:43 +0000
treeherdermozilla-aurora@a8e95ae10ea7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs776217
milestone17.0a1
Bug 776217: Support gecko-implemented screen rotation with omtc. r=roc
gfx/layers/basic/BasicLayerManager.cpp
gfx/layers/basic/BasicLayers.h
gfx/layers/d3d10/LayerManagerD3D10.cpp
gfx/layers/ipc/CompositorParent.cpp
gfx/layers/ipc/CompositorParent.h
gfx/layers/ipc/PLayers.ipdl
gfx/layers/ipc/ShadowLayerUtils.h
gfx/layers/ipc/ShadowLayers.cpp
gfx/layers/ipc/ShadowLayers.h
gfx/layers/ipc/ShadowLayersManager.h
gfx/layers/ipc/ShadowLayersParent.cpp
gfx/layers/ipc/ShadowLayersParent.h
layout/ipc/RenderFrameParent.cpp
layout/ipc/RenderFrameParent.h
widget/Makefile.in
widget/WidgetUtils.h
widget/gonk/nsWindow.cpp
widget/gonk/nsWindow.h
widget/nsIWidget.h
widget/xpwidgets/Makefile.in
widget/xpwidgets/WidgetUtils.cpp
widget/xpwidgets/nsBaseWidget.cpp
widget/xpwidgets/nsBaseWidget.h
--- a/gfx/layers/basic/BasicLayerManager.cpp
+++ b/gfx/layers/basic/BasicLayerManager.cpp
@@ -17,16 +17,17 @@
 #define PIXMAN_DONT_DEFINE_STDINT
 #include "pixman.h"
 
 #include "BasicTiledThebesLayer.h"
 #include "BasicLayersImpl.h"
 #include "BasicThebesLayer.h"
 #include "BasicContainerLayer.h"
 #include "mozilla/Preferences.h"
+#include "nsIWidget.h"
 
 using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace layers {
 
 /**
  * Clips to the smallest device-pixel-aligned rectangle containing aRect
@@ -127,17 +128,18 @@ BasicLayerManager::~BasicLayerManager()
 
   mRoot = nsnull;
 
   MOZ_COUNT_DTOR(BasicLayerManager);
 }
 
 void
 BasicLayerManager::SetDefaultTarget(gfxContext* aContext,
-                                    BufferMode aDoubleBuffering)
+                                    BufferMode aDoubleBuffering,
+                                    ScreenRotation aRotation)
 {
   NS_ASSERTION(!InTransaction(),
                "Must set default target outside transaction");
   mDefaultTarget = aContext;
   mDoubleBuffering = aDoubleBuffering;
 }
 
 void
@@ -917,17 +919,17 @@ already_AddRefed<ReadbackLayer>
 BasicLayerManager::CreateReadbackLayer()
 {
   NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
   nsRefPtr<ReadbackLayer> layer = new BasicReadbackLayer(this);
   return layer.forget();
 }
 
 BasicShadowLayerManager::BasicShadowLayerManager(nsIWidget* aWidget) :
-  BasicLayerManager(aWidget)
+  BasicLayerManager(aWidget), mTargetRotation(ROTATION_0)
 {
   MOZ_COUNT_CTOR(BasicShadowLayerManager);
 }
 
 BasicShadowLayerManager::~BasicShadowLayerManager()
 {
   MOZ_COUNT_DTOR(BasicShadowLayerManager);
 }
@@ -938,16 +940,28 @@ BasicShadowLayerManager::GetMaxTextureSi
   if (HasShadowManager()) {
     return ShadowLayerForwarder::GetMaxTextureSize();
   }
 
   return PR_INT32_MAX;
 }
 
 void
+BasicShadowLayerManager::SetDefaultTarget(gfxContext* aContext,
+                                          BufferMode aDoubleBuffering,
+                                          ScreenRotation aRotation)
+{
+  BasicLayerManager::SetDefaultTarget(aContext, aDoubleBuffering, aRotation);
+  mTargetRotation = aRotation;
+  if (mWidget) {
+    mTargetBounds = mWidget->GetNaturalBounds();
+  }
+}
+
+void
 BasicShadowLayerManager::SetRoot(Layer* aLayer)
 {
   if (mRoot != aLayer) {
     if (HasShadowManager()) {
       // Have to hold the old root and its children in order to
       // maintain the same view of the layer tree in this process as
       // the parent sees.  Otherwise layers can be destroyed
       // mid-transaction and bad things can happen (v. bug 612573)
@@ -976,17 +990,17 @@ BasicShadowLayerManager::BeginTransactio
 {
   NS_ABORT_IF_FALSE(mKeepAlive.IsEmpty(), "uncommitted txn?");
   nsRefPtr<gfxContext> targetContext = aTarget;
 
   // If the last transaction was incomplete (a failed DoEmptyTransaction),
   // don't signal a new transaction to ShadowLayerForwarder. Carry on adding
   // to the previous transaction.
   if (HasShadowManager()) {
-    ShadowLayerForwarder::BeginTransaction();
+    ShadowLayerForwarder::BeginTransaction(mTargetBounds, mTargetRotation);
 
     // If we have a non-default target, we need to let our shadow manager draw
     // to it. This will happen at the end of the transaction.
     if (aTarget && (aTarget != mDefaultTarget) &&
         XRE_GetProcessType() == GeckoProcessType_Default) {
       mShadowTarget = aTarget;
 
       // Create a temporary target for ourselves, so that mShadowTarget is only
--- a/gfx/layers/basic/BasicLayers.h
+++ b/gfx/layers/basic/BasicLayers.h
@@ -5,21 +5,21 @@
 
 #ifndef GFX_BASICLAYERS_H
 #define GFX_BASICLAYERS_H
 
 #include "Layers.h"
 
 #include "gfxContext.h"
 #include "gfxCachedTempSurface.h"
+#include "mozilla/layers/ShadowLayers.h"
+#include "mozilla/WidgetUtils.h"
 #include "nsAutoRef.h"
 #include "nsThreadUtils.h"
 
-#include "mozilla/layers/ShadowLayers.h"
-
 class nsIWidget;
 
 namespace mozilla {
 namespace layers {
 
 class BasicShadowableLayer;
 class ShadowThebesLayer;
 class ShadowContainerLayer;
@@ -75,17 +75,18 @@ public:
    * results, by using a temporary buffer when necessary. In BUFFERED
    * mode we always completely overwrite the contents of aContext's
    * destination surface (within the clip region) using OPERATOR_SOURCE.
    */
   enum BufferMode {
     BUFFER_NONE,
     BUFFER_BUFFERED
   };
-  void SetDefaultTarget(gfxContext* aContext, BufferMode aDoubleBuffering);
+  virtual void SetDefaultTarget(gfxContext* aContext, BufferMode aDoubleBuffering,
+                                ScreenRotation aRotation);
   gfxContext* GetDefaultTarget() { return mDefaultTarget; }
 
   nsIWidget* GetRetainerWidget() { return mWidget; }
   void ClearRetainerWidget() { mWidget = nsnull; }
 
   virtual bool IsWidgetLayerManager() { return mWidget != nsnull; }
 
   virtual void BeginTransaction();
@@ -216,16 +217,18 @@ public:
   }
   virtual ShadowLayerManager* AsShadowManager()
   {
     return this;
   }
 
   virtual PRInt32 GetMaxTextureSize() const;
 
+  virtual void SetDefaultTarget(gfxContext* aContext, BufferMode aDoubleBuffering,
+                                ScreenRotation aRotation) MOZ_OVERRIDE;
   virtual void BeginTransactionWithTarget(gfxContext* aTarget);
   virtual bool EndEmptyTransaction();
   virtual void EndTransaction(DrawThebesLayerCallback aCallback,
                               void* aCallbackData,
                               EndTransactionFlags aFlags = END_DEFAULT);
 
   virtual void SetRoot(Layer* aLayer);
 
@@ -256,21 +259,30 @@ public:
   void SetRepeatTransaction() { mRepeatTransaction = true; }
 
 private:
   /**
    * Forward transaction results to the parent context.
    */
   void ForwardTransaction();
 
+  // The bounds of |mTarget| in device pixels.
+  nsIntRect mTargetBounds;
+
+  LayerRefArray mKeepAlive;
+
+  // Sometimes we draw to targets that don't natively support
+  // landscape/portrait orientation.  When we need to implement that
+  // ourselves, |mTargetRotation| describes the induced transform we
+  // need to apply when compositing content to our target.
+  ScreenRotation mTargetRotation;
+
   // Used to repeat the transaction right away (to avoid rebuilding
   // a display list) to support progressive drawing.
   bool mRepeatTransaction;
-
-  LayerRefArray mKeepAlive;
 };
 
 class BasicShadowableThebesLayer;
 class BasicShadowableLayer : public ShadowableLayer
 {
 public:
   BasicShadowableLayer()
   {
--- a/gfx/layers/d3d10/LayerManagerD3D10.cpp
+++ b/gfx/layers/d3d10/LayerManagerD3D10.cpp
@@ -15,16 +15,17 @@
 
 #include "ContainerLayerD3D10.h"
 #include "ThebesLayerD3D10.h"
 #include "ColorLayerD3D10.h"
 #include "CanvasLayerD3D10.h"
 #include "ReadbackLayerD3D10.h"
 #include "ImageLayerD3D10.h"
 #include "mozilla/layers/PLayerChild.h"
+#include "mozilla/WidgetUtils.h"
 
 #include "../d3d9/Nv3DVUtils.h"
 
 #include "gfxCrashReporterUtils.h"
 
 using namespace std;
 using namespace mozilla::gfx;
 
@@ -724,17 +725,18 @@ LayerManagerD3D10::Render()
   }
   device()->RSSetScissorRects(1, &r);
 
   static_cast<LayerD3D10*>(mRoot->ImplData())->RenderLayer();
 
   if (mTarget) {
     PaintToTarget();
   } else if (mBackBuffer) {
-    ShadowLayerForwarder::BeginTransaction();
+    ShadowLayerForwarder::BeginTransaction(mWidget->GetNaturalBounds(),
+                                           ROTATION_0);
     
     nsIntRect contentRect = nsIntRect(0, 0, rect.width, rect.height);
     if (!mRootForShadowTree) {
         mRootForShadowTree = new DummyRoot(this);
         mRootForShadowTree->SetShadow(ConstructShadowFor(mRootForShadowTree));
         CreatedContainerLayer(mRootForShadowTree);
         ShadowLayerForwarder::SetRoot(mRootForShadowTree);
     }
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -417,16 +417,23 @@ CompositorParent::Composite()
 
   bool requestNextFrame = TransformShadowTree(mLastCompose);
   if (requestNextFrame) {
     ScheduleComposition();
   }
 
   RenderTraceLayers(aLayer, "0000");
 
+  if (LAYERS_OPENGL == mLayerManager->GetBackendType() &&
+      !mTargetConfig.naturalBounds().IsEmpty()) {
+    LayerManagerOGL* lm = static_cast<LayerManagerOGL*>(mLayerManager.get());
+    lm->SetWorldTransform(
+      ComputeGLTransformForRotation(mTargetConfig.naturalBounds(),
+                                    mTargetConfig.rotation()));
+  }
   mLayerManager->EndEmptyTransaction();
 
 #ifdef COMPOSITOR_PERFORMANCE_WARNING
   if (mExpectedComposeTime + TimeDuration::FromMilliseconds(15) < TimeStamp::Now()) {
     printf_stderr("Compositor: Composite took %i ms.\n",
                   15 + (int)(TimeStamp::Now() - mExpectedComposeTime).ToMilliseconds());
   }
 #endif
@@ -689,18 +696,20 @@ CompositorParent::SyncViewportInfo(const
 #ifdef MOZ_WIDGET_ANDROID
   AndroidBridge::Bridge()->SyncViewportInfo(aDisplayPort, aDisplayResolution, aLayersUpdated,
                                             aScrollOffset, aScaleX, aScaleY);
 #endif
 }
 
 void
 CompositorParent::ShadowLayersUpdated(ShadowLayersParent* aLayerTree,
+                                      const TargetConfig& aTargetConfig,
                                       bool isFirstPaint)
 {
+  mTargetConfig = aTargetConfig;
   mIsFirstPaint = mIsFirstPaint || isFirstPaint;
   mLayersUpdated = true;
   Layer* root = aLayerTree->GetRoot();
   mLayerManager->SetRoot(root);
   if (root) {
     SetShadowProperties(root);
   }
   ScheduleComposition();
@@ -893,16 +902,17 @@ public:
 
   virtual PLayersParent* AllocPLayers(const LayersBackend& aBackendType,
                                       const uint64_t& aId,
                                       LayersBackend* aBackend,
                                       int32_t* aMaxTextureSize) MOZ_OVERRIDE;
   virtual bool DeallocPLayers(PLayersParent* aLayers) MOZ_OVERRIDE;
 
   virtual void ShadowLayersUpdated(ShadowLayersParent* aLayerTree,
+                                   const TargetConfig& aTargetConfig,
                                    bool isFirstPaint) MOZ_OVERRIDE;
 
 private:
   void DeferredDestroy();
 
   // There can be many CPCPs, and IPDL-generated code doesn't hold a
   // reference to top-level actors.  So we hold a reference to
   // ourself.  This is released (deferred) in ActorDestroy().
@@ -992,18 +1002,20 @@ CrossProcessCompositorParent::DeallocPLa
 {
   ShadowLayersParent* slp = static_cast<ShadowLayersParent*>(aLayers);
   RemoveIndirectTree(slp->GetId());
   delete aLayers;
   return true;
 }
 
 void
-CrossProcessCompositorParent::ShadowLayersUpdated(ShadowLayersParent* aLayerTree,
-                                                  bool isFirstPaint)
+CrossProcessCompositorParent::ShadowLayersUpdated(
+  ShadowLayersParent* aLayerTree,
+  const TargetConfig& aTargetConfig,
+  bool isFirstPaint)
 {
   uint64_t id = aLayerTree->GetId();
   MOZ_ASSERT(id != 0);
   Layer* shadowRoot = aLayerTree->GetRoot();
   if (shadowRoot) {
     SetShadowProperties(shadowRoot);
   }
   UpdateIndirectTree(id, shadowRoot, isFirstPaint);
--- a/gfx/layers/ipc/CompositorParent.h
+++ b/gfx/layers/ipc/CompositorParent.h
@@ -65,16 +65,17 @@ public:
   virtual ~CompositorParent();
 
   virtual bool RecvWillStop() MOZ_OVERRIDE;
   virtual bool RecvStop() MOZ_OVERRIDE;
   virtual bool RecvPause() MOZ_OVERRIDE;
   virtual bool RecvResume() MOZ_OVERRIDE;
 
   virtual void ShadowLayersUpdated(ShadowLayersParent* aLayerTree,
+                                   const TargetConfig& aTargetConfig,
                                    bool isFirstPaint) MOZ_OVERRIDE;
   void Destroy();
 
   LayerManager* GetLayerManager() { return mLayerManager; }
 
   void SetTransformation(float aScale, nsIntPoint aScrollOffset);
   void AsyncRender();
 
@@ -222,16 +223,17 @@ private:
    * fixed position layers remain in the same position.
    */
   void TransformFixedLayers(Layer* aLayer,
                             const gfxPoint& aTranslation,
                             const gfxPoint& aScaleDiff);
 
   nsRefPtr<LayerManager> mLayerManager;
   nsIWidget* mWidget;
+  TargetConfig mTargetConfig;
   CancelableTask *mCurrentCompositeTask;
   TimeStamp mLastCompose;
 #ifdef COMPOSITOR_PERFORMANCE_WARNING
   TimeStamp mExpectedComposeTime;
 #endif
 
   bool mPaused;
   float mXScale;
--- a/gfx/layers/ipc/PLayers.ipdl
+++ b/gfx/layers/ipc/PLayers.ipdl
@@ -1,34 +1,41 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: sw=2 ts=8 et :
  */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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/. */
 
 include LayersSurfaces;
+using mozilla::ScreenRotation;
 include protocol PCompositor;
 include protocol PGrallocBuffer;
 include protocol PLayer;
 include protocol PRenderFrame;
 
 include "gfxipc/ShadowLayerUtils.h";
+include "mozilla/WidgetUtils.h";
 
 /**
  * The layers protocol is spoken between thread contexts that manage
  * layer (sub)trees.  The protocol comprises atomically publishing
  * layer subtrees to a "shadow" thread context (which grafts the
  * subtree into its own tree), and atomically updating a published
  * subtree.  ("Atomic" in this sense is wrt painting.)
  */
 
 namespace mozilla {
 namespace layers {
 
+struct TargetConfig {
+  nsIntRect naturalBounds;
+  ScreenRotation rotation;
+};
+
 // Create a shadow layer for |layer|
 struct OpCreateThebesLayer     { PLayer layer; };
 struct OpCreateContainerLayer  { PLayer layer; };
 struct OpCreateImageLayer      { PLayer layer; };
 struct OpCreateColorLayer      { PLayer layer; };
 struct OpCreateCanvasLayer     { PLayer layer; };
 struct OpCreateRefLayer        { PLayer layer; };
 
@@ -186,24 +193,24 @@ parent:
    * is returned.
    */
   sync PGrallocBuffer(gfxIntSize size, gfxContentType content)
     returns (MaybeMagicGrallocBufferHandle handle);
   async PLayer();
 
   // The isFirstPaint flag can be used to indicate that this is the first update
   // for a particular document.
-  sync Update(Edit[] cset, bool isFirstPaint)
+  sync Update(Edit[] cset, TargetConfig targetConfig, bool isFirstPaint)
     returns (EditReply[] reply);
 
   // Composite the layer tree to the given surface, and return the surface.
   sync DrawToSurface(SurfaceDescriptor surfaceIn)
     returns (SurfaceDescriptor surfaceOut);
 
   // We don't need to send a sync transaction if
   // no transaction operate require a swap.
-  async UpdateNoSwap(Edit[] cset, bool isFirstPaint);
+  async UpdateNoSwap(Edit[] cset, TargetConfig targetConfig, bool isFirstPaint);
 
   async __delete__();
 };
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/ipc/ShadowLayerUtils.h
+++ b/gfx/layers/ipc/ShadowLayerUtils.h
@@ -6,16 +6,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef IPC_ShadowLayerUtils_h
 #define IPC_ShadowLayerUtils_h
 
 #include "IPC/IPCMessageUtils.h"
 #include "Layers.h"
 #include "GLContext.h"
+#include "mozilla/WidgetUtils.h"
 
 #if defined(MOZ_ENABLE_D3D10_LAYER)
 # include "mozilla/layers/ShadowLayerUtilsD3D10.h"
 #endif
 
 #if defined(MOZ_X11)
 # include "mozilla/layers/ShadowLayerUtilsX11.h"
 #else
@@ -102,11 +103,18 @@ struct ParamTraits<mozilla::gl::TextureI
 template <>
 struct ParamTraits<mozilla::layers::MagicGrallocBufferHandle> {
   typedef mozilla::layers::MagicGrallocBufferHandle paramType;
   static void Write(Message*, const paramType&) {}
   static bool Read(const Message*, void**, paramType*) { return false; }
 };
 #endif  // !defined(MOZ_HAVE_XSURFACEDESCRIPTORGRALLOC)
 
-}
+template <>
+struct ParamTraits<mozilla::ScreenRotation>
+  : public EnumSerializer<mozilla::ScreenRotation,
+                          mozilla::ROTATION_0,
+                          mozilla::ROTATION_COUNT>
+{};
+
+} // namespace IPC
 
 #endif // IPC_ShadowLayerUtils_h
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -36,17 +36,22 @@ typedef std::set<ShadowableLayer*> Shado
 class Transaction
 {
 public:
   Transaction()
     : mSwapRequired(false)
     , mOpen(false)
   {}
 
-  void Begin() { mOpen = true; }
+  void Begin(const nsIntRect& aTargetBounds, ScreenRotation aRotation)
+  {
+    mOpen = true;
+    mTargetBounds = aTargetBounds;
+    mTargetRotation = aRotation;
+  }
 
   void AddEdit(const Edit& aEdit)
   {
     NS_ABORT_IF_FALSE(!Finished(), "forgot BeginTransaction?");
     mCset.push_back(aEdit);
   }
   void AddPaint(const Edit& aPaint)
   {
@@ -87,16 +92,18 @@ public:
     return mCset.empty() && mPaints.empty() && mMutants.empty();
   }
   bool Finished() const { return !mOpen && Empty(); }
 
   EditVector mCset;
   EditVector mPaints;
   BufferArray mDyingBuffers;
   ShadowableLayerSet mMutants;
+  nsIntRect mTargetBounds;
+  ScreenRotation mTargetRotation;
   bool mSwapRequired;
 
 private:
   bool mOpen;
 
   // disabled
   Transaction(const Transaction&);
   Transaction& operator=(const Transaction&);
@@ -118,21 +125,22 @@ ShadowLayerForwarder::ShadowLayerForward
 
 ShadowLayerForwarder::~ShadowLayerForwarder()
 {
   NS_ABORT_IF_FALSE(mTxn->Finished(), "unfinished transaction?");
   delete mTxn;
 }
 
 void
-ShadowLayerForwarder::BeginTransaction()
+ShadowLayerForwarder::BeginTransaction(const nsIntRect& aTargetBounds,
+                                       ScreenRotation aRotation)
 {
   NS_ABORT_IF_FALSE(HasShadowManager(), "no manager to forward to");
   NS_ABORT_IF_FALSE(mTxn->Finished(), "uncommitted txn?");
-  mTxn->Begin();
+  mTxn->Begin(aTargetBounds, aRotation);
 }
 
 static PLayerChild*
 Shadow(ShadowableLayer* aLayer)
 {
   return aLayer->GetShadow();
 }
 
@@ -320,32 +328,35 @@ ShadowLayerForwarder::EndTransaction(Inf
     cset.AppendElements(&mTxn->mCset.front(), mTxn->mCset.size());
   }
   // Paints after non-paint ops, including attribute changes.  See
   // above.
   if (!mTxn->mPaints.empty()) {
     cset.AppendElements(&mTxn->mPaints.front(), mTxn->mPaints.size());
   }
 
+  TargetConfig targetConfig(mTxn->mTargetBounds, mTxn->mTargetRotation);
+
   MOZ_LAYERS_LOG(("[LayersForwarder] syncing before send..."));
   PlatformSyncBeforeUpdate();
 
   if (mTxn->mSwapRequired) {
     MOZ_LAYERS_LOG(("[LayersForwarder] sending transaction..."));
     RenderTraceScope rendertrace3("Forward Transaction", "000093");
-    if (!mShadowManager->SendUpdate(cset, mIsFirstPaint, aReplies)) {
+    if (!mShadowManager->SendUpdate(cset, targetConfig, mIsFirstPaint,
+                                    aReplies)) {
       MOZ_LAYERS_LOG(("[LayersForwarder] WARNING: sending transaction failed!"));
       return false;
     }
   } else {
     // If we don't require a swap we can call SendUpdateNoSwap which
     // assumes that aReplies is empty (DEBUG assertion)
     MOZ_LAYERS_LOG(("[LayersForwarder] sending no swap transaction..."));
     RenderTraceScope rendertrace3("Forward NoSwap Transaction", "000093");
-    if (!mShadowManager->SendUpdateNoSwap(cset, mIsFirstPaint)) {
+    if (!mShadowManager->SendUpdateNoSwap(cset, targetConfig, mIsFirstPaint)) {
       MOZ_LAYERS_LOG(("[LayersForwarder] WARNING: sending transaction failed!"));
       return false;
     }
   }
 
   mIsFirstPaint = false;
   MOZ_LAYERS_LOG(("[LayersForwarder] ... done"));
   return true;
--- a/gfx/layers/ipc/ShadowLayers.h
+++ b/gfx/layers/ipc/ShadowLayers.h
@@ -9,16 +9,17 @@
 #define mozilla_layers_ShadowLayers_h 1
 
 #include "gfxASurface.h"
 #include "GLDefs.h"
 
 #include "ImageLayers.h"
 #include "LayersBackend.h"
 #include "mozilla/ipc/SharedMemory.h"
+#include "mozilla/WidgetUtils.h"
 
 class gfxSharedImageSurface;
 
 namespace mozilla {
 
 namespace gl {
 class GLContext;
 class TextureImage;
@@ -111,17 +112,18 @@ public:
   typedef gfxASurface::gfxContentType gfxContentType;
 
   virtual ~ShadowLayerForwarder();
 
   /**
    * Begin recording a transaction to be forwarded atomically to a
    * ShadowLayerManager.
    */
-  void BeginTransaction();
+  void BeginTransaction(const nsIntRect& aTargetBounds,
+                        ScreenRotation aRotation);
 
   /**
    * The following methods may only be called after BeginTransaction()
    * but before EndTransaction().  They mirror the LayerManager
    * interface in Layers.h.
    */
 
   /**
--- a/gfx/layers/ipc/ShadowLayersManager.h
+++ b/gfx/layers/ipc/ShadowLayersManager.h
@@ -5,22 +5,23 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_layers_ShadowLayersManager_h
 #define mozilla_layers_ShadowLayersManager_h
 
 namespace mozilla {
 namespace layers {
 
+class TargetConfig;
 class ShadowLayersParent;
 
 class ShadowLayersManager
 {
 public:
     virtual void ShadowLayersUpdated(ShadowLayersParent* aLayerTree,
-                                     // FIXME nuke this
+                                     const TargetConfig& aTargetConfig,
                                      bool isFirstPaint) = 0;
 };
 
 } // layers
 } // mozilla
 
 #endif // mozilla_layers_ShadowLayersManager_h
--- a/gfx/layers/ipc/ShadowLayersParent.cpp
+++ b/gfx/layers/ipc/ShadowLayersParent.cpp
@@ -114,26 +114,28 @@ ShadowLayersParent::Destroy()
       static_cast<ShadowLayerParent*>(ManagedPLayerParent()[i]);
     slp->Destroy();
   }
 }
 
 /* virtual */
 bool
 ShadowLayersParent::RecvUpdateNoSwap(const InfallibleTArray<Edit>& cset,
-                 const bool& isFirstPaint)
+                                     const TargetConfig& targetConfig,
+                                     const bool& isFirstPaint)
 {
   InfallibleTArray<EditReply> noReplies;
-  bool success = RecvUpdate(cset, isFirstPaint, &noReplies);
+  bool success = RecvUpdate(cset, targetConfig, isFirstPaint, &noReplies);
   NS_ABORT_IF_FALSE(noReplies.Length() == 0, "RecvUpdateNoSwap requires a sync Update to carry Edits");
   return success;
 }
 
 bool
 ShadowLayersParent::RecvUpdate(const InfallibleTArray<Edit>& cset,
+                               const TargetConfig& targetConfig,
                                const bool& isFirstPaint,
                                InfallibleTArray<EditReply>* reply)
 {
 #ifdef COMPOSITOR_PERFORMANCE_WARNING
   TimeStamp updateStart = TimeStamp::Now();
 #endif
 
   MOZ_LAYERS_LOG(("[ParentSide] received txn with %d edits", cset.Length()));
@@ -412,17 +414,17 @@ ShadowLayersParent::RecvUpdate(const Inf
     reply->AppendElements(&replyv.front(), replyv.size());
   }
 
   // Ensure that any pending operations involving back and front
   // buffers have completed, so that neither process stomps on the
   // other's buffer contents.
   ShadowLayerManager::PlatformSyncBeforeReplyUpdate();
 
-  mShadowLayersManager->ShadowLayersUpdated(this, isFirstPaint);
+  mShadowLayersManager->ShadowLayersUpdated(this, targetConfig, isFirstPaint);
 
 #ifdef COMPOSITOR_PERFORMANCE_WARNING
   int compositeTime = (int)(mozilla::TimeStamp::Now() - updateStart).ToMilliseconds();
   if (compositeTime > 15) {
     printf_stderr("Compositor: Layers update took %i ms (blocking gecko).\n", compositeTime);
   }
 #endif
 
--- a/gfx/layers/ipc/ShadowLayersParent.h
+++ b/gfx/layers/ipc/ShadowLayersParent.h
@@ -43,23 +43,25 @@ public:
   uint64_t GetId() const { return mId; }
   ContainerLayer* GetRoot() const { return mRoot; }
 
   virtual void DestroySharedSurface(gfxSharedImageSurface* aSurface);
   virtual void DestroySharedSurface(SurfaceDescriptor* aSurface);
 
 protected:
   virtual bool RecvUpdate(const EditArray& cset,
+                          const TargetConfig& targetConfig,
                           const bool& isFirstPaint,
                           EditReplyArray* reply) MOZ_OVERRIDE;
 
   virtual bool RecvDrawToSurface(const SurfaceDescriptor& surfaceIn,
                                  SurfaceDescriptor* surfaceOut) MOZ_OVERRIDE;
 
   virtual bool RecvUpdateNoSwap(const EditArray& cset,
+                                const TargetConfig& targetConfig,
                                 const bool& isFirstPaint) MOZ_OVERRIDE;
 
   virtual PGrallocBufferParent*
   AllocPGrallocBuffer(const gfxIntSize& aSize, const gfxContentType& aContent,
                       MaybeMagicGrallocBufferHandle* aOutHandle) MOZ_OVERRIDE;
   virtual bool
   DeallocPGrallocBuffer(PGrallocBufferParent* actor) MOZ_OVERRIDE;
 
--- a/layout/ipc/RenderFrameParent.cpp
+++ b/layout/ipc/RenderFrameParent.cpp
@@ -552,16 +552,17 @@ RenderFrameParent::ContentViewScaleChang
 {
   // Since the scale has changed for a view, it and its descendents need their
   // shadow-space attributes updated. It's easiest to rebuild the view map.
   BuildViewMap();
 }
 
 void
 RenderFrameParent::ShadowLayersUpdated(ShadowLayersParent* aLayerTree,
+                                       const TargetConfig& aTargetConfig,
                                        bool isFirstPaint)
 {
   // View map must only contain views that are associated with the current
   // shadow layer tree. We must always update the map when shadow layers
   // are updated.
   BuildViewMap();
 
   TriggerRepaint();
--- a/layout/ipc/RenderFrameParent.h
+++ b/layout/ipc/RenderFrameParent.h
@@ -22,30 +22,32 @@ class nsSubDocumentFrame;
 
 namespace mozilla {
 
 class InputEvent;
 
 namespace layers {
 class AsyncPanZoomController;
 class GestureEventListener;
+class TargetConfig;
 class ShadowLayersParent;
 }
 
 namespace layout {
 
 class RemoteContentController;
 
 class RenderFrameParent : public PRenderFrameParent,
                           public mozilla::layers::ShadowLayersManager
 {
   typedef mozilla::layers::FrameMetrics FrameMetrics;
   typedef mozilla::layers::ContainerLayer ContainerLayer;
   typedef mozilla::layers::Layer Layer;
   typedef mozilla::layers::LayerManager LayerManager;
+  typedef mozilla::layers::TargetConfig TargetConfig;
   typedef mozilla::layers::ShadowLayersParent ShadowLayersParent;
   typedef FrameMetrics::ViewID ViewID;
 
 public:
   typedef std::map<ViewID, nsRefPtr<nsContentView> > ViewMap;
 
   /**
    * Select the desired scrolling behavior.  If ASYNC_PAN_ZOOM is
@@ -65,16 +67,17 @@ public:
    * Helper function for getting a non-owning reference to a scrollable.
    * @param aId The ID of the frame.
    */
   nsContentView* GetContentView(ViewID aId = FrameMetrics::ROOT_SCROLL_ID);
 
   void ContentViewScaleChanged(nsContentView* aView);
 
   virtual void ShadowLayersUpdated(ShadowLayersParent* aLayerTree,
+                                   const TargetConfig& aTargetConfig,
                                    bool isFirstPaint) MOZ_OVERRIDE;
 
   NS_IMETHOD BuildDisplayList(nsDisplayListBuilder* aBuilder,
                               nsSubDocumentFrame* aFrame,
                               const nsRect& aDirtyRect,
                               const nsDisplayListSet& aLists);
 
   already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
--- a/widget/Makefile.in
+++ b/widget/Makefile.in
@@ -38,26 +38,25 @@ endif
 ifdef MOZ_ENABLE_GTK2
 DIRS += gtk2
 ifdef MOZ_X11
 DIRS += gtkxtbin
 endif
 endif
 
 
-EXPORTS_NAMESPACES = IPC
+EXPORTS_NAMESPACES = IPC mozilla
 
 EXPORTS_IPC = \
 		nsGUIEventIPC.h \
 		$(NULL)
 
-EXPORTS_NAMESPACES += mozilla
-
 EXPORTS_mozilla = \
 		LookAndFeel.h \
+		WidgetUtils.h \
 		$(NULL)
 
 ifdef MOZ_INSTRUMENT_EVENT_LOOP
 EXPORTS_mozilla += \
 		WidgetTraceEvent.h \
 		$(NULL)
 endif
 
new file mode 100644
--- /dev/null
+++ b/widget/WidgetUtils.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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_WidgetUtils_h
+#define mozilla_WidgetUtils_h
+
+#include "gfxMatrix.h"
+
+namespace mozilla {
+
+// NB: these must match up with pseudo-enum in nsIScreen.idl.
+enum ScreenRotation {
+  ROTATION_0 = 0,
+  ROTATION_90,
+  ROTATION_180,
+  ROTATION_270,
+
+  ROTATION_COUNT
+};
+
+gfxMatrix ComputeGLTransformForRotation(const nsIntRect& aBounds,
+                                        ScreenRotation aRotation);
+
+} // namespace mozilla
+
+#endif // mozilla_WidgetUtils_h
--- a/widget/gonk/nsWindow.cpp
+++ b/widget/gonk/nsWindow.cpp
@@ -63,16 +63,22 @@ static android::FramebufferNativeWindow 
 static bool sFramebufferOpen;
 static bool sUsingOMTC;
 static bool sScreenInitialized;
 static nsRefPtr<gfxASurface> sOMTCSurface;
 static pthread_t sFramebufferWatchThread;
 
 namespace {
 
+static PRUint32
+EffectiveScreenRotation()
+{
+    return (sScreenRotation + sPhysicalScreenRotation) % (360 / 90);
+}
+
 class ScreenOnOffEvent : public nsRunnable {
 public:
     ScreenOnOffEvent(bool on)
         : mIsOn(on)
     {}
 
     NS_IMETHOD Run() {
         nsSizeModeEvent event(true, NS_SIZEMODE, NULL);
@@ -231,17 +237,18 @@ nsWindow::DoDraw(void)
 
         {
             nsRefPtr<gfxContext> ctx = new gfxContext(targetSurface);
             gfxUtils::PathFromRegion(ctx, event.region);
             ctx->Clip();
 
             // No double-buffering needed.
             AutoLayerManagerSetup setupLayerManager(
-                gWindowToRedraw, ctx, BasicLayerManager::BUFFER_NONE);
+                gWindowToRedraw, ctx, BasicLayerManager::BUFFER_NONE,
+                ScreenRotation(EffectiveScreenRotation()));
             gWindowToRedraw->mEventCallback(&event);
         }
 
         if (!sUsingOMTC) {
             targetSurface->Flush();
             Framebuffer::Present(event.region);
         }
     } else {
@@ -597,16 +604,22 @@ nsWindow::GetGLFrameBufferFormat()
         mLayerManager->GetBackendType() == mozilla::layers::LAYERS_OPENGL) {
         // We directly map the hardware fb on Gonk.  The hardware fb
         // has RGB format.
         return LOCAL_GL_RGB;
     }
     return LOCAL_GL_NONE;
 }
 
+nsIntRect
+nsWindow::GetNaturalBounds()
+{
+    return gScreenBounds;
+}
+
 // nsScreenGonk.cpp
 
 nsScreenGonk::nsScreenGonk(void *nativeScreen)
 {
 }
 
 nsScreenGonk::~nsScreenGonk()
 {
@@ -671,42 +684,26 @@ nsScreenGonk::SetRotation(PRUint32 aRota
 {
     if (!(ROTATION_0_DEG <= aRotation && aRotation <= ROTATION_270_DEG))
         return NS_ERROR_ILLEGAL_VALUE;
 
     if (sScreenRotation == aRotation)
         return NS_OK;
 
     sScreenRotation = aRotation;
-    sRotationMatrix.Reset();
-    switch ((aRotation + sPhysicalScreenRotation) % (360 / 90)) {
-    case nsIScreen::ROTATION_0_DEG:
-        sVirtualBounds = gScreenBounds;
-        break;
-    case nsIScreen::ROTATION_90_DEG:
-        sRotationMatrix.Translate(gfxPoint(gScreenBounds.width, 0));
-        sRotationMatrix.Rotate(M_PI / 2);
+    sRotationMatrix =
+        ComputeGLTransformForRotation(gScreenBounds,
+                                      ScreenRotation(EffectiveScreenRotation()));
+    PRUint32 rotation = EffectiveScreenRotation();
+    if (rotation == nsIScreen::ROTATION_90_DEG ||
+        rotation == nsIScreen::ROTATION_270_DEG) {
         sVirtualBounds = nsIntRect(0, 0, gScreenBounds.height,
-                                         gScreenBounds.width);
-        break;
-    case nsIScreen::ROTATION_180_DEG:
-        sRotationMatrix.Translate(gfxPoint(gScreenBounds.width,
-                                           gScreenBounds.height));
-        sRotationMatrix.Rotate(M_PI);
+                                   gScreenBounds.width);
+    } else {
         sVirtualBounds = gScreenBounds;
-        break;
-    case nsIScreen::ROTATION_270_DEG:
-        sRotationMatrix.Translate(gfxPoint(0, gScreenBounds.height));
-        sRotationMatrix.Rotate(M_PI * 3 / 2);
-        sVirtualBounds = nsIntRect(0, 0, gScreenBounds.height,
-                                         gScreenBounds.width);
-        break;
-    default:
-        MOZ_NOT_REACHED("Unknown rotation");
-        break;
     }
 
     for (unsigned int i = 0; i < sTopWindows.Length(); i++)
         sTopWindows[i]->Resize(sVirtualBounds.width,
                                sVirtualBounds.height,
                                !i);
 
     nsAppShell::NotifyScreenRotation();
--- a/widget/gonk/nsWindow.h
+++ b/widget/gonk/nsWindow.h
@@ -100,16 +100,18 @@ public:
     gfxASurface* GetThebesSurface();
 
     NS_IMETHOD_(void) SetInputContext(const InputContext& aContext,
                                       const InputContextAction& aAction);
     NS_IMETHOD_(InputContext) GetInputContext();
 
     virtual PRUint32 GetGLFrameBufferFormat() MOZ_OVERRIDE;
 
+    virtual nsIntRect GetNaturalBounds() MOZ_OVERRIDE;
+
 protected:
     nsWindow* mParent;
     bool mVisible;
     nsIntRegion mDirtyRegion;
     InputContext mInputContext;
     nsCOMPtr<nsIIdleServiceInternal> mIdleService;
 
     void BringToTop();
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -82,18 +82,18 @@ typedef nsEventStatus (* EVENT_CALLBACK)
 #endif
 #ifdef XP_WIN
 #define NS_NATIVE_TSF_THREAD_MGR       100
 #define NS_NATIVE_TSF_CATEGORY_MGR     101
 #define NS_NATIVE_TSF_DISPLAY_ATTR_MGR 102
 #endif
 
 #define NS_IWIDGET_IID \
-  { 0x97afe930, 0x72d7, 0x4d95, \
-    { 0x88, 0x5f, 0x37, 0x09, 0x14, 0x2a, 0xf4, 0xe2 } }
+  { 0x91aafae4, 0xd814, 0x4803, \
+    { 0x9a, 0xf5, 0xb0, 0x2f, 0x1b, 0x2c, 0xaf, 0x57 } }
 
 /*
  * Window shadow styles
  * Also used for the -moz-window-shadow CSS property
  */
 
 #define NS_STYLE_WINDOW_SHADOW_NONE             0
 #define NS_STYLE_WINDOW_SHADOW_DEFAULT          1
@@ -1565,16 +1565,34 @@ class nsIWidget : public nsISupports {
 
     /**
      * Returns true to indicate that this widget paints an opaque background
      * that we want to be visible under the page, so layout should not force
      * a default background.
      */
     virtual bool WidgetPaintsBackground() { return false; }
 
+    /**
+     * Get the natural bounds of this widget.  This method is only
+     * meaningful for widgets for which Gecko implements screen
+     * rotation natively.  When this is the case, GetBounds() returns
+     * the widget bounds taking rotation into account, and
+     * GetNaturalBounds() returns the bounds *not* taking rotation
+     * into account.
+     *
+     * No code outside of the composition pipeline should know or care
+     * about this.  If you're not an agent of the compositor, you
+     * probably shouldn't call this method.
+     */
+    virtual nsIntRect GetNaturalBounds() {
+        nsIntRect bounds;
+        GetBounds(bounds);
+        return bounds;
+    }
+
 protected:
 
     // keep the list of children.  We also keep track of our siblings.
     // The ownership model is as follows: parent holds a strong ref to
     // the first element of the list, and each element holds a strong
     // ref to the next element in the list.  The prevsibling and
     // lastchild pointers are weak, which is fine as long as they are
     // maintained properly.
--- a/widget/xpwidgets/Makefile.in
+++ b/widget/xpwidgets/Makefile.in
@@ -38,16 +38,17 @@ CPPSRCS		= \
 		nsPrintSession.cpp \
 		nsIdleService.cpp \
 		nsClipboardPrivacyHandler.cpp \
 		GfxInfoWebGL.cpp \
                 GfxDriverInfo.cpp \
                 GfxInfoBase.cpp \
 		PuppetWidget.cpp \
 		nsFilePickerProxy.cpp \
+		WidgetUtils.cpp \
 		$(NULL)
 
 ifdef MOZ_X11
 CPPSRCS		+= \
 		GfxInfoX11.cpp
 endif
 
 ifneq (,$(filter os2 cocoa windows,$(MOZ_WIDGET_TOOLKIT)))
new file mode 100644
--- /dev/null
+++ b/widget/xpwidgets/WidgetUtils.cpp
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#include "mozilla/WidgetUtils.h"
+
+namespace mozilla {
+
+gfxMatrix
+ComputeGLTransformForRotation(const nsIntRect& aBounds,
+                              ScreenRotation aRotation)
+{
+    gfxMatrix transform;
+    switch (aRotation) {
+    case ROTATION_0:
+        break;
+    case ROTATION_90:
+        transform.Translate(gfxPoint(aBounds.width, 0));
+        transform.Rotate(M_PI / 2);
+        break;
+    case ROTATION_180:
+        transform.Translate(gfxPoint(aBounds.width, aBounds.height));
+        transform.Rotate(M_PI);
+        break;
+    case ROTATION_270:
+        transform.Translate(gfxPoint(0, aBounds.height));
+        transform.Rotate(M_PI * 3 / 2);
+        break;
+    default:
+        MOZ_NOT_REACHED("Unknown rotation");
+        break;
+    }
+    return transform;
+}
+
+} // namespace mozilla
--- a/widget/xpwidgets/nsBaseWidget.cpp
+++ b/widget/xpwidgets/nsBaseWidget.cpp
@@ -726,36 +726,37 @@ NS_IMETHODIMP nsBaseWidget::MakeFullScre
            mOriginalBounds->height, true);
   }
 
   return NS_OK;
 }
 
 nsBaseWidget::AutoLayerManagerSetup::AutoLayerManagerSetup(
     nsBaseWidget* aWidget, gfxContext* aTarget,
-    BasicLayerManager::BufferMode aDoubleBuffering)
+    BasicLayerManager::BufferMode aDoubleBuffering, ScreenRotation aRotation)
   : mWidget(aWidget)
 {
   BasicLayerManager* manager =
     static_cast<BasicLayerManager*>(mWidget->GetLayerManager());
   if (manager) {
     NS_ASSERTION(manager->GetBackendType() == LAYERS_BASIC,
       "AutoLayerManagerSetup instantiated for non-basic layer backend!");
-    manager->SetDefaultTarget(aTarget, aDoubleBuffering);
+    manager->SetDefaultTarget(aTarget, aDoubleBuffering, aRotation);
   }
 }
 
 nsBaseWidget::AutoLayerManagerSetup::~AutoLayerManagerSetup()
 {
   BasicLayerManager* manager =
     static_cast<BasicLayerManager*>(mWidget->GetLayerManager());
   if (manager) {
     NS_ASSERTION(manager->GetBackendType() == LAYERS_BASIC,
       "AutoLayerManagerSetup instantiated for non-basic layer backend!");
-    manager->SetDefaultTarget(nsnull, BasicLayerManager::BUFFER_NONE);
+    manager->SetDefaultTarget(nsnull, BasicLayerManager::BUFFER_NONE,
+                              ROTATION_0);
   }
 }
 
 nsBaseWidget::AutoUseBasicLayerManager::AutoUseBasicLayerManager(nsBaseWidget* aWidget)
   : mWidget(aWidget)
 {
   mWidget->mTemporarilyUseBasicLayerManager = true;
 }
--- a/widget/xpwidgets/nsBaseWidget.h
+++ b/widget/xpwidgets/nsBaseWidget.h
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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 nsBaseWidget_h__
 #define nsBaseWidget_h__
 
+#include "mozilla/WidgetUtils.h"
 #include "nsRect.h"
 #include "nsIWidget.h"
 #include "nsWidgetsCID.h"
 #include "nsIFile.h"
 #include "nsString.h"
 #include "nsCOMPtr.h"
 #include "nsGUIEvent.h"
 #include "nsAutoPtr.h"
@@ -41,20 +42,21 @@ class Thread;
  * class, but it gives them a head start.)
  */
 
 class nsBaseWidget : public nsIWidget
 {
   friend class nsAutoRollup;
 
 protected:
+  typedef base::Thread Thread;
   typedef mozilla::layers::BasicLayerManager BasicLayerManager;
   typedef mozilla::layers::CompositorChild CompositorChild;
   typedef mozilla::layers::CompositorParent CompositorParent;
-  typedef base::Thread Thread;
+  typedef mozilla::ScreenRotation ScreenRotation;
 
 public:
   nsBaseWidget();
   virtual ~nsBaseWidget();
   
   NS_DECL_ISUPPORTS
   
   // nsIWidget interface
@@ -177,21 +179,27 @@ public:
   NS_IMETHOD              ReparentNativeWidget(nsIWidget* aNewParent) = 0;
 
   virtual PRUint32 GetGLFrameBufferFormat() MOZ_OVERRIDE;
 
   /**
    * Use this when GetLayerManager() returns a BasicLayerManager
    * (nsBaseWidget::GetLayerManager() does). This sets up the widget's
    * layer manager to temporarily render into aTarget.
+   *
+   * |aNaturalWidgetBounds| is the un-rotated bounds of |aWidget|.
+   * |aRotation| is the "virtual rotation" to apply when rendering to
+   * the target.  When |aRotation| is ROTATION_0,
+   * |aNaturalWidgetBounds| is not used.
    */
   class AutoLayerManagerSetup {
   public:
     AutoLayerManagerSetup(nsBaseWidget* aWidget, gfxContext* aTarget,
-                          BasicLayerManager::BufferMode aDoubleBuffering);
+                          BasicLayerManager::BufferMode aDoubleBuffering,
+                          ScreenRotation aRotation = mozilla::ROTATION_0);
     ~AutoLayerManagerSetup();
   private:
     nsBaseWidget* mWidget;
   };
   friend class AutoLayerManagerSetup;
 
   class AutoUseBasicLayerManager {
   public: