Bug 974197 - Fire MozAfterPaint after the compositor has composited the frame. r=roc
☠☠ backed out by 7a2edc5171e6 ☠ ☠
authorMatt Woodrow <mwoodrow@mozilla.com>
Fri, 07 Mar 2014 16:24:32 +1300
changeset 189600 f94ee00aa4d69643ffe3e532e50808c8ad6ad927
parent 189599 fd36c14f015324c1f56db9dc1d4e3cec87430da4
child 189601 45363136618e19faa24ae5dce9c836e388f8fb6e
push id3503
push userraliiev@mozilla.com
push dateMon, 28 Apr 2014 18:51:11 +0000
treeherdermozilla-beta@c95ac01e332e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs974197
milestone30.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 974197 - Fire MozAfterPaint after the compositor has composited the frame. r=roc
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
gfx/layers/client/ClientLayerManager.cpp
gfx/layers/client/ClientLayerManager.h
gfx/layers/ipc/CompositorChild.cpp
gfx/layers/ipc/CompositorChild.h
gfx/layers/ipc/CompositorParent.cpp
gfx/layers/ipc/CompositorParent.h
gfx/layers/ipc/PCompositor.ipdl
layout/base/nsPresShell.cpp
layout/reftests/forms/input/range/stepUp-unthemed.html
layout/reftests/forms/input/range/stepUp.html
layout/reftests/printing/745025-1.html
view/public/nsView.h
view/src/nsView.cpp
widget/nsIWidgetListener.h
widget/xpwidgets/nsIWidgetListener.cpp
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -69,16 +69,17 @@
 #include "PuppetWidget.h"
 #include "StructuredCloneUtils.h"
 #include "nsViewportInfo.h"
 #include "JavaScriptChild.h"
 #include "APZCCallbackHelper.h"
 #include "nsILoadContext.h"
 #include "ipc/nsGUIEventIPC.h"
 #include "mozilla/gfx/Matrix.h"
+#include "ClientLayerManager.h"
 
 #include "nsColorPickerProxy.h"
 
 #ifdef DEBUG
 #include "PCOMContentPermissionRequestChild.h"
 #endif /* DEBUG */
 
 #define BROWSER_ELEMENT_CHILD_SCRIPT \
@@ -101,16 +102,19 @@ static const CSSSize kDefaultViewportSiz
 
 static const char BROWSER_ZOOM_TO_RECT[] = "browser-zoom-to-rect";
 static const char BEFORE_FIRST_PAINT[] = "before-first-paint";
 
 static bool sCpowsEnabled = false;
 static int32_t sActiveDurationMs = 10;
 static bool sActiveDurationMsSet = false;
 
+typedef nsDataHashtable<nsUint64HashKey, TabChild*> TabChildMap;
+static TabChildMap* sTabChildren;
+
 NS_IMETHODIMP
 ContentListener::HandleEvent(nsIDOMEvent* aEvent)
 {
   RemoteDOMEvent remoteEvent;
   remoteEvent.mEvent = do_QueryInterface(aEvent);
   NS_ENSURE_STATE(remoteEvent.mEvent);
   mTabChild->SendEvent(remoteEvent);
   return NS_OK;
@@ -275,16 +279,17 @@ TabChild::Create(ContentChild* aManager,
 
 
 TabChild::TabChild(ContentChild* aManager, const TabContext& aContext, uint32_t aChromeFlags)
   : TabContext(aContext)
   , mRemoteFrame(nullptr)
   , mManager(aManager)
   , mTabChildGlobal(nullptr)
   , mChromeFlags(aChromeFlags)
+  , mLayersId(0)
   , mOuterRect(0, 0, 0, 0)
   , mInnerSize(0, 0)
   , mActivePointerId(-1)
   , mTapHoldTimer(nullptr)
   , mAppPackageFileDescriptorRecved(false)
   , mOldViewportWidth(0.0f)
   , mLastBackgroundColor(NS_RGB(255, 255, 255))
   , mDidFakeShow(false)
@@ -1203,16 +1208,27 @@ TabChild::DestroyWindow()
     if (mWidget) {
         mWidget->Destroy();
     }
 
     if (mRemoteFrame) {
         mRemoteFrame->Destroy();
         mRemoteFrame = nullptr;
     }
+
+
+    if (mLayersId != 0) {
+      MOZ_ASSERT(sTabChildren);
+      sTabChildren->Remove(mLayersId);
+      if (!sTabChildren->Count()) {
+        delete sTabChildren;
+        sTabChildren = nullptr;
+      }
+      mLayersId = 0;
+    }
 }
 
 bool
 TabChild::UseDirectCompositor()
 {
     return !!CompositorChild::Get();
 }
 
@@ -2422,16 +2438,23 @@ TabChild::InitRenderingState()
         mWidget->GetLayerManager(shadowManager, mTextureFactoryIdentifier.mParentBackend)
                ->AsShadowForwarder();
     NS_ABORT_IF_FALSE(lf && lf->HasShadowManager(),
                       "PuppetWidget should have shadow manager");
     lf->IdentifyTextureHost(mTextureFactoryIdentifier);
     ImageBridgeChild::IdentifyCompositorTextureHost(mTextureFactoryIdentifier);
 
     mRemoteFrame = remoteFrame;
+    if (id != 0) {
+      if (!sTabChildren) {
+        sTabChildren = new TabChildMap;
+      }
+      sTabChildren->Put(id, this);
+      mLayersId = id;
+    }
 
     nsCOMPtr<nsIObserverService> observerService =
         mozilla::services::GetObserverService();
 
     if (observerService) {
         observerService->AddObserver(this,
                                      BROWSER_ZOOM_TO_RECT,
                                      false);
@@ -2624,16 +2647,36 @@ TabChild::GetFrom(nsIPresShell* aPresShe
   nsIDocument* doc = aPresShell->GetDocument();
   if (!doc) {
       return nullptr;
   }
   nsCOMPtr<nsIDocShell> docShell(doc->GetDocShell());
   return GetFrom(docShell);
 }
 
+TabChild*
+TabChild::GetFrom(uint64_t aLayersId)
+{
+  if (!sTabChildren) {
+    return nullptr;
+  }
+  return sTabChildren->Get(aLayersId);
+}
+
+void
+TabChild::DidComposite()
+{
+  MOZ_ASSERT(mWidget);
+  MOZ_ASSERT(mWidget->GetLayerManager());
+  MOZ_ASSERT(mWidget->GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT);
+
+  ClientLayerManager *manager = static_cast<ClientLayerManager*>(mWidget->GetLayerManager());
+  manager->DidComposite();
+}
+
 NS_IMETHODIMP
 TabChild::OnShowTooltip(int32_t aXCoords, int32_t aYCoords, const char16_t *aTipText)
 {
     nsString str(aTipText);
     SendShowTooltip(aXCoords, aYCoords, str);
     return NS_OK;
 }
 
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -376,16 +376,19 @@ public:
     static inline TabChild*
     GetFrom(nsIDocShell* aDocShell)
     {
       nsCOMPtr<nsITabChild> tc = do_GetInterface(aDocShell);
       return static_cast<TabChild*>(tc.get());
     }
 
     static TabChild* GetFrom(nsIPresShell* aPresShell);
+    static TabChild* GetFrom(uint64_t aLayersId);
+
+    void DidComposite();
 
     static inline TabChild*
     GetFrom(nsIDOMWindow* aWindow)
     {
       nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(aWindow);
       nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(webNav);
       return GetFrom(docShell);
     }
@@ -494,16 +497,17 @@ private:
     nsCOMPtr<nsIWebNavigation> mWebNav;
     nsCOMPtr<nsIWidget> mWidget;
     nsCOMPtr<nsIURI> mLastURI;
     FrameMetrics mLastRootMetrics;
     RenderFrameChild* mRemoteFrame;
     nsRefPtr<ContentChild> mManager;
     nsRefPtr<TabChildGlobal> mTabChildGlobal;
     uint32_t mChromeFlags;
+    uint64_t mLayersId;
     nsIntRect mOuterRect;
     ScreenIntSize mInnerSize;
     // When we're tracking a possible tap gesture, this is the "down"
     // point of the touchstart.
     LayoutDevicePoint mGestureDownPoint;
     // The touch identifier of the active gesture.
     int32_t mActivePointerId;
     // A timer task that fires if the tap-hold timeout is exceeded by
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -17,16 +17,17 @@
 #include "mozilla/layers/ContentClient.h"  // for ContentClientRemote
 #include "mozilla/layers/ISurfaceAllocator.h"
 #include "mozilla/layers/LayersMessages.h"  // for EditReply, etc
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor
 #include "mozilla/layers/PLayerChild.h"  // for PLayerChild
 #include "mozilla/layers/LayerTransactionChild.h"
 #include "nsAString.h"
 #include "nsIWidget.h"                  // for nsIWidget
+#include "nsIWidgetListener.h"
 #include "nsTArray.h"                   // for AutoInfallibleTArray
 #include "nsXULAppAPI.h"                // for XRE_GetProcessType, etc
 #ifdef MOZ_WIDGET_ANDROID
 #include "AndroidBridge.h"
 #endif
 
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
@@ -259,16 +260,31 @@ ClientLayerManager::GetRemoteRenderer()
 void
 ClientLayerManager::Composite()
 {
   if (LayerTransactionChild* manager = mForwarder->GetShadowManager()) {
     manager->SendForceComposite();
   }
 }
 
+void
+ClientLayerManager::DidComposite()
+{
+  MOZ_ASSERT(mWidget);
+  nsIWidgetListener *listener = mWidget->GetWidgetListener();
+  if (listener) {
+    listener->DidCompositeWindow();
+  } else {
+    listener = mWidget->GetAttachedWidgetListener();
+    if (listener) {
+      listener->DidCompositeWindow();
+    }
+  }
+}
+
 void 
 ClientLayerManager::MakeSnapshotIfRequired()
 {
   if (!mShadowTarget) {
     return;
   }
   if (mWidget) {
     if (CompositorChild* remoteRenderer = GetRemoteRenderer()) {
--- a/gfx/layers/client/ClientLayerManager.h
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -148,16 +148,18 @@ public:
   void SetNeedsComposite(bool aNeedsComposite)
   {
     mNeedsComposite = aNeedsComposite;
   }
   bool NeedsComposite() const { return mNeedsComposite; }
 
   virtual void Composite() MOZ_OVERRIDE;
 
+  virtual void DidComposite();
+
 protected:
   enum TransactionPhase {
     PHASE_NONE, PHASE_CONSTRUCTION, PHASE_DRAWING, PHASE_FORWARD
   };
   TransactionPhase mPhase;
 
 private:
   /**
--- a/gfx/layers/ipc/CompositorChild.cpp
+++ b/gfx/layers/ipc/CompositorChild.cpp
@@ -15,16 +15,17 @@
 #include "mozilla/layers/PLayerTransactionChild.h"
 #include "mozilla/mozalloc.h"           // for operator new, etc
 #include "nsDebug.h"                    // for NS_RUNTIMEABORT
 #include "nsIObserver.h"                // for nsIObserver
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
 #include "nsTArray.h"                   // for nsTArray, nsTArray_Impl
 #include "nsXULAppAPI.h"                // for XRE_GetIOMessageLoop, etc
 #include "FrameLayerBuilder.h"
+#include "mozilla/dom/TabChild.h"
 
 using mozilla::layers::LayerTransactionChild;
 
 namespace mozilla {
 namespace layers {
 
 /*static*/ CompositorChild* CompositorChild::sCompositor;
 
@@ -115,16 +116,31 @@ CompositorChild::DeallocPLayerTransactio
 
 bool
 CompositorChild::RecvInvalidateAll()
 {
   FrameLayerBuilder::InvalidateAllLayers(mLayerManager);
   return true;
 }
 
+bool
+CompositorChild::RecvDidComposite(const uint64_t& aId)
+{
+  if (mLayerManager) {
+    MOZ_ASSERT(aId == 0);
+    mLayerManager->DidComposite();
+  } else if (aId != 0) {
+    dom::TabChild *child = dom::TabChild::GetFrom(aId);
+    if (child) {
+      child->DidComposite();
+    }
+  }
+  return true;
+}
+
 void
 CompositorChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   MOZ_ASSERT(sCompositor == this);
 
 #ifdef MOZ_B2G
   // Due to poor lifetime management of gralloc (and possibly shmems) we will
   // crash at some point in the future when we get destroyed due to abnormal
--- a/gfx/layers/ipc/CompositorChild.h
+++ b/gfx/layers/ipc/CompositorChild.h
@@ -52,16 +52,18 @@ public:
   Create(Transport* aTransport, ProcessId aOtherProcess);
 
   static CompositorChild* Get();
 
   static bool ChildProcessHasCompositor() { return sCompositor != nullptr; }
 
   virtual bool RecvInvalidateAll() MOZ_OVERRIDE;
 
+  virtual bool RecvDidComposite(const uint64_t& aId) MOZ_OVERRIDE;
+
 protected:
   virtual PLayerTransactionChild*
     AllocPLayerTransactionChild(const nsTArray<LayersBackend>& aBackendHints,
                                 const uint64_t& aId,
                                 TextureFactoryIdentifier* aTextureFactoryIdentifier,
                                 bool* aSuccess) MOZ_OVERRIDE;
 
   virtual bool DeallocPLayerTransactionChild(PLayerTransactionChild *aChild) MOZ_OVERRIDE;
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -46,16 +46,17 @@
 #include "nsThreadUtils.h"              // for NS_IsMainThread
 #include "nsXULAppAPI.h"                // for XRE_GetIOMessageLoop
 #ifdef XP_WIN
 #include "mozilla/layers/CompositorD3D11.h"
 #include "mozilla/layers/CompositorD3D9.h"
 #endif
 #include "GeckoProfiler.h"
 #include "mozilla/ipc/ProtocolTypes.h"
+#include "mozilla/unused.h"
 
 using namespace base;
 using namespace mozilla;
 using namespace mozilla::ipc;
 using namespace mozilla::gfx;
 using namespace std;
 
 namespace mozilla {
@@ -184,16 +185,17 @@ CompositorParent::CompositorParent(nsIWi
   , mIsTesting(false)
   , mPaused(false)
   , mUseExternalSurfaceSize(aUseExternalSurfaceSize)
   , mEGLSurfaceSize(aSurfaceWidth, aSurfaceHeight)
   , mPauseCompositionMonitor("PauseCompositionMonitor")
   , mResumeCompositionMonitor("ResumeCompositionMonitor")
   , mOverrideComposeReadiness(false)
   , mForceCompositionTask(nullptr)
+  , mWantDidCompositeEvent(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.
@@ -532,16 +534,18 @@ CompositorParent::NotifyShadowTreeTransa
     AutoResolveRefLayers resolve(mCompositionManager);
     mApzcTreeManager->UpdatePanZoomControllerTree(this, mLayerManager->GetRoot(), aIsFirstPaint, aId);
 
     mLayerManager->NotifyShadowTreeTransaction();
   }
   if (aScheduleComposite) {
     ScheduleComposition();
   }
+
+  mWantDidCompositeEvent = true;
 }
 
 // Used when layout.frame_rate is -1. Needs to be kept in sync with
 // DEFAULT_FRAME_RATE in nsRefreshDriver.cpp.
 static const int32_t kDefaultFrameRate = 60;
 
 static int32_t
 CalculateCompositionFrameRate()
@@ -658,16 +662,21 @@ CompositorParent::CompositeToTarget(Draw
   if (gDumpCompositorTree) {
     printf_stderr("Painting --- compositing layer tree:\n");
     mLayerManager->Dump();
   }
 #endif
   mLayerManager->SetDebugOverlayWantsNextFrame(false);
   mLayerManager->EndEmptyTransaction();
 
+  if (!aTarget && mWantDidCompositeEvent) {
+    DidComposite();
+    mWantDidCompositeEvent = false;
+  }
+
   if (mLayerManager->DebugOverlayWantsNextFrame()) {
     ScheduleComposition();
   }
 
 #ifdef COMPOSITOR_PERFORMANCE_WARNING
   TimeDuration executionTime = TimeStamp::Now() - mLastCompose;
   TimeDuration frameBudget = TimeDuration::FromMilliseconds(15);
   int32_t frameRate = CalculateCompositionFrameRate();
@@ -685,16 +694,30 @@ CompositorParent::CompositeToTarget(Draw
     // Special full-tilt composite mode for performance testing
     ScheduleComposition();
   }
 
   profiler_tracing("Paint", "Composite", TRACING_INTERVAL_END);
 }
 
 void
+CompositorParent::DidComposite()
+{
+  unused << SendDidComposite(0);
+
+  for (LayerTreeMap::iterator it = sIndirectLayerTrees.begin();
+       it != sIndirectLayerTrees.end(); it++) {
+    LayerTreeState* lts = &it->second;
+    if (lts->mParent == this && lts->mCrossProcessParent) {
+      unused << lts->mCrossProcessParent->SendDidComposite(it->first);
+    }
+  }
+}
+
+void
 CompositorParent::ForceComposeToTarget(DrawTarget* aTarget)
 {
   PROFILER_LABEL("CompositorParent", "ForceComposeToTarget");
   AutoRestore<bool> override(mOverrideComposeReadiness);
   mOverrideComposeReadiness = true;
 
   CompositeToTarget(aTarget);
 }
@@ -760,16 +783,17 @@ CompositorParent::ShadowLayersUpdated(La
     if (mIsTesting) {
       mCompositionManager->TransformShadowTree(mTestTime);
     }
   }
   if (aScheduleComposite) {
     ScheduleComposition();
   }
   mLayerManager->NotifyShadowTreeTransaction();
+  mWantDidCompositeEvent = true;
 }
 
 void
 CompositorParent::ForceComposite(LayerTransactionParent* aLayerTree)
 {
   ScheduleComposition();
 }
 
--- a/gfx/layers/ipc/CompositorParent.h
+++ b/gfx/layers/ipc/CompositorParent.h
@@ -292,16 +292,18 @@ private:
   static CompositorParent* RemoveCompositor(uint64_t id);
 
    /**
    * Return true if current state allows compositing, that is
    * finishing a layers transaction.
    */
   bool CanComposite();
 
+  void DidComposite();
+
   nsRefPtr<LayerManagerComposite> mLayerManager;
   nsRefPtr<Compositor> mCompositor;
   RefPtr<AsyncCompositionManager> mCompositionManager;
   nsIWidget* mWidget;
   CancelableTask *mCurrentCompositeTask;
   TimeStamp mLastCompose;
   TimeStamp mTestTime;
   bool mIsTesting;
@@ -320,15 +322,17 @@ private:
   uint64_t mCompositorID;
   uint64_t mRootLayerTreeID;
 
   bool mOverrideComposeReadiness;
   CancelableTask* mForceCompositionTask;
 
   nsRefPtr<APZCTreeManager> mApzcTreeManager;
 
+  bool mWantDidCompositeEvent;
+
   DISALLOW_EVIL_CONSTRUCTORS(CompositorParent);
 };
 
 } // layers
 } // mozilla
 
 #endif // mozilla_layers_CompositorParent_h
--- a/gfx/layers/ipc/PCompositor.ipdl
+++ b/gfx/layers/ipc/PCompositor.ipdl
@@ -35,16 +35,21 @@ intr protocol PCompositor
 {
   // A Compositor manages a single Layer Manager (PLayerTransaction)
   manages PLayerTransaction;
 
 child:
   // The child should invalidate everything so that the whole window is redrawn.
   async InvalidateAll();
 
+  // The compositor completed a layers transaction. id is the layers id
+  // of the child layer tree that was composited (or 0 when notifying
+  // the root layer tree).
+  async DidComposite(uint64_t id);
+
 parent:
 
   // The child is about to be destroyed, so perform any necessary cleanup.
   sync WillStop();
 
   // Clean up in preparation for own destruction.
   sync Stop();
 
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -5833,21 +5833,17 @@ PresShell::Paint(nsView*        aViewToP
   nsIFrame* frame = aViewToPaint->GetFrame();
 
   bool isRetainingManager;
   LayerManager* layerManager =
     aViewToPaint->GetWidget()->GetLayerManager(&isRetainingManager);
   NS_ASSERTION(layerManager, "Must be in paint event");
   bool shouldInvalidate = layerManager->NeedsWidgetInvalidation();
 
-  uint32_t didPaintFlags = aFlags;
-  if (!shouldInvalidate) {
-    didPaintFlags |= PAINT_COMPOSITE;
-  }
-  nsAutoNotifyDidPaint notifyDidPaint(this, didPaintFlags);
+  nsAutoNotifyDidPaint notifyDidPaint(this, aFlags);
   AutoUpdateHitRegion updateHitRegion(this, frame);
 
   // Whether or not we should set first paint when painting is
   // suppressed is debatable. For now we'll do it because
   // B2G relies on first paint to configure the viewport and
   // we only want to do that when we have real content to paint.
   // See Bug 798245
   if (mIsFirstPaint && !mPaintingSuppressed) {
--- a/layout/reftests/forms/input/range/stepUp-unthemed.html
+++ b/layout/reftests/forms/input/range/stepUp-unthemed.html
@@ -4,14 +4,13 @@
              should be moved to the appropriate position -->
   <script type="text/javascript">
     function setValue()
     {
       document.getElementById('i').stepUp();
       document.documentElement.className = '';
     }
     document.addEventListener("MozReftestInvalidate", setValue);
-    setTimeout(setValue, 2000); // useful when not running under reftest suite
   </script>
   <body>
     <input type=range id='i' value=50 step=25 style='-moz-appearance:none'>
   </body>
 </html>
--- a/layout/reftests/forms/input/range/stepUp.html
+++ b/layout/reftests/forms/input/range/stepUp.html
@@ -4,14 +4,13 @@
              should be moved to the appropriate position -->
   <script type="text/javascript">
     function setValue()
     {
       document.getElementById('i').stepUp();
       document.documentElement.className = '';
     }
     document.addEventListener("MozReftestInvalidate", setValue);
-    setTimeout(setValue, 2000); // useful when not running under reftest suite
   </script>
   <body>
     <input type=range id='i' value=50 step=25>
   </body>
 </html>
--- a/layout/reftests/printing/745025-1.html
+++ b/layout/reftests/printing/745025-1.html
@@ -7,16 +7,16 @@
       canvas.mozPrintCallback = function(obj) {
         var ctx = obj.context;
         ctx.fillStyle = 'rgb(255, 0, 0)';
         ctx.fillRect(0, 0, 10, 10);
         obj.done();
       };
       setTimeout(function() {
         document.documentElement.className = "reftest-print" 
-      }, 0);
+      }, 100);
     };
   </script>
 </head>
 <body>
   <canvas id="canvas" width="10px" height="10px"></canvas>
 </body>
 </html>
--- a/view/public/nsView.h
+++ b/view/public/nsView.h
@@ -365,16 +365,17 @@ public:
   virtual nsIPresShell* GetPresShell() MOZ_OVERRIDE;
   virtual nsView* GetView() MOZ_OVERRIDE { return this; }
   virtual bool WindowMoved(nsIWidget* aWidget, int32_t x, int32_t y) MOZ_OVERRIDE;
   virtual bool WindowResized(nsIWidget* aWidget, int32_t aWidth, int32_t aHeight) MOZ_OVERRIDE;
   virtual bool RequestWindowClose(nsIWidget* aWidget) MOZ_OVERRIDE;
   virtual void WillPaintWindow(nsIWidget* aWidget) MOZ_OVERRIDE;
   virtual bool PaintWindow(nsIWidget* aWidget, nsIntRegion aRegion) MOZ_OVERRIDE;
   virtual void DidPaintWindow() MOZ_OVERRIDE;
+  virtual void DidCompositeWindow() MOZ_OVERRIDE;
   virtual void RequestRepaint() MOZ_OVERRIDE;
   virtual nsEventStatus HandleEvent(mozilla::WidgetGUIEvent* aEvent,
                                     bool aUseAttachedEvents) MOZ_OVERRIDE;
 
   virtual ~nsView();
 
   nsPoint GetOffsetTo(const nsView* aOther, const int32_t aAPD) const;
   nsIWidget* GetNearestWidget(nsPoint* aOffset, const int32_t aAPD) const;
--- a/view/src/nsView.cpp
+++ b/view/src/nsView.cpp
@@ -11,16 +11,17 @@
 #include "mozilla/Likely.h"
 #include "mozilla/Poison.h"
 #include "nsIWidget.h"
 #include "nsViewManager.h"
 #include "nsIFrame.h"
 #include "nsPresArena.h"
 #include "nsXULPopupManager.h"
 #include "nsIWidgetListener.h"
+#include "nsContentUtils.h" // for nsAutoScriptBlocker
 
 using namespace mozilla;
 
 nsView::nsView(nsViewManager* aViewManager, nsViewVisibility aVisibility)
 {
   MOZ_COUNT_CTOR(nsView);
 
   mVis = aVisibility;
@@ -1050,16 +1051,26 @@ nsView::PaintWindow(nsIWidget* aWidget, 
 void
 nsView::DidPaintWindow()
 {
   nsRefPtr<nsViewManager> vm = mViewManager;
   vm->DidPaintWindow();
 }
 
 void
+nsView::DidCompositeWindow()
+{
+  nsIPresShell* presShell = mViewManager->GetPresShell();
+  if (presShell) {
+    nsAutoScriptBlocker scriptBlocker;
+    presShell->GetPresContext()->GetDisplayRootPresContext()->GetRootPresContext()->NotifyDidPaintForSubtree(nsIPresShell::PAINT_COMPOSITE);
+  }
+}
+
+void
 nsView::RequestRepaint()
 {
   nsIPresShell* presShell = mViewManager->GetPresShell();
   if (presShell) {
     presShell->ScheduleViewManagerFlush();
   }
 }
 
--- a/widget/nsIWidgetListener.h
+++ b/widget/nsIWidgetListener.h
@@ -125,16 +125,18 @@ public:
   /**
    * Indicates that a paint occurred.
    * This is called at a time when it is OK to change the geometry of
    * this widget or of other widgets.
    * Must be called after every call to PaintWindow.
    */
   virtual void DidPaintWindow();
 
+  virtual void DidCompositeWindow();
+
   /**
    * Request that layout schedules a repaint on the next refresh driver tick.
    */
   virtual void RequestRepaint();
 
   /**
    * Handle an event.
    */
--- a/widget/xpwidgets/nsIWidgetListener.cpp
+++ b/widget/xpwidgets/nsIWidgetListener.cpp
@@ -97,16 +97,21 @@ nsIWidgetListener::PaintWindow(nsIWidget
 }
 
 void
 nsIWidgetListener::DidPaintWindow()
 {
 }
 
 void
+nsIWidgetListener::DidCompositeWindow()
+{
+}
+
+void
 nsIWidgetListener::RequestRepaint()
 {
 }
 
 nsEventStatus
 nsIWidgetListener::HandleEvent(WidgetGUIEvent* aEvent,
                                bool aUseAttachedEvents)
 {