Bug 1009628 - Part 1: Need mozAfterRemotePaint event for remote iframes. r=smaug.
authorDavid Parks <davidp99>
Fri, 23 May 2014 11:19:00 -0700
changeset 197913 736c8625bcf76e386abde257104c8cbce557abd4
parent 197912 4bd09430f06376b23684b9efc62336d76f942826
child 197914 7c813e99202d426a64bb40a5fbd2b8f9685a9170
push id8062
push usermconley@mozilla.com
push dateTue, 05 Aug 2014 21:40:33 +0000
treeherderfx-team@06b6aa5c734d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1009628
milestone34.0a1
Bug 1009628 - Part 1: Need mozAfterRemotePaint event for remote iframes. r=smaug.
content/base/public/nsIFrameLoader.idl
content/base/src/nsFrameLoader.cpp
dom/base/nsGlobalWindow.cpp
dom/base/nsPIDOMWindow.h
dom/ipc/PBrowser.ipdl
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
dom/ipc/TabParent.cpp
dom/ipc/TabParent.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
--- a/content/base/public/nsIFrameLoader.idl
+++ b/content/base/public/nsIFrameLoader.idl
@@ -107,17 +107,17 @@ interface nsIContentViewManager : nsISup
                          [retval, array, size_is(aLength)] out nsIContentView aResult);
 
   /**
    * The root content view.
    */
   readonly attribute nsIContentView rootContentView;
 };
 
-[scriptable, builtinclass, uuid(a5cefcc8-551b-4901-83f3-7436b09ecaba)]
+[scriptable, builtinclass, uuid(d52ca6a8-8237-4ae0-91d7-7be4f1db24ef)]
 interface nsIFrameLoader : nsISupports
 {
   /**
    * Get the docshell from the frame loader.
    */
   readonly attribute nsIDocShell docShell;
 
   /**
@@ -200,16 +200,23 @@ interface nsIFrameLoader : nsISupports
    * @see nsIDOMWindowUtils sendKeyEvent.
    */
   void sendCrossProcessKeyEvent(in AString aType,
                                 in long aKeyCode,
                                 in long aCharCode,
                                 in long aModifiers,
                                 [optional] in boolean aPreventDefault);
 
+  /**
+   * Request that the next time a remote layer transaction has been
+   * received by the Compositor, a MozAfterRemoteFrame event be sent
+   * to the window.
+   */
+  void requestNotifyAfterRemotePaint();
+
   /** 
    * The default rendering mode is synchronous scrolling.  In this
    * mode, it's an error to try to set a target viewport.
    */
   const unsigned long RENDER_MODE_DEFAULT        = 0x00000000;
 
   /**
    * When asynchronous scrolling is enabled, a target viewport can be
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -2669,16 +2669,39 @@ nsFrameLoader::ResetPermissionManagerSta
 
   // Register the new AppId.
   if (appId != nsIScriptSecurityManager::NO_APP_ID) {
     mAppIdSentToPermissionManager = appId;
     permMgr->AddrefAppId(mAppIdSentToPermissionManager);
   }
 }
 
+/**
+ * Send the RequestNotifyAfterRemotePaint message to the current Tab.
+ */
+NS_IMETHODIMP
+nsFrameLoader::RequestNotifyAfterRemotePaint()
+{
+  // If remote browsing (e10s), handle this with the TabParent.
+  if (mRemoteBrowser) {
+    unused << mRemoteBrowser->SendRequestNotifyAfterRemotePaint();
+    return NS_OK;
+  }
+
+  // If not remote browsing, directly use the document's window.
+  nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(mDocShell);
+  if (!window) {
+    NS_WARNING("Unable to get window for synchronous MozAfterRemotePaint event.");
+    return NS_OK;
+  }
+
+  window->SetRequestNotifyAfterRemotePaint();
+  return NS_OK;
+}
+
 /* [infallible] */ NS_IMETHODIMP
 nsFrameLoader::SetVisible(bool aVisible)
 {
   if (mVisible == aVisible) {
     return NS_OK;
   }
 
   mVisible = aVisible;
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -573,17 +573,17 @@ nsPIDOMWindow::nsPIDOMWindow(nsPIDOMWind
   mMayHaveTouchCaret(false), mMayHaveMouseEnterLeaveEventListener(false),
   mMayHavePointerEnterLeaveEventListener(false),
   mIsModalContentWindow(false),
   mIsActive(false), mIsBackground(false),
   mAudioMuted(false), mAudioVolume(1.0),
   mInnerWindow(nullptr), mOuterWindow(aOuterWindow),
   // Make sure no actual window ends up with mWindowID == 0
   mWindowID(++gNextWindowID), mHasNotifiedGlobalCreated(false),
-  mMarkedCCGeneration(0)
+  mMarkedCCGeneration(0), mSendAfterRemotePaint(false)
  {}
 
 nsPIDOMWindow::~nsPIDOMWindow() {}
 
 // DialogValueHolder CC goop.
 NS_IMPL_CYCLE_COLLECTION(DialogValueHolder, mValue)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DialogValueHolder)
@@ -3791,16 +3791,31 @@ nsPIDOMWindow::RefreshMediaElements()
 {
   nsRefPtr<AudioChannelService> service =
     AudioChannelService::GetAudioChannelService();
   if (service) {
     service->RefreshAgentsVolume(this);
   }
 }
 
+void
+nsPIDOMWindow::SendAfterRemotePaintIfRequested()
+{
+  if (!mSendAfterRemotePaint) {
+    return;
+  }
+
+  mSendAfterRemotePaint = false;
+
+  nsContentUtils::DispatchChromeEvent(GetExtantDoc(),
+                                      GetParentTarget(),
+                                      NS_LITERAL_STRING("MozAfterRemotePaint"),
+                                      false, false);
+}
+
 // nsISpeechSynthesisGetter
 
 #ifdef MOZ_WEBSPEECH
 SpeechSynthesis*
 nsGlobalWindow::GetSpeechSynthesis(ErrorResult& aError)
 {
   FORWARD_TO_INNER_OR_THROW(GetSpeechSynthesis, (aError), aError, nullptr);
 
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -719,16 +719,28 @@ public:
   {
     mMarkedCCGeneration = aGeneration;
   }
 
   uint32_t GetMarkedCCGeneration()
   {
     return mMarkedCCGeneration;
   }
+
+  // Sets the condition that we send an NS_AFTER_REMOTE_PAINT message just before the next
+  // composite.  Used in non-e10s implementations.
+  void SetRequestNotifyAfterRemotePaint()
+  {
+    mSendAfterRemotePaint = true;
+  }
+
+  // Sends an NS_AFTER_REMOTE_PAINT message if requested by
+  // SetRequestNotifyAfterRemotePaint().
+  void SendAfterRemotePaintIfRequested();
+
 protected:
   // The nsPIDOMWindow constructor. The aOuterWindow argument should
   // be null if and only if the created window itself is an outer
   // window. In all other cases aOuterWindow should be the outer
   // window for the inner window that is being created.
   nsPIDOMWindow(nsPIDOMWindow *aOuterWindow);
 
   ~nsPIDOMWindow();
@@ -810,16 +822,20 @@ protected:
   // this window.
   uint64_t mWindowID;
 
   // This is only used by the inner window. Set to true once we've sent
   // the (chrome|content)-document-global-created notification.
   bool mHasNotifiedGlobalCreated;
 
   uint32_t mMarkedCCGeneration;
+
+  // If true, send an NS_AFTER_REMOTE_PAINT message before compositing in a
+  // non-e10s implementation.
+  bool mSendAfterRemotePaint;
 };
 
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsPIDOMWindow, NS_PIDOMWINDOW_IID)
 
 #ifdef MOZILLA_INTERNAL_API
 PopupControlState
 PushPopupControlState(PopupControlState aState, bool aForce);
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -376,16 +376,25 @@ parent:
 
     __delete__();
 
     ReplyKeyEvent(WidgetKeyboardEvent event);
 
     sync RequestNativeKeyBindings(WidgetKeyboardEvent event)
         returns (MaybeNativeKeyBinding bindings);
 
+    /**
+     * Child informs the parent that the graphics objects are ready for
+     * compositing.  This is sent when all pending changes have been
+     * sent to the compositor and are ready to be shown on the next composite.
+     * @see PCompositor
+     * @see RequestNotifyAfterRemotePaint
+     */
+    async RemotePaintIsReady();
+
 child:
     /**
      * Notify the remote browser that it has been Show()n on this
      * side, with the given |visibleRect|.  This message is expected
      * to trigger creation of the remote browser's "widget".
      *
      * |Show()| and |Move()| take IntSizes rather than Rects because
      * content processes always render to a virtual <0, 0> top-left
@@ -494,16 +503,24 @@ child:
     SetUpdateHitRegion(bool aEnabled);
 
     /**
      * Tell the child to update its docShell's active state.
      */
     SetIsDocShellActive(bool aIsActive);
 
     /**
+     * The parent (chrome thread) requests that the child inform it when
+     * the graphics objects are ready to display.
+     * @see PCompositor
+     * @see RemotePaintIsReady
+     */
+    async RequestNotifyAfterRemotePaint();
+
+    /**
      * Tell the child that the UI resolution changed for the containing
      * window.
      */
     UIResolutionChanged();
 
 /*
  * FIXME: write protocol!
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -2853,16 +2853,29 @@ TabChild::OnShowTooltip(int32_t aXCoords
 NS_IMETHODIMP
 TabChild::OnHideTooltip()
 {
     SendHideTooltip();
     return NS_OK;
 }
 
 bool
+TabChild::RecvRequestNotifyAfterRemotePaint()
+{
+  // Get the CompositorChild instance for this content thread.
+  CompositorChild* compositor = CompositorChild::Get();
+
+  // Tell the CompositorChild that, when it gets a RemotePaintIsReady
+  // message that it should forward it us so that we can bounce it to our
+  // RenderFrameParent.
+  compositor->RequestNotifyAfterRemotePaint(this);
+  return true;
+}
+
+bool
 TabChild::RecvUIResolutionChanged()
 {
   static_cast<PuppetWidget*>(mWidget.get())->ClearBackingScaleCache();
   nsCOMPtr<nsIDocument> document(GetDocument());
   nsCOMPtr<nsIPresShell> presShell = document->GetShell();
   nsRefPtr<nsPresContext> presContext = presShell->GetPresContext();
   presContext->UIResolutionChanged();
   return true;
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -496,16 +496,18 @@ protected:
     virtual bool RecvSetIsDocShellActive(const bool& aIsActive) MOZ_OVERRIDE;
 
     virtual PIndexedDBChild* AllocPIndexedDBChild(const nsCString& aGroup,
                                                   const nsCString& aASCIIOrigin,
                                                   bool* /* aAllowed */) MOZ_OVERRIDE;
 
     virtual bool DeallocPIndexedDBChild(PIndexedDBChild* aActor) MOZ_OVERRIDE;
 
+    virtual bool RecvRequestNotifyAfterRemotePaint();
+
 private:
     /**
      * Create a new TabChild object.
      *
      * |aOwnOrContainingAppId| is the app-id of our frame or of the closest app
      * frame in the hierarchy which contains us.
      *
      * |aIsBrowserElement| indicates whether we're a browser (but not an app).
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -2146,16 +2146,35 @@ TabParent::GetUseAsyncPanZoom(bool* useA
 
 NS_IMETHODIMP
 TabParent::SetIsDocShellActive(bool isActive)
 {
   unused << SendSetIsDocShellActive(isActive);
   return NS_OK;
 }
 
+bool
+TabParent::RecvRemotePaintIsReady()
+{
+  nsCOMPtr<mozilla::dom::EventTarget> target = do_QueryInterface(mFrameElement);
+  if (!target) {
+    NS_WARNING("Could not locate target for MozAfterRemotePaint message.");
+    return true;
+  }
+
+  nsCOMPtr<nsIDOMEvent> event;
+  NS_NewDOMEvent(getter_AddRefs(event), mFrameElement, nullptr, nullptr);
+  event->InitEvent(NS_LITERAL_STRING("MozAfterRemotePaint"), false, false);
+  event->SetTrusted(true);
+  event->GetInternalNSEvent()->mFlags.mOnlyChromeDispatch = true;
+  bool dummy;
+  mFrameElement->DispatchEvent(event, &dummy);
+  return true;
+}
+
 class FakeChannel MOZ_FINAL : public nsIChannel,
                               public nsIAuthPromptCallback,
                               public nsIInterfaceRequestor,
                               public nsILoadContext
 {
 public:
   FakeChannel(const nsCString& aUri, uint64_t aCallbackId, Element* aElement)
     : mCallbackId(aCallbackId)
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -347,16 +347,18 @@ protected:
     nsIntPoint GetChildProcessOffset();
 
     virtual PRenderFrameParent* AllocPRenderFrameParent(ScrollingBehavior* aScrolling,
                                                         TextureFactoryIdentifier* aTextureFactoryIdentifier,
                                                         uint64_t* aLayersId,
                                                         bool* aSuccess) MOZ_OVERRIDE;
     virtual bool DeallocPRenderFrameParent(PRenderFrameParent* aFrame) MOZ_OVERRIDE;
 
+    virtual bool RecvRemotePaintIsReady() MOZ_OVERRIDE;
+
     // IME
     static TabParent *mIMETabParent;
     nsString mIMECacheText;
     uint32_t mIMESelectionAnchor;
     uint32_t mIMESelectionFocus;
     bool mIMEComposing;
     bool mIMECompositionEnding;
     // Buffer to store composition text during ResetInputState
--- a/gfx/layers/ipc/CompositorChild.cpp
+++ b/gfx/layers/ipc/CompositorChild.cpp
@@ -16,18 +16,21 @@
 #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"
+#include "mozilla/unused.h"
 
 using mozilla::layers::LayerTransactionChild;
+using mozilla::dom::TabChildBase;
+using mozilla::unused;
 
 namespace mozilla {
 namespace layers {
 
 /*static*/ CompositorChild* CompositorChild::sCompositor;
 
 Atomic<int32_t> CompositableForwarder::sSerialCounter(0);
 
@@ -251,11 +254,44 @@ CompositorChild::SharedFrameMetricsData:
 }
 
 uint32_t
 CompositorChild::SharedFrameMetricsData::GetAPZCId()
 {
   return mAPZCId;
 }
 
+
+bool
+CompositorChild::RecvRemotePaintIsReady()
+{
+  // Used on the content thread, this bounces the message to the
+  // TabParent (via the TabChild) if the notification was previously requested.
+  // XPCOM gives a soup of compiler errors when trying to do_QueryReference
+  // so I'm using static_cast<>
+  MOZ_LAYERS_LOG(("[RemoteGfx] CompositorChild received RemotePaintIsReady"));
+  nsRefPtr<nsISupports> iTabChildBase(do_QueryReferent(mWeakTabChild));
+  if (!iTabChildBase) {
+    MOZ_LAYERS_LOG(("[RemoteGfx] Note: TabChild was released before RemotePaintIsReady. "
+        "MozAfterRemotePaint will not be sent to listener."));
+    return true;
+  }
+  TabChildBase* tabChildBase = static_cast<TabChildBase*>(iTabChildBase.get());
+  TabChild* tabChild = static_cast<TabChild*>(tabChildBase);
+  MOZ_ASSERT(tabChild);
+  unused << tabChild->SendRemotePaintIsReady();
+  mWeakTabChild = nullptr;
+  return true;
+}
+
+
+void
+CompositorChild::RequestNotifyAfterRemotePaint(TabChild* aTabChild)
+{
+  MOZ_ASSERT(aTabChild, "NULL TabChild not allowed in CompositorChild::RequestNotifyAfterRemotePaint");
+  mWeakTabChild = do_GetWeakReference( static_cast<dom::TabChildBase*>(aTabChild) );
+  unused << SendRequestNotifyAfterRemotePaint();
+}
+
+
 } // namespace layers
 } // namespace mozilla
 
--- a/gfx/layers/ipc/CompositorChild.h
+++ b/gfx/layers/ipc/CompositorChild.h
@@ -13,22 +13,30 @@
 #include "mozilla/ipc/ProtocolUtils.h"
 #include "mozilla/layers/PCompositorChild.h"
 #include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsClassHashtable.h"           // for nsClassHashtable
 #include "nsCOMPtr.h"                   // for nsCOMPtr
 #include "nsHashKeys.h"                 // for nsUint64HashKey
 #include "nsISupportsImpl.h"            // for NS_INLINE_DECL_REFCOUNTING
 #include "ThreadSafeRefcountingWithMainThreadDestruction.h"
+#include "nsWeakReference.h"
 
 class nsIObserver;
 
 namespace mozilla {
+
+namespace dom {
+  class TabChild;
+}
+
 namespace layers {
 
+using mozilla::dom::TabChild;
+
 class ClientLayerManager;
 class CompositorParent;
 struct FrameMetrics;
 
 class CompositorChild MOZ_FINAL : public PCompositorChild
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(CompositorChild)
 
@@ -57,16 +65,24 @@ public:
   static bool ChildProcessHasCompositor() { return sCompositor != nullptr; }
 
   virtual bool RecvInvalidateAll() MOZ_OVERRIDE;
   virtual bool RecvOverfill(const uint32_t &aOverfill) MOZ_OVERRIDE;
   void AddOverfillObserver(ClientLayerManager* aLayerManager);
 
   virtual bool RecvDidComposite(const uint64_t& aId, const uint64_t& aTransactionId) MOZ_OVERRIDE;
 
+  /**
+   * Request that the parent tell us when graphics are ready on GPU.
+   * When we get that message, we bounce it to the TabParent via
+   * the TabChild
+   * @param tabChild The object to bounce the note to.  Non-NULL.
+   */
+  void RequestNotifyAfterRemotePaint(TabChild* aTabChild);
+
 private:
   // Private destructor, to discourage deletion outside of Release():
   virtual ~CompositorChild();
 
   virtual PLayerTransactionChild*
     AllocPLayerTransactionChild(const nsTArray<LayersBackend>& aBackendHints,
                                 const uint64_t& aId,
                                 TextureFactoryIdentifier* aTextureFactoryIdentifier,
@@ -78,16 +94,19 @@ private:
 
   virtual bool RecvSharedCompositorFrameMetrics(const mozilla::ipc::SharedMemoryBasic::Handle& metrics,
                                                 const CrossProcessMutexHandle& handle,
                                                 const uint32_t& aAPZCId) MOZ_OVERRIDE;
 
   virtual bool RecvReleaseSharedCompositorFrameMetrics(const ViewID& aId,
                                                        const uint32_t& aAPZCId) MOZ_OVERRIDE;
 
+  virtual bool
+  RecvRemotePaintIsReady() MOZ_OVERRIDE;
+
   // Class used to store the shared FrameMetrics, mutex, and APZCId  in a hash table
   class SharedFrameMetricsData {
   public:
     SharedFrameMetricsData(
         const mozilla::ipc::SharedMemoryBasic::Handle& metrics,
         const CrossProcessMutexHandle& handle,
         const uint32_t& aAPZCId);
 
@@ -112,16 +131,20 @@ private:
   // While this should be safe to use since the ViewID is unique
   nsClassHashtable<nsUint64HashKey, SharedFrameMetricsData> mFrameMetricsTable;
 
   // When we're in a child process, this is the process-global
   // compositor that we use to forward transactions directly to the
   // compositor context in another process.
   static CompositorChild* sCompositor;
 
+  // Weakly hold the TabChild that made a request to be alerted when
+  // the transaction has been received.
+  nsWeakPtr mWeakTabChild;      // type is TabChild
+
   DISALLOW_EVIL_CONSTRUCTORS(CompositorChild);
 
   // When we receive overfill numbers, notify these client layer managers
   nsAutoTArray<ClientLayerManager*,0> mOverfillObservers;
 };
 
 } // layers
 } // mozilla
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -1121,16 +1121,17 @@ class CrossProcessCompositorParent MOZ_F
   friend class CompositorParent;
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(CrossProcessCompositorParent)
 public:
   CrossProcessCompositorParent(Transport* aTransport, ProcessId aOtherProcess)
     : mTransport(aTransport)
     , mChildProcessId(aOtherProcess)
     , mCompositorThreadHolder(sCompositorThreadHolder)
+    , mNotifyAfterRemotePaint(false)
   {
     MOZ_ASSERT(NS_IsMainThread());
   }
 
   // IToplevelProtocol::CloneToplevel()
   virtual IToplevelProtocol*
   CloneToplevel(const InfallibleTArray<mozilla::ipc::ProtocolFdMapping>& aFds,
                 base::ProcessHandle aPeerProcess,
@@ -1148,16 +1149,21 @@ public:
   virtual bool RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot,
                                 const nsIntRect& aRect)
   { return true; }
   virtual bool RecvFlushRendering() MOZ_OVERRIDE { return true; }
   virtual bool RecvNotifyRegionInvalidated(const nsIntRegion& aRegion) { return true; }
   virtual bool RecvStartFrameTimeRecording(const int32_t& aBufferSize, uint32_t* aOutStartIndex) MOZ_OVERRIDE { return true; }
   virtual bool RecvStopFrameTimeRecording(const uint32_t& aStartIndex, InfallibleTArray<float>* intervals) MOZ_OVERRIDE  { return true; }
 
+  /**
+   * Tells this CompositorParent to send a message when the compositor has received the transaction.
+   */
+  virtual bool RecvRequestNotifyAfterRemotePaint() MOZ_OVERRIDE;
+
   virtual PLayerTransactionParent*
     AllocPLayerTransactionParent(const nsTArray<LayersBackend>& aBackendHints,
                                  const uint64_t& aId,
                                  TextureFactoryIdentifier* aTextureFactoryIdentifier,
                                  bool *aSuccess) MOZ_OVERRIDE;
 
   virtual bool DeallocPLayerTransactionParent(PLayerTransactionParent* aLayers) MOZ_OVERRIDE;
 
@@ -1189,16 +1195,19 @@ private:
   // reference to top-level actors.  So we hold a reference to
   // ourself.  This is released (deferred) in ActorDestroy().
   nsRefPtr<CrossProcessCompositorParent> mSelfRef;
   Transport* mTransport;
   // Child side's process Id.
   base::ProcessId mChildProcessId;
 
   nsRefPtr<CompositorThreadHolder> mCompositorThreadHolder;
+  // If true, we should send a RemotePaintIsReady message when the layer transaction
+  // is received
+  bool mNotifyAfterRemotePaint;
 };
 
 void
 CompositorParent::DidComposite()
 {
   if (mPendingTransaction) {
     unused << SendDidComposite(0, mPendingTransaction);
     mPendingTransaction = 0;
@@ -1281,16 +1290,23 @@ CompositorParent::GetIndirectShadowTree(
 }
 
 static void
 RemoveIndirectTree(uint64_t aId)
 {
   sIndirectLayerTrees.erase(aId);
 }
 
+bool
+CrossProcessCompositorParent::RecvRequestNotifyAfterRemotePaint()
+{
+  mNotifyAfterRemotePaint = true;
+  return true;
+}
+
 void
 CrossProcessCompositorParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   MessageLoop::current()->PostTask(
     FROM_HERE,
     NewRunnableMethod(this, &CrossProcessCompositorParent::DeferredDestroy));
 }
 
@@ -1375,16 +1391,23 @@ CrossProcessCompositorParent::ShadowLaye
   Layer* shadowRoot = aLayerTree->GetRoot();
   if (shadowRoot) {
     SetShadowProperties(shadowRoot);
   }
   UpdateIndirectTree(id, shadowRoot, aTargetConfig);
 
   state->mParent->NotifyShadowTreeTransaction(id, aIsFirstPaint, aScheduleComposite,
       aPaintSequenceNumber, aIsRepeatTransaction);
+
+  // Send the 'remote paint ready' message to the content thread if it has already asked.
+  if(mNotifyAfterRemotePaint)  {
+    unused << SendRemotePaintIsReady();
+    mNotifyAfterRemotePaint = false;
+  }
+
   aLayerTree->SetPendingTransactionId(aTransactionId);
 }
 
 void
 CrossProcessCompositorParent::DidComposite(uint64_t aId)
 {
   LayerTransactionParent *layerTree = sIndirectLayerTrees[aId].mLayerTree;
   if (layerTree && layerTree->GetPendingTransactionId()) {
--- a/gfx/layers/ipc/CompositorParent.h
+++ b/gfx/layers/ipc/CompositorParent.h
@@ -112,16 +112,20 @@ public:
   virtual bool RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot,
                                 const nsIntRect& aRect) MOZ_OVERRIDE;
   virtual bool RecvFlushRendering() MOZ_OVERRIDE;
 
   virtual bool RecvNotifyRegionInvalidated(const nsIntRegion& aRegion) MOZ_OVERRIDE;
   virtual bool RecvStartFrameTimeRecording(const int32_t& aBufferSize, uint32_t* aOutStartIndex) MOZ_OVERRIDE;
   virtual bool RecvStopFrameTimeRecording(const uint32_t& aStartIndex, InfallibleTArray<float>* intervals) MOZ_OVERRIDE;
 
+  // Unused for chrome <-> compositor communication (which this class does).
+  // @see CrossProcessCompositorParent::RecvRequestNotifyAfterRemotePaint
+  virtual bool RecvRequestNotifyAfterRemotePaint() MOZ_OVERRIDE { return true; };
+
   virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
 
   virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
                                    const uint64_t& aTransactionId,
                                    const TargetConfig& aTargetConfig,
                                    bool aIsFirstPaint,
                                    bool aScheduleComposite,
                                    uint32_t aPaintSequenceNumber,
--- a/gfx/layers/ipc/PCompositor.ipdl
+++ b/gfx/layers/ipc/PCompositor.ipdl
@@ -42,16 +42,25 @@ child:
   // 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, uint64_t transactionId);
 
   // The parent sends the child the requested fill ratio numbers.
   async Overfill(uint32_t aOverfill);
 
+  /**
+   * Parent informs the child that the graphics objects are ready for
+   * compositing.  This usually means that the graphics objects (textures
+   * and the like) are available on the GPU.  This is used for chrome UI.
+   * @see RequestNotifyAfterRemotePaint
+   * @see PBrowser
+   */
+  async RemotePaintIsReady();
+
 parent:
 
   // Child sends the parent a request for fill ratio numbers.
   async RequestOverfill();
 
   // The child is about to be destroyed, so perform any necessary cleanup.
   sync WillStop();
 
@@ -88,16 +97,24 @@ parent:
   // layersBackendHints[0] is the best backend. If any hints are LayersBackend::LAYERS_NONE
   // that hint is ignored.
   sync PLayerTransaction(LayersBackend[] layersBackendHints, uint64_t id)
     returns (TextureFactoryIdentifier textureFactoryIdentifier, bool success);
 
   // Notify the compositor that a region of the screen has been invalidated.
   async NotifyRegionInvalidated(nsIntRegion region);
 
+  /**
+   * The child (content/chrome thread) requests that the parent inform it when
+   * the graphics objects are ready to display.
+   * @see PBrowser
+   * @see RemotePaintIsReady
+   */
+  async RequestNotifyAfterRemotePaint();
+
 child:
   // Send back Compositor Frame Metrics from APZCs so tiled layers can
   // update progressively.
   async SharedCompositorFrameMetrics(Handle metrics, CrossProcessMutexHandle mutex, uint32_t aAPZCId);
   async ReleaseSharedCompositorFrameMetrics(ViewID aId, uint32_t aAPZCId);
 };
 
 } // layers
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -8378,16 +8378,23 @@ PresShell::WillPaintWindow()
 #ifndef XP_MACOSX
   rootPresContext->ApplyPluginGeometryUpdates();
 #endif
 }
 
 void
 PresShell::DidPaintWindow()
 {
+  if (mDocument) {
+    nsCOMPtr<nsPIDOMWindow> window = mDocument->GetWindow();
+    if (window) {
+      window->SendAfterRemotePaintIfRequested();
+    }
+  }
+
   nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext();
   if (rootPresContext != mPresContext) {
     // This could be a popup's presshell. No point in notifying XPConnect
     // about compositing of popups.
     return;
   }
 
   if (nsContentUtils::XPConnect()) {