Bug 805939: Wait for shadow trees to update orientation before recompositing. r=cjones
☠☠ backed out by bcb3c5c91c28 ☠ ☠
authorChiajung Hung <chung@mozilla.com>
Thu, 22 Nov 2012 10:40:57 +0800
changeset 116743 cf585138ff25010a27e6cc1120242b29dcf9ac64
parent 116742 cca7f05e2c053daeedd7a95e843fe29f740db8e4
child 116744 72eead57d12ce21dfa3a8c1ccb0e4d52294f72c1
push id24072
push userMs2ger@gmail.com
push dateSat, 22 Dec 2012 13:18:22 +0000
treeherdermozilla-central@ea373e534245 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscjones
bugs805939
milestone20.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 805939: Wait for shadow trees to update orientation before recompositing. r=cjones
b2g/app/b2g.js
dom/base/ScreenOrientation.h
dom/ipc/Makefile.in
dom/ipc/PBrowser.ipdl
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
dom/ipc/TabParent.cpp
gfx/layers/Makefile.in
gfx/layers/basic/BasicLayerManager.cpp
gfx/layers/ipc/CompositorParent.cpp
gfx/layers/ipc/CompositorParent.h
gfx/layers/ipc/PLayers.ipdl
gfx/layers/ipc/ShadowLayers.cpp
gfx/layers/ipc/ShadowLayers.h
gfx/layers/opengl/LayerManagerOGL.cpp
gfx/layers/opengl/LayerManagerOGL.h
gfx/thebes/gfxPlatform.cpp
gfx/thebes/gfxPlatform.h
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -610,8 +610,11 @@ pref("font.size.inflation.disabledInMast
 
 // Enable freeing dirty pages when minimizing memory; this reduces memory
 // consumption when applications are sent to the background.
 pref("memory.free_dirty_pages", true);
 
 // UAProfile settings
 pref("wap.UAProf.url", "");
 pref("wap.UAProf.tagname", "x-wap-profile");
+
+// Wait up to this much milliseconds when orientation changed
+pref("layers.orientation.sync.timeout", 1000);
--- a/dom/base/ScreenOrientation.h
+++ b/dom/base/ScreenOrientation.h
@@ -1,17 +1,15 @@
 /* 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_dom_ScreenOrientation_h
 #define mozilla_dom_ScreenOrientation_h
 
-#include "ipc/IPCMessageUtils.h"
-
 namespace mozilla {
 namespace dom {
 
 // Make sure that any change here is also made in
 // * mobile/android/base/GeckoScreenOrientationListener.java
 // * embedding/android/GeckoScreenOrientationListener.java
 typedef uint32_t ScreenOrientation;
 
--- a/dom/ipc/Makefile.in
+++ b/dom/ipc/Makefile.in
@@ -89,16 +89,17 @@ LOCAL_INCLUDES += \
 	-I$(topsrcdir)/dom/base \
 	-I$(topsrcdir)/toolkit/xre \
 	-I$(topsrcdir)/hal/sandbox \
 	-I$(topsrcdir)/dom/sms/src/ipc \
 	-I$(topsrcdir)/dom/devicestorage \
 	-I$(topsrcdir)/widget/xpwidgets \
 	-I$(topsrcdir)/dom/bluetooth \
 	-I$(topsrcdir)/dom/bluetooth/ipc \
+        -I$(topsrcdir)/hal \
 	$(NULL)
 
 DEFINES += -DBIN_SUFFIX='"$(BIN_SUFFIX)"'
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),$(findstring $(MOZ_WIDGET_TOOLKIT),android gtk2 gonk qt))
 DEFINES += -DMOZ_ENABLE_FREETYPE
 endif
 
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -15,16 +15,17 @@ include protocol POfflineCacheUpdate;
 include protocol PIndexedDB;
 include DOMTypes;
 include URIParams;
 
 include "gfxMatrix.h";
 include "FrameMetrics.h";
 include "IPC/nsGUIEventIPC.h";
 include "mozilla/dom/TabMessageUtils.h";
+include "mozilla/dom/ScreenOrientation.h";
 include "mozilla/dom/PermissionMessageUtils.h";
 include "mozilla/layout/RenderFrameUtils.h";
 
 using IPC::Principal;
 using gfxMatrix;
 using gfxRect;
 using gfxSize;
 using mozilla::layers::LayersBackend;
@@ -42,16 +43,17 @@ using nsMouseEvent;
 using nsMouseScrollEvent;
 using mozilla::widget::WheelEvent;
 using nsQueryContentEvent;
 using nsRect;
 using nsSelectionEvent;
 using nsTextEvent;
 using nsTouchEvent;
 using RemoteDOMEvent;
+using mozilla::dom::ScreenOrientation;
 
 namespace mozilla {
 namespace dom {
 
 rpc protocol PBrowser
 {
     manager PContent;
 
@@ -283,17 +285,17 @@ child:
      * |Show()| and |Move()| take IntSizes rather than Rects because
      * content processes always render to a virtual <0, 0> top-left
      * point.
      */
     Show(nsIntSize size);
 
     LoadURL(nsCString uri);
 
-    UpdateDimensions(nsRect rect, nsIntSize size) compress;
+    UpdateDimensions(nsRect rect, nsIntSize size, ScreenOrientation orientation) compress;
 
     UpdateFrame(FrameMetrics frame) compress;
 
     /**
      * Requests handling of a double tap. |point| is in CSS pixels, relative to
      * the scroll offset. This message is expected to round-trip back to
      * ZoomToRect() with a rect indicating where we should zoom to.
      */
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -163,16 +163,17 @@ TabChild::TabChild(const TabContext& aCo
   , mOuterRect(0, 0, 0, 0)
   , mInnerSize(0, 0)
   , mOldViewportWidth(0.0f)
   , mLastBackgroundColor(NS_RGB(255, 255, 255))
   , mDidFakeShow(false)
   , mNotified(false)
   , mContentDocumentIsDisplayed(false)
   , mTriedBrowserInit(false)
+  , mOrientation(eScreenOrientation_PortraitPrimary)
 {
     printf("creating %d!\n", NS_IsMainThread());
 }
 
 NS_IMETHODIMP
 TabChild::HandleEvent(nsIDOMEvent* aEvent)
 {
   nsAutoString eventType;
@@ -1114,27 +1115,28 @@ TabChild::RecvShow(const nsIntSize& size
     }
 
     baseWindow->SetVisibility(true);
 
     return InitTabChildGlobal();
 }
 
 bool
-TabChild::RecvUpdateDimensions(const nsRect& rect, const nsIntSize& size)
+TabChild::RecvUpdateDimensions(const nsRect& rect, const nsIntSize& size, const ScreenOrientation& orientation)
 {
     if (!mRemoteFrame) {
         return true;
     }
 
     mOuterRect.x = rect.x;
     mOuterRect.y = rect.y;
     mOuterRect.width = rect.width;
     mOuterRect.height = rect.height;
 
+    mOrientation = orientation;
     mInnerSize = size;
     mWidget->Resize(0, 0, size.width, size.height,
                     true);
 
     nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(mWebNav);
     baseWin->SetPositionAndSize(0, 0, size.width, size.height,
                                 true);
 
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -192,17 +192,17 @@ public:
     virtual bool DoSendSyncMessage(const nsAString& aMessage,
                                    const mozilla::dom::StructuredCloneData& aData,
                                    InfallibleTArray<nsString>* aJSONRetVal);
     virtual bool DoSendAsyncMessage(const nsAString& aMessage,
                                     const mozilla::dom::StructuredCloneData& aData);
 
     virtual bool RecvLoadURL(const nsCString& uri);
     virtual bool RecvShow(const nsIntSize& size);
-    virtual bool RecvUpdateDimensions(const nsRect& rect, const nsIntSize& size);
+    virtual bool RecvUpdateDimensions(const nsRect& rect, const nsIntSize& size, const ScreenOrientation& orientation);
     virtual bool RecvUpdateFrame(const mozilla::layers::FrameMetrics& aFrameMetrics);
     virtual bool RecvHandleDoubleTap(const nsIntPoint& aPoint);
     virtual bool RecvHandleSingleTap(const nsIntPoint& aPoint);
     virtual bool RecvHandleLongTap(const nsIntPoint& aPoint);
     virtual bool RecvActivate();
     virtual bool RecvDeactivate();
     virtual bool RecvMouseEvent(const nsString& aType,
                                 const float&    aX,
@@ -286,16 +286,18 @@ public:
 
     nsIPrincipal* GetPrincipal() { return mPrincipal; }
 
     /** Return the DPI of the widget this TabChild draws to. */
     void GetDPI(float* aDPI);
 
     gfxSize GetZoom() { return mLastMetrics.mZoom; }
 
+    ScreenOrientation GetOrientation() { return mOrientation; }
+
     void SetBackgroundColor(const nscolor& aColor);
 
     void NotifyPainted();
 
     bool IsAsyncPanZoomEnabled();
 
     /**
      * Signal to this TabChild that it should be made visible:
@@ -412,16 +414,17 @@ private:
     float mOldViewportWidth;
     nscolor mLastBackgroundColor;
     ScrollingBehavior mScrolling;
     bool mDidFakeShow;
     bool mNotified;
     bool mContentDocumentIsDisplayed;
     bool mTriedBrowserInit;
     nsString mAppType;
+    ScreenOrientation mOrientation;
 
     DISALLOW_EVIL_CONSTRUCTORS(TabChild);
 };
 
 inline TabChild*
 GetTabChildFrom(nsIDocShell* aDocShell)
 {
     nsCOMPtr<nsITabChild> tc = do_GetInterface(aDocShell);
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -46,16 +46,17 @@
 #include "nsNetUtil.h"
 #include "nsPIDOMWindow.h"
 #include "nsPrintfCString.h"
 #include "nsSerializationHelper.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
 #include "StructuredCloneUtils.h"
 #include "TabChild.h"
+#include "Hal.h"
 
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
 using namespace mozilla::layers;
 using namespace mozilla::layout;
 using namespace mozilla::services;
 using namespace mozilla::widget;
 using namespace mozilla::dom::indexedDB;
@@ -251,17 +252,20 @@ TabParent::Show(const nsIntSize& size)
 }
 
 void
 TabParent::UpdateDimensions(const nsRect& rect, const nsIntSize& size)
 {
   if (mIsDestroyed) {
     return;
   }
-  unused << SendUpdateDimensions(rect, size);
+  hal::ScreenConfiguration config;
+  hal::GetCurrentScreenConfiguration(&config);
+
+  unused << SendUpdateDimensions(rect, size, config.orientation());
   if (RenderFrameParent* rfp = GetRenderFrame()) {
     rfp->NotifyDimensionsChanged(size.width, size.height);
   }
   mDimensions = size;
 }
 
 void
 TabParent::UpdateFrame(const FrameMetrics& aFrameMetrics)
--- a/gfx/layers/Makefile.in
+++ b/gfx/layers/Makefile.in
@@ -194,16 +194,17 @@ CPPSRCS += \
 endif
 
 include $(topsrcdir)/config/rules.mk
 
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
 
 LOCAL_INCLUDES += \
         -I$(topsrcdir)/content/events/src \
+        -I$(topsrcdir)/hal \
         -I$(ANDROID_SOURCE)/frameworks/base/include/media/stagefright \
         -I$(ANDROID_SOURCE)/frameworks/base/include/media/stagefright/openmax \
         $(NULL)
 
 CXXFLAGS += $(MOZ_CAIRO_CFLAGS) $(MOZ_PIXMAN_CFLAGS) $(TK_CFLAGS)
 
 ifdef _MSC_VER
 ifeq ($(CPU_ARCH),x86_64)
--- a/gfx/layers/basic/BasicLayerManager.cpp
+++ b/gfx/layers/basic/BasicLayerManager.cpp
@@ -10,16 +10,17 @@
 
 #include "gfxSharedImageSurface.h"
 #include "gfxImageSurface.h"
 #include "gfxUtils.h"
 #include "gfxPlatform.h"
 #include "nsXULAppAPI.h"
 #include "RenderTrace.h"
 #include "sampler.h"
+#include "Hal.h"
 
 #define PIXMAN_DONT_DEFINE_STDINT
 #include "pixman.h"
 
 #include "BasicTiledThebesLayer.h"
 #include "BasicLayersImpl.h"
 #include "BasicThebesLayer.h"
 #include "BasicContainerLayer.h"
@@ -1089,17 +1090,27 @@ 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(mTargetBounds, mTargetRotation);
+    ScreenOrientation orientation;
+    nsIntRect clientBounds;
+    if (TabChild* window = mWidget->GetOwningTabChild()) {
+      orientation = window->GetOrientation();
+    } else {
+      hal::ScreenConfiguration currentConfig;
+      hal::GetCurrentScreenConfiguration(&currentConfig);
+      orientation = currentConfig.orientation();
+    }
+    mWidget->GetClientBounds(clientBounds);
+    ShadowLayerForwarder::BeginTransaction(mTargetBounds, mTargetRotation, clientBounds, orientation);
 
     // If we're drawing on behalf of a context with async pan/zoom
     // enabled, then the entire buffer of thebes layers might be
     // composited (including resampling) asynchronously before we get
     // a chance to repaint, so we have to ensure that it's all valid
     // and not rotated.
     if (mWidget) {
       if (TabChild* window = mWidget->GetOwningTabChild()) {
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -29,19 +29,24 @@
 #include "nsIWidget.h"
 #include "nsGkAtoms.h"
 #include "RenderTrace.h"
 #include "nsStyleAnimation.h"
 #include "nsDisplayList.h"
 #include "AnimationCommon.h"
 #include "nsAnimationManager.h"
 #include "TiledLayerBuffer.h"
+#include "gfxPlatform.h"
+#include "mozilla/dom/ScreenOrientation.h"
+#include "mozilla/AutoRestore.h"
 
 using namespace base;
+using namespace mozilla;
 using namespace mozilla::ipc;
+using namespace mozilla::dom;
 using namespace std;
 
 namespace mozilla {
 namespace layers {
 
 // FIXME/bug 774386: we're assuming that there's only one
 // CompositorParent, but that's not always true.  This assumption only
 // affects CrossProcessCompositorParent below.
@@ -55,16 +60,17 @@ static MessageLoop* sMainLoop = nullptr;
 // duplicated globals, because there's no API to make a Thread from an
 // existing thread.
 static PlatformThreadId sCompositorThreadID = 0;
 static MessageLoop* sCompositorLoop = nullptr;
 
 struct LayerTreeState {
   nsRefPtr<Layer> mRoot;
   nsRefPtr<AsyncPanZoomController> mController;
+  TargetConfig mTargetConfig;
 };
 
 static uint8_t sPanZoomUserDataKey;
 struct PanZoomUserData : public LayerUserData {
   PanZoomUserData(AsyncPanZoomController* aController)
     : mController(aController)
   { }
 
@@ -167,16 +173,18 @@ CompositorParent::CompositorParent(nsIWi
   , mXScale(1.0)
   , mYScale(1.0)
   , mIsFirstPaint(false)
   , mLayersUpdated(false)
   , mRenderToEGLSurface(aRenderToEGLSurface)
   , mEGLSurfaceSize(aSurfaceWidth, aSurfaceHeight)
   , mPauseCompositionMonitor("PauseCompositionMonitor")
   , mResumeCompositionMonitor("ResumeCompositionMonitor")
+  , mForceCompositionTask(nullptr)
+  , mOverrideComposeReadiness(false)
 {
   NS_ABORT_IF_FALSE(sCompositorThread != nullptr || sCompositorThreadID,
                     "The compositor thread must be Initialized before instanciating a COmpositorParent.");
   MOZ_COUNT_CTOR(CompositorParent);
   mCompositorID = 0;
   // FIXME: This holds on the the fact that right now the only thing that 
   // can destroy this instance is initialized on the compositor thread after 
   // this task has been processed.
@@ -342,16 +350,24 @@ CompositorParent::ResumeComposition()
 
   Composite();
 
   // if anyone's waiting to make sure that composition really got resumed, tell them
   lock.NotifyAll();
 }
 
 void
+CompositorParent::ForceComposition()
+{
+  // Cancel the orientation changed state to force composition
+  mForceCompositionTask = nullptr;
+  ScheduleRenderOnCompositorThread();
+}
+
+void
 CompositorParent::SetEGLSurfaceSize(int width, int height)
 {
   NS_ASSERTION(mRenderToEGLSurface, "Compositor created without RenderToEGLSurface ar provided");
   mEGLSurfaceSize.SizeTo(width, height);
   if (mLayerManager) {
     static_cast<LayerManagerOGL*>(mLayerManager.get())->SetSurfaceSize(mEGLSurfaceSize.width, mEGLSurfaceSize.height);
   }
 }
@@ -464,42 +480,54 @@ CompositorParent::SetTransformation(floa
  */
 class NS_STACK_CLASS AutoResolveRefLayers {
 public:
   /**
    * |aRoot| must remain valid in the scope of this, which should be
    * guaranteed by this helper only being used during the drawing
    * phase.
    */
-  AutoResolveRefLayers(Layer* aRoot) : mRoot(aRoot)
+  AutoResolveRefLayers(Layer* aRoot, const TargetConfig& aConfig) : mRoot(aRoot), mTargetConfig(aConfig), mReadyForCompose(true)
   { WalkTheTree<Resolve>(mRoot, nullptr); }
 
   ~AutoResolveRefLayers()
   { WalkTheTree<Detach>(mRoot, nullptr); }
 
+  bool IsReadyForCompose()
+  { return mReadyForCompose; }
+
 private:
   enum Op { Resolve, Detach };
   template<Op OP>
   void WalkTheTree(Layer* aLayer, Layer* aParent)
   {
     if (RefLayer* ref = aLayer->AsRefLayer()) {
       if (const LayerTreeState* state = GetIndirectShadowTree(ref->GetReferentId())) {
-        if (Layer* referent = state->mRoot) {
-          if (OP == Resolve) {
-            ref->ConnectReferentLayer(referent);
-            if (AsyncPanZoomController* apzc = state->mController) {
-              referent->SetUserData(&sPanZoomUserDataKey,
-                                    new PanZoomUserData(apzc));
-            } else {
-              CompensateForContentScrollOffset(ref, referent);
-            }
+        Layer* referent = state->mRoot;
+
+        if (!ref->GetVisibleRegion().IsEmpty()) {
+          ScreenOrientation chromeOrientation = mTargetConfig.orientation();
+          ScreenOrientation contentOrientation = state->mTargetConfig.orientation();
+          if (!IsSameDimension(chromeOrientation, contentOrientation) &&
+              ContentMightReflowOnOrientationChange(mTargetConfig.clientBounds())) {
+            mReadyForCompose = false;
+          }
+        }
+
+        if (OP == Resolve) {
+          ref->ConnectReferentLayer(referent);
+          if (AsyncPanZoomController* apzc = state->mController) {
+            referent->SetUserData(&sPanZoomUserDataKey,
+                                  new PanZoomUserData(apzc));
           } else {
-            ref->DetachReferentLayer(referent);
-            referent->RemoveUserData(&sPanZoomUserDataKey);
+            CompensateForContentScrollOffset(ref, referent);
           }
+        } else {
+          ref->DetachReferentLayer(referent);
+          referent->RemoveUserData(&sPanZoomUserDataKey);
         }
       }
     }
     for (Layer* child = aLayer->GetFirstChild();
          child; child = child->GetNextSibling()) {
       WalkTheTree<OP>(child, aLayer);
     }
   }
@@ -524,17 +552,29 @@ private:
             1.0f/c->GetPreYScale(),
             1);
     m.ScalePost(1.0f/c->GetPostXScale(),
                 1.0f/c->GetPostYScale(),
                 1);
     aContainer->AsShadowLayer()->SetShadowTransform(m);
   }
 
+  bool IsSameDimension(ScreenOrientation o1, ScreenOrientation o2) {
+    bool isO1portrait = (o1 == eScreenOrientation_PortraitPrimary || o1 == eScreenOrientation_PortraitSecondary);
+    bool isO2portrait = (o2 == eScreenOrientation_PortraitPrimary || o2 == eScreenOrientation_PortraitSecondary);
+    return !(isO1portrait ^ isO2portrait);
+  }
+
+  bool ContentMightReflowOnOrientationChange(nsIntRect& rect) {
+    return rect.width != rect.height;
+  }
+
   Layer* mRoot;
+  TargetConfig mTargetConfig;
+  bool mReadyForCompose;
 
   AutoResolveRefLayers(const AutoResolveRefLayers&) MOZ_DELETE;
   AutoResolveRefLayers& operator=(const AutoResolveRefLayers&) MOZ_DELETE;
 };
 
 void
 CompositorParent::Composite()
 {
@@ -544,17 +584,25 @@ CompositorParent::Composite()
 
   mLastCompose = TimeStamp::Now();
 
   if (!CanComposite()) {
     return;
   }
 
   Layer* layer = mLayerManager->GetRoot();
-  AutoResolveRefLayers resolve(layer);
+  AutoResolveRefLayers resolve(layer, mTargetConfig);
+  if (mForceCompositionTask && !mOverrideComposeReadiness) {
+    if (!resolve.IsReadyForCompose()) {
+      return;
+    } else {
+      mForceCompositionTask->Cancel();
+      mForceCompositionTask = nullptr;
+    }
+  }
 
   bool requestNextFrame = TransformShadowTree(mLastCompose);
   if (requestNextFrame) {
     ScheduleComposition();
   }
 
   RenderTraceLayers(layer, "0000");
 
@@ -573,19 +621,23 @@ CompositorParent::Composite()
                   15 + (int)(TimeStamp::Now() - mExpectedComposeTime).ToMilliseconds());
   }
 #endif
 }
 
 void
 CompositorParent::ComposeToTarget(gfxContext* aTarget)
 {
+  AutoRestore<bool> override(mOverrideComposeReadiness);
+  mOverrideComposeReadiness = true;
+
   if (!CanComposite()) {
     return;
   }
+
   mLayerManager->BeginTransactionWithTarget(aTarget);
   // Since CanComposite() is true, Composite() must end the layers txn
   // we opened above.
   Composite();
 }
 
 bool
 CompositorParent::CanComposite()
@@ -985,16 +1037,32 @@ CompositorParent::SyncViewportInfo(const
 #endif
 }
 
 void
 CompositorParent::ShadowLayersUpdated(ShadowLayersParent* aLayerTree,
                                       const TargetConfig& aTargetConfig,
                                       bool isFirstPaint)
 {
+  if (!isFirstPaint && !mIsFirstPaint && mTargetConfig.orientation() != aTargetConfig.orientation()) {
+    if (mForceCompositionTask != NULL) {
+      mForceCompositionTask->Cancel();
+    }
+    mForceCompositionTask = NewRunnableMethod(this, &CompositorParent::ForceComposition);
+    ScheduleTask(mForceCompositionTask, gfxPlatform::GetPlatform()->GetOrientationSyncMillis());
+  }
+
+  // Instruct the LayerManager to update its render bounds now. Since all the orientation
+  // change, dimension change would be done at the stage, update the size here is free of
+  // race condition.
+  if (LAYERS_OPENGL == mLayerManager->GetBackendType()) {
+    LayerManagerOGL* lm = static_cast<LayerManagerOGL*>(mLayerManager.get());
+    lm->UpdateRenderBounds(aTargetConfig.clientBounds());
+  }
+
   mTargetConfig = aTargetConfig;
   mIsFirstPaint = mIsFirstPaint || isFirstPaint;
   mLayersUpdated = true;
   Layer* root = aLayerTree->GetRoot();
   mLayerManager->SetRoot(root);
   if (root) {
     SetShadowProperties(root);
   }
@@ -1247,19 +1315,20 @@ CompositorParent::Create(Transport* aTra
     NewRunnableFunction(OpenCompositor, cpcp.get(),
                         aTransport, handle, XRE_GetIOMessageLoop()));
   // The return value is just compared to null for success checking,
   // we're not sharing a ref.
   return cpcp.get();
 }
 
 static void
-UpdateIndirectTree(uint64_t aId, Layer* aRoot, bool isFirstPaint)
+UpdateIndirectTree(uint64_t aId, Layer* aRoot, const TargetConfig& aTargetConfig, bool isFirstPaint)
 {
   sIndirectLayerTrees[aId].mRoot = aRoot;
+  sIndirectLayerTrees[aId].mTargetConfig = aTargetConfig;
   if (ContainerLayer* root = aRoot->AsContainerLayer()) {
     if (AsyncPanZoomController* apzc = sIndirectLayerTrees[aId].mController) {
       apzc->NotifyLayersUpdated(root->GetFrameMetrics(), isFirstPaint);
     }
   }
 }
 
 static const LayerTreeState*
@@ -1316,17 +1385,17 @@ CrossProcessCompositorParent::ShadowLaye
   bool isFirstPaint)
 {
   uint64_t id = aLayerTree->GetId();
   MOZ_ASSERT(id != 0);
   Layer* shadowRoot = aLayerTree->GetRoot();
   if (shadowRoot) {
     SetShadowProperties(shadowRoot);
   }
-  UpdateIndirectTree(id, shadowRoot, isFirstPaint);
+  UpdateIndirectTree(id, shadowRoot, aTargetConfig, isFirstPaint);
 
   sCurrentCompositor->NotifyShadowTreeTransaction();
 }
 
 void
 CrossProcessCompositorParent::DeferredDestroy()
 {
   mSelfRef = NULL;
--- a/gfx/layers/ipc/CompositorParent.h
+++ b/gfx/layers/ipc/CompositorParent.h
@@ -53,16 +53,17 @@ struct ViewTransform {
   float mXScale;
   float mYScale;
 };
 
 class CompositorParent : public PCompositorParent,
                          public ShadowLayersManager
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorParent)
+
 public:
   CompositorParent(nsIWidget* aWidget,
                    bool aRenderToEGLSurface = false,
                    int aSurfaceWidth = -1, int aSurfaceHeight = -1);
 
   virtual ~CompositorParent();
 
   virtual bool RecvWillStop() MOZ_OVERRIDE;
@@ -175,16 +176,17 @@ protected:
   // derived class AsyncPanZoomController transformations.
   // Compositor will not own AsyncPanZoomController here.
   virtual AsyncPanZoomController* GetDefaultPanZoomController() { return nullptr; }
 
 private:
   void PauseComposition();
   void ResumeComposition();
   void ResumeCompositionAndResize(int width, int height);
+  void ForceComposition();
 
   // Sample transforms for layer trees.  Return true to request
   // another animation frame.
   bool TransformShadowTree(TimeStamp aCurrentFrame);
   // Return true if an AsyncPanZoomController content transform was
   // applied for |aLayer|.  *aWantNextFrame is set to true if the
   // controller wants another animation frame.
   bool ApplyAsyncContentTransformToTree(TimeStamp aCurrentFrame, Layer* aLayer,
@@ -286,15 +288,18 @@ private:
   bool mRenderToEGLSurface;
   nsIntSize mEGLSurfaceSize;
 
   mozilla::Monitor mPauseCompositionMonitor;
   mozilla::Monitor mResumeCompositionMonitor;
 
   uint64_t mCompositorID;
 
+  bool mOverrideComposeReadiness;
+  CancelableTask* mForceCompositionTask;
+
   DISALLOW_EVIL_CONSTRUCTORS(CompositorParent);
 };
 
 } // layers
 } // mozilla
 
 #endif // mozilla_layers_CompositorParent_h
--- a/gfx/layers/ipc/PLayers.ipdl
+++ b/gfx/layers/ipc/PLayers.ipdl
@@ -9,41 +9,45 @@ include LayersSurfaces;
 include protocol PCompositor;
 include protocol PGrallocBuffer;
 include protocol PLayer;
 include protocol PRenderFrame;
 
 include "gfxipc/ShadowLayerUtils.h";
 include "mozilla/WidgetUtils.h";
 include "mozilla/TimeStamp.h";
+include "mozilla/dom/ScreenOrientation.h";
 include "nsCSSProperty.h";
 
 using gfxPoint3D;
 using nscoord;
 using nsRect;
 using nsPoint;
 using mozilla::TimeDuration;
 using mozilla::TimeStamp;
 using mozilla::ScreenRotation;
 using nsCSSProperty;
+using mozilla::dom::ScreenOrientation;
 
 /**
  * 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;
+  nsIntRect clientBounds;
+  ScreenOrientation orientation;
 };
 
 // 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; };
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -20,16 +20,17 @@
 #include "ShadowLayerChild.h"
 #include "gfxipc/ShadowLayerUtils.h"
 #include "RenderTrace.h"
 #include "sampler.h"
 #include "nsXULAppAPI.h"
 
 using namespace mozilla::ipc;
 using namespace mozilla::gl;
+using namespace mozilla::dom;
 
 namespace mozilla {
 namespace layers {
 
 typedef nsTArray<SurfaceDescriptor> BufferArray; 
 typedef std::vector<Edit> EditVector;
 typedef std::set<ShadowableLayer*> ShadowableLayerSet;
 
@@ -37,24 +38,27 @@ class Transaction
 {
 public:
   Transaction()
     : mSwapRequired(false)
     , mOpen(false)
     , mRotationChanged(false)
   {}
 
-  void Begin(const nsIntRect& aTargetBounds, ScreenRotation aRotation)
+  void Begin(const nsIntRect& aTargetBounds, ScreenRotation aRotation,
+             const nsIntRect& aClientBounds, ScreenOrientation aOrientation)
   {
     mOpen = true;
     mTargetBounds = aTargetBounds;
     if (aRotation != mTargetRotation) {
         mRotationChanged = true;
     }
     mTargetRotation = aRotation;
+    mClientBounds = aClientBounds;
+    mTargetOrientation = aOrientation;
   }
 
   void AddEdit(const Edit& aEdit)
   {
     NS_ABORT_IF_FALSE(!Finished(), "forgot BeginTransaction?");
     mCset.push_back(aEdit);
   }
   void AddPaint(const Edit& aPaint)
@@ -102,16 +106,18 @@ public:
   bool Finished() const { return !mOpen && Empty(); }
 
   EditVector mCset;
   EditVector mPaints;
   BufferArray mDyingBuffers;
   ShadowableLayerSet mMutants;
   nsIntRect mTargetBounds;
   ScreenRotation mTargetRotation;
+  nsIntRect mClientBounds;
+  ScreenOrientation mTargetOrientation;
   bool mSwapRequired;
 
 private:
   bool mOpen;
   bool mRotationChanged;
 
   // disabled
   Transaction(const Transaction&);
@@ -135,21 +141,23 @@ ShadowLayerForwarder::ShadowLayerForward
 ShadowLayerForwarder::~ShadowLayerForwarder()
 {
   NS_ABORT_IF_FALSE(mTxn->Finished(), "unfinished transaction?");
   delete mTxn;
 }
 
 void
 ShadowLayerForwarder::BeginTransaction(const nsIntRect& aTargetBounds,
-                                       ScreenRotation aRotation)
+                                       ScreenRotation aRotation,
+                                       const nsIntRect& aClientBounds,
+                                       ScreenOrientation aOrientation)
 {
   NS_ABORT_IF_FALSE(HasShadowManager(), "no manager to forward to");
   NS_ABORT_IF_FALSE(mTxn->Finished(), "uncommitted txn?");
-  mTxn->Begin(aTargetBounds, aRotation);
+  mTxn->Begin(aTargetBounds, aRotation, aClientBounds, aOrientation);
 }
 
 static PLayerChild*
 Shadow(ShadowableLayer* aLayer)
 {
   return aLayer->GetShadow();
 }
 
@@ -353,17 +361,17 @@ 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);
+  TargetConfig targetConfig(mTxn->mTargetBounds, mTxn->mTargetRotation, mTxn->mClientBounds, mTxn->mTargetOrientation);
 
   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, targetConfig, mIsFirstPaint,
--- 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 "mozilla/ipc/SharedMemory.h"
 #include "mozilla/WidgetUtils.h"
+#include "mozilla/dom/ScreenOrientation.h"
 
 class gfxSharedImageSurface;
 
 namespace mozilla {
 
 namespace gl {
 class GLContext;
 class TextureImage;
@@ -111,17 +112,19 @@ public:
 
   virtual ~ShadowLayerForwarder();
 
   /**
    * Begin recording a transaction to be forwarded atomically to a
    * ShadowLayerManager.
    */
   void BeginTransaction(const nsIntRect& aTargetBounds,
-                        ScreenRotation aRotation);
+                        ScreenRotation aRotation,
+                        const nsIntRect& aClientBounds,
+                        mozilla::dom::ScreenOrientation aOrientation);
 
   /**
    * The following methods may only be called after BeginTransaction()
    * but before EndTransaction().  They mirror the LayerManager
    * interface in Layers.h.
    */
 
   /**
--- a/gfx/layers/opengl/LayerManagerOGL.cpp
+++ b/gfx/layers/opengl/LayerManagerOGL.cpp
@@ -1032,20 +1032,24 @@ LayerManagerOGL::Render()
     NS_WARNING("Call on destroyed layer manager");
     return;
   }
 
   nsIntRect rect;
   if (mIsRenderingToEGLSurface) {
     rect = nsIntRect(0, 0, mSurfaceSize.width, mSurfaceSize.height);
   } else {
-    // FIXME/bug XXXXXX this races with rotation changes on the main
-    // thread, and undoes all the care we take with layers txns being
-    // sent atomically with rotation changes
-    mWidget->GetClientBounds(rect);
+    rect = mRenderBounds;
+    // If render bounds is not updated explicitly, try to infer it from widget
+    if (rect.width == 0 || rect.height == 0) {
+      // FIXME/bug XXXXXX this races with rotation changes on the main
+      // thread, and undoes all the care we take with layers txns being
+      // sent atomically with rotation changes
+      mWidget->GetClientBounds(rect);
+    }
   }
   WorldTransformRect(rect);
 
   GLint width = rect.width;
   GLint height = rect.height;
 
   // We can't draw anything to something with no area
   // so just return
@@ -1261,16 +1265,22 @@ void
 LayerManagerOGL::WorldTransformRect(nsIntRect& aRect)
 {
   gfxRect grect(aRect.x, aRect.y, aRect.width, aRect.height);
   grect = mWorldMatrix.TransformBounds(grect);
   aRect.SetRect(grect.X(), grect.Y(), grect.Width(), grect.Height());
 }
 
 void
+LayerManagerOGL::UpdateRenderBounds(const nsIntRect& aRect)
+{
+  mRenderBounds = aRect;
+}
+
+void
 LayerManagerOGL::SetSurfaceSize(int width, int height)
 {
   mSurfaceSize.width = width;
   mSurfaceSize.height = height;
 }
 
 void
 LayerManagerOGL::SetupPipeline(int aWidth, int aHeight, WorldTransforPolicy aTransformPolicy)
--- a/gfx/layers/opengl/LayerManagerOGL.h
+++ b/gfx/layers/opengl/LayerManagerOGL.h
@@ -285,16 +285,18 @@ public:
    * Setup World transform matrix.
    * Transform will be ignored if it is not PreservesAxisAlignedRectangles
    * or has non integer scale
    */
   void SetWorldTransform(const gfxMatrix& aMatrix);
   gfxMatrix& GetWorldTransform(void);
   void WorldTransformRect(nsIntRect& aRect);
 
+  void UpdateRenderBounds(const nsIntRect& aRect);
+
   /**
    * Set the size of the surface we're rendering to.
    */
   void SetSurfaceSize(int width, int height);
 
   bool CompositingDisabled() { return mCompositingDisabled; }
   void SetCompositingDisabled(bool aCompositingDisabled) { mCompositingDisabled = aCompositingDisabled; }
 
@@ -409,16 +411,17 @@ private:
                                              const gfx3DMatrix& aTransform);
 
   /* Thebes layer callbacks; valid at the end of a transaciton,
    * while rendering */
   DrawThebesLayerCallback mThebesLayerCallback;
   void *mThebesLayerCallbackData;
   gfxMatrix mWorldMatrix;
   nsAutoPtr<FPSState> mFPS;
+  nsIntRect mRenderBounds;
 #ifdef DEBUG
   // NB: only interesting when this is a purely compositing layer
   // manager.  True after possibly onscreen layers have had their
   // cached resources cleared outside of a transaction, and before the
   // next forwarded transaction that re-validates their buffers.
   bool mMaybeInvalidTree;
 #endif
 
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -169,17 +169,39 @@ FontPrefsObserver::Observe(nsISupports *
         return NS_ERROR_UNEXPECTED;
     }
     NS_ASSERTION(gfxPlatform::GetPlatform(), "the singleton instance has gone");
     gfxPlatform::GetPlatform()->FontsPrefsChanged(NS_ConvertUTF16toUTF8(someData).get());
 
     return NS_OK;
 }
 
+class OrientationSyncPrefsObserver : public nsIObserver
+{
+public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIOBSERVER
+};
 
+NS_IMPL_ISUPPORTS1(OrientationSyncPrefsObserver, nsIObserver)
+
+NS_IMETHODIMP
+OrientationSyncPrefsObserver::Observe(nsISupports *aSubject,
+                                      const char *aTopic,
+                                      const PRUnichar *someData)
+{
+    if (!someData) {
+        NS_ERROR("orientation sync pref observer broken");
+        return NS_ERROR_UNEXPECTED;
+    }
+    NS_ASSERTION(gfxPlatform::GetPlatform(), "the singleton instance has gone");
+    gfxPlatform::GetPlatform()->OrientationSyncPrefsObserverChanged();
+
+    return NS_OK;
+}
 
 // this needs to match the list of pref font.default.xx entries listed in all.js!
 // the order *must* match the order in eFontPrefLang
 static const char *gPrefLangNames[] = {
     "x-western",
     "x-central-euro",
     "ja",
     "zh-TW",
@@ -333,16 +355,19 @@ gfxPlatform::Init()
 
     /* Create and register our CMS Override observer. */
     gPlatform->mSRGBOverrideObserver = new SRGBOverrideObserver();
     Preferences::AddWeakObserver(gPlatform->mSRGBOverrideObserver, "gfx.color_management.force_srgb");
 
     gPlatform->mFontPrefsObserver = new FontPrefsObserver();
     Preferences::AddStrongObservers(gPlatform->mFontPrefsObserver, kObservedPrefs);
 
+    gPlatform->mOrientationSyncPrefsObserver = new OrientationSyncPrefsObserver();
+    Preferences::AddStrongObserver(gPlatform->mOrientationSyncPrefsObserver, "layers.orientation.sync.timeout");
+
     gPlatform->mWorkAroundDriverBugs = Preferences::GetBool("gfx.work-around-driver-bugs", true);
 
     mozilla::Preferences::AddBoolVarCache(&gPlatform->mWidgetUpdateFlashing,
                                           "nglayout.debug.widget_update_flashing");
 
     mozilla::gl::GLContext::PlatformStartup();
 
 #ifdef MOZ_WIDGET_ANDROID
@@ -354,16 +379,18 @@ gfxPlatform::Init()
     // ::Shutdown to be called.
     nsCOMPtr<nsISupports> forceReg
         = do_CreateInstance("@mozilla.org/gfx/init;1");
 
     if (Preferences::GetBool("gfx.2d.recording", false)) {
       gPlatform->mRecorder = Factory::CreateEventRecorderForFile("browserrecording.aer");
       Factory::SetGlobalEventRecorder(gPlatform->mRecorder);
     }
+
+    gPlatform->mOrientationSyncMillis = Preferences::GetUint("layers.orientation.sync.timeout", (uint32_t)0);
 }
 
 void
 gfxPlatform::Shutdown()
 {
     // These may be called before the corresponding subsystems have actually
     // started up. That's OK, they can handle it.
     gfxFontCache::Shutdown();
@@ -1694,8 +1721,20 @@ gfxPlatform::OptimalFormatForContent(gfx
     return gfxASurface::ImageFormatA8;
   case gfxASurface::CONTENT_COLOR_ALPHA:
     return gfxASurface::ImageFormatARGB32;
   default:
     NS_NOTREACHED("unknown gfxContentType");
     return gfxASurface::ImageFormatARGB32;
   }
 }
+
+void
+gfxPlatform::OrientationSyncPrefsObserverChanged()
+{
+  mOrientationSyncMillis = Preferences::GetUint("layers.orientation.sync.timeout", (uint32_t)0);
+}
+
+uint32_t
+gfxPlatform::GetOrientationSyncMillis() const
+{
+  return mOrientationSyncMillis;
+}
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -464,16 +464,18 @@ public:
 
     /**
      * Return sRGBA -> output device transform.
      */
     static qcms_transform* GetCMSRGBATransform();
 
     virtual void FontsPrefsChanged(const char *aPref);
 
+    void OrientationSyncPrefsObserverChanged();
+
     int32_t GetBidiNumeralOption();
 
     /**
      * Returns a 1x1 surface that can be used to create graphics contexts
      * for measuring text etc as if they will be rendered to the screen
      */
     gfxASurface* ScreenReferenceSurface() { return mScreenReferenceSurface; }
 
@@ -490,16 +492,18 @@ public:
     static PRLogModuleInfo* GetLog(eGfxLog aWhichLog);
 
     bool WorkAroundDriverBugs() const { return mWorkAroundDriverBugs; }
 
     virtual int GetScreenDepth() const;
 
     bool WidgetUpdateFlashing() const { return mWidgetUpdateFlashing; }
 
+    uint32_t GetOrientationSyncMillis() const;
+
 protected:
     gfxPlatform();
     virtual ~gfxPlatform();
 
     void AppendCJKPrefLangs(eFontPrefLang aPrefLangs[], uint32_t &aLen, 
                             eFontPrefLang aCharLang, eFontPrefLang aPageLang);
 
     /**
@@ -571,24 +575,26 @@ private:
     static void Init();
 
     virtual qcms_profile* GetPlatformCMSOutputProfile();
 
     nsRefPtr<gfxASurface> mScreenReferenceSurface;
     nsTArray<uint32_t> mCJKPrefLangs;
     nsCOMPtr<nsIObserver> mSRGBOverrideObserver;
     nsCOMPtr<nsIObserver> mFontPrefsObserver;
+    nsCOMPtr<nsIObserver> mOrientationSyncPrefsObserver;
 
     // The preferred draw target backend to use for canvas
     mozilla::gfx::BackendType mPreferredCanvasBackend;
     // The fallback draw target backend to use for canvas, if the preferred backend fails
     mozilla::gfx::BackendType mFallbackCanvasBackend;
     // The backend to use for content
     mozilla::gfx::BackendType mContentBackend;
 
     mozilla::widget::GfxInfoCollector<gfxPlatform> mAzureCanvasBackendCollector;
     bool mWorkAroundDriverBugs;
 
     mozilla::RefPtr<mozilla::gfx::DrawEventRecorder> mRecorder;
     bool mWidgetUpdateFlashing;
+    uint32_t mOrientationSyncMillis;
 };
 
 #endif /* GFX_PLATFORM_H */