Bug 1378203 - Add nsITabParent.canPresent. r?billm draft
authorMike Conley <mconley@mozilla.com>
Tue, 04 Jul 2017 21:24:42 -0400
changeset 605491 761186f6cf4718ac8476aef70af9e761b0bf402e
parent 605338 173533a310060d0a52d6f6c3b3bee998ab9199d2
child 605492 c8a96bac7d28164c379d3960d74455ad6334c7e0
push id67419
push usermconley@mozilla.com
push dateFri, 07 Jul 2017 20:07:29 +0000
reviewersbillm
bugs1378203
milestone56.0a1
Bug 1378203 - Add nsITabParent.canPresent. r?billm nsITabParent.canPresent is set to true once the child has informed the parent that it has initialized and has enough information to paint something. MozReview-Commit-ID: GgReQT4MTtT
dom/interfaces/base/nsITabParent.idl
dom/ipc/PBrowser.ipdl
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
widget/PuppetWidget.cpp
--- a/dom/interfaces/base/nsITabParent.idl
+++ b/dom/interfaces/base/nsITabParent.idl
@@ -53,23 +53,29 @@ interface nsITabParent : nsISupports
    * If aForDocumentNavigation is false, navigate by element.
    *
    * If aForward is true, navigate to the first focusable element or document.
    * If aForward is false, navigate to the last focusable element or document.
    */
   void navigateByKey(in bool aForward, in bool aForDocumentNavigation);
 
   readonly attribute boolean hasContentOpener;
+
   /**
    * True if we've previously received layers for this tab when switching to
    * it.
    */
   readonly attribute boolean hasPresented;
 
   /**
+   * True if the tab has ever loaded enough to paint something.
+   */
+  readonly attribute boolean canPresent;
+
+  /**
    * Ensures that the content process which has this tab parent has all of the
    * permissions required to load a document with the given principal.
    */
   void transmitPermissionsForPrincipal(in nsIPrincipal aPrincipal);
 
   /**
    * True if any of the frames loaded in the TabChild have registered
    * an onbeforeunload event handler.
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -544,16 +544,23 @@ parent:
 
 child:
     async NativeSynthesisResponse(uint64_t aObserverId, nsCString aResponse);
 
 
 parent:
 
     /**
+     * Child informs the parent that it has initialized enough to paint something
+     * if asked to. It is expected that this message be sent only once during the
+     * lifetime of a PBrowser.
+     */
+    async CanPresent();
+
+    /**
      * 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();
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -395,16 +395,17 @@ TabChild::TabChild(nsIContentChild* aMan
   , mDefaultScale(0)
   , mIsTransparent(false)
   , mIPCOpen(false)
   , mParentIsActive(false)
   , mDidSetRealShowInfo(false)
   , mDidLoadURLInit(false)
   , mAwaitingLA(false)
   , mSkipKeyPress(false)
+  , mToldParentCanPresent(false)
   , mLayerObserverEpoch(0)
 #if defined(XP_WIN) && defined(ACCESSIBILITY)
   , mNativeWindowHandle(0)
 #endif
 #if defined(ACCESSIBILITY)
   , mTopLevelDocAccessibleChild(nullptr)
 #endif
   , mPendingDocShellIsActive(false)
@@ -2379,16 +2380,25 @@ TabChild::RemovePendingDocShellBlocker()
   if (!mPendingDocShellBlockers && mPendingDocShellReceivedMessage) {
     mPendingDocShellReceivedMessage = false;
     InternalSetDocShellIsActive(mPendingDocShellIsActive,
                                 mPendingDocShellPreserveLayers);
   }
 }
 
 void
+TabChild::CanPresent()
+{
+  if (!mToldParentCanPresent) {
+    Unused << SendCanPresent();
+    mToldParentCanPresent = true;
+  }
+}
+
+void
 TabChild::InternalSetDocShellIsActive(bool aIsActive, bool aPreserveLayers)
 {
   auto clearForcePaint = MakeScopeExit([&] {
     // We might force a paint, or we might already have painted and this is a
     // no-op. In either case, once we exit this scope, we need to alert the
     // ProcessHangMonitor that we've finished responding to what might have
     // been a request to force paint. This is so that the BackgroundHangMonitor
     // for force painting can be made to wait again.
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -710,16 +710,18 @@ public:
   void RemovePendingDocShellBlocker();
 
   // The HANDLE object for the widget this TabChild in.
   WindowsHandle WidgetNativeData()
   {
     return mWidgetNativeData;
   }
 
+  void CanPresent();
+
 protected:
   virtual ~TabChild();
 
   virtual PRenderFrameChild* AllocPRenderFrameChild() override;
 
   virtual bool DeallocPRenderFrameChild(PRenderFrameChild* aFrame) override;
 
   virtual mozilla::ipc::IPCResult RecvDestroy() override;
@@ -864,16 +866,17 @@ private:
   bool mIPCOpen;
   bool mParentIsActive;
   CSSSize mUnscaledInnerSize;
   bool mDidSetRealShowInfo;
   bool mDidLoadURLInit;
   bool mAwaitingLA;
 
   bool mSkipKeyPress;
+  bool mToldParentCanPresent;
 
   // Store the end time of the handling of the last repeated keydown/keypress
   // event so that in case event handling takes time, some repeated events can
   // be skipped to not flood child process.
   mozilla::TimeStamp mRepeatedKeyEventTime;
 
   // Similar to mRepeatedKeyEventTime, store the end time (from parent process)
   // of handling the last repeated wheel event so that in case event handling
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -164,16 +164,17 @@ TabParent::TabParent(nsIContentParent* a
   , mTabSetsCursor(false)
   , mHasContentOpener(false)
 #ifdef DEBUG
   , mActiveSupressDisplayportCount(0)
 #endif
   , mLayerTreeEpoch(0)
   , mPreserveLayers(false)
   , mHasPresented(false)
+  , mCanPresent(false)
   , mHasBeforeUnload(false)
 {
   MOZ_ASSERT(aManager);
 }
 
 TabParent::~TabParent()
 {
 }
@@ -2785,16 +2786,23 @@ TabParent::SetHasContentOpener(bool aHas
 NS_IMETHODIMP
 TabParent::GetHasPresented(bool* aResult)
 {
   *aResult = mHasPresented;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+TabParent::GetCanPresent(bool* aResult)
+{
+  *aResult = mCanPresent;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 TabParent::NavigateByKey(bool aForward, bool aForDocumentNavigation)
 {
   Unused << SendNavigateByKey(aForward, aForDocumentNavigation);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TabParent::TransmitPermissionsForPrincipal(nsIPrincipal* aPrincipal)
@@ -2857,16 +2865,20 @@ TabParent::LayerTreeUpdate(uint64_t aEpo
   nsCOMPtr<mozilla::dom::EventTarget> target = do_QueryInterface(mFrameElement);
   if (!target) {
     NS_WARNING("Could not locate target for layer tree message.");
     return;
   }
 
   RefPtr<Event> event = NS_NewDOMEvent(mFrameElement, nullptr, nullptr);
   if (aActive) {
+    // mCanPresent might not have been set to true yet by RecvCanPresent, since
+    // the layer tree update message is sent from a different thread. It's pretty
+    // safe to assume, however, that if we've presented, that we can present.
+    mCanPresent = true;
     mHasPresented = true;
     event->InitEvent(NS_LITERAL_STRING("MozLayerTreeReady"), true, false);
   } else {
     event->InitEvent(NS_LITERAL_STRING("MozLayerTreeCleared"), true, false);
   }
   event->SetTrusted(true);
   event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
   bool dummy;
@@ -2879,16 +2891,23 @@ TabParent::RecvForcePaintNoOp(const uint
   // We sent a ForcePaint message when layers were already visible. In this
   // case, we should act as if an update occurred even though we already have
   // the layers.
   LayerTreeUpdate(aLayerObserverEpoch, true);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
+TabParent::RecvCanPresent()
+{
+  mCanPresent = true;
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
 TabParent::RecvRemotePaintIsReady()
 {
   nsCOMPtr<mozilla::dom::EventTarget> target = do_QueryInterface(mFrameElement);
   if (!target) {
     NS_WARNING("Could not locate target for MozAfterRemotePaint message.");
     return IPC_OK();
   }
 
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -613,16 +613,18 @@ protected:
 
   Element* mFrameElement;
   nsCOMPtr<nsIBrowserDOMWindow> mBrowserDOMWindow;
 
   virtual PRenderFrameParent* AllocPRenderFrameParent() override;
 
   virtual bool DeallocPRenderFrameParent(PRenderFrameParent* aFrame) override;
 
+  virtual mozilla::ipc::IPCResult RecvCanPresent() override;
+
   virtual mozilla::ipc::IPCResult RecvRemotePaintIsReady() override;
 
   virtual mozilla::ipc::IPCResult RecvForcePaintNoOp(const uint64_t& aLayerObserverEpoch) override;
 
   virtual mozilla::ipc::IPCResult RecvSetDimensions(const uint32_t& aFlags,
                                                     const int32_t& aX, const int32_t& aY,
                                                     const int32_t& aCx, const int32_t& aCy) override;
 
@@ -757,20 +759,23 @@ private:
   static void RemoveTabParentFromTable(uint64_t aLayersId);
 
   uint64_t mLayerTreeEpoch;
 
   // If this flag is set, then the tab's layers will be preserved even when
   // the tab's docshell is inactive.
   bool mPreserveLayers;
 
-  // True if this TabParent has had its layer tree sent to the compositor
+  // True if the TabChild has had its layer tree sent to the compositor
   // at least once.
   bool mHasPresented;
 
+  // True if the TabChild has ever reported that it is able to paint.
+  bool mCanPresent;
+
   // True if at least one window hosted in the TabChild has added a
   // beforeunload event listener.
   bool mHasBeforeUnload;
 
 public:
   static TabParent* GetTabParentFromLayersId(uint64_t aLayersId);
 };
 
--- a/widget/PuppetWidget.cpp
+++ b/widget/PuppetWidget.cpp
@@ -1022,16 +1022,20 @@ PuppetWidget::ClearCachedCursor()
 nsresult
 PuppetWidget::Paint()
 {
   MOZ_ASSERT(!mDirtyRegion.IsEmpty(), "paint event logic messed up");
 
   if (!GetCurrentWidgetListener())
     return NS_OK;
 
+  if (mTabChild) {
+    mTabChild->CanPresent();
+  }
+
   LayoutDeviceIntRegion region = mDirtyRegion;
 
   // reset repaint tracking
   mDirtyRegion.SetEmpty();
   mPaintTask.Revoke();
 
   RefPtr<PuppetWidget> strongThis(this);