Bug 1342927 - Fire a MozTabChildNotReady event on a frameloader if force-painting a tab without a TabChild. r=billm
☠☠ backed out by d3fb96a2c6ef ☠ ☠
authorMike Conley <mconley@mozilla.com>
Tue, 28 Feb 2017 17:22:02 -0500
changeset 345920 2f9561ccd777eab9e51ede75eb5513cc57281f50
parent 345919 28ac9c36b726dcd0868292b77328712a1d2b3813
child 345921 9d44c499c3dbfdbb6312608012f142a82f1835fb
push id31451
push usercbook@mozilla.com
push dateMon, 06 Mar 2017 09:52:09 +0000
treeherdermozilla-central@7099e03837e8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbillm
bugs1342927
milestone54.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 1342927 - Fire a MozTabChildNotReady event on a frameloader if force-painting a tab without a TabChild. r=billm MozReview-Commit-ID: D8vgvQ3MLJN
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
dom/ipc/PProcessHangMonitor.ipdl
dom/ipc/ProcessHangMonitor.cpp
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -4245,16 +4245,33 @@ ContentParent::RecvDeallocateTabId(const
 mozilla::ipc::IPCResult
 ContentParent::RecvNotifyTabDestroying(const TabId& aTabId,
                                        const ContentParentId& aCpId)
 {
   NotifyTabDestroying(aTabId, aCpId);
   return IPC_OK();
 }
 
+mozilla::ipc::IPCResult
+ContentParent::RecvTabChildNotReady(const TabId& aTabId)
+{
+  ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+  RefPtr<TabParent> tp =
+    cpm->GetTopLevelTabParentByProcessAndTabId(this->ChildID(), aTabId);
+
+  if (!tp) {
+    NS_WARNING("Couldn't find TabParent for TabChildNotReady message.");
+    return IPC_OK();
+  }
+
+  tp->DispatchTabChildNotReadyEvent();
+
+  return IPC_OK();
+}
+
 nsTArray<TabContext>
 ContentParent::GetManagedTabContext()
 {
   return Move(ContentProcessManager::GetSingleton()->
           GetTabContextByContentProcess(this->ChildID()));
 }
 
 mozilla::docshell::POfflineCacheUpdateParent*
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -486,16 +486,18 @@ public:
 
   virtual mozilla::ipc::IPCResult RecvDeallocateTabId(const TabId& aTabId,
                                                       const ContentParentId& aCpId,
                                                       const bool& aMarkedDestroying) override;
 
   virtual mozilla::ipc::IPCResult RecvNotifyTabDestroying(const TabId& aTabId,
                                                           const ContentParentId& aCpId) override;
 
+  virtual mozilla::ipc::IPCResult RecvTabChildNotReady(const TabId& aTabId) override;
+
   nsTArray<TabContext> GetManagedTabContext();
 
   virtual POfflineCacheUpdateParent*
   AllocPOfflineCacheUpdateParent(const URIParams& aManifestURI,
                                  const URIParams& aDocumentURI,
                                  const PrincipalInfo& aLoadingPrincipalInfo,
                                  const bool& aStickDocument) override;
 
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -1017,16 +1017,19 @@ parent:
                           ContentParentId cpId,
                           bool aMarkedDestroying);
 
     /**
      * Tell the chrome process there is a destruction of PBrowser(Tab)
      */
     async NotifyTabDestroying(TabId tabId,
                               ContentParentId cpId);
+
+    async TabChildNotReady(TabId tabId);
+
     /**
      * Starts an offline application cache update.
      * @param manifestURI
      *   URI of the manifest to fetch, the application cache group ID
      * @param documentURI
      *   URI of the document that referred the manifest
      * @param loadingPrincipal
      *   Principal of the document that referred the manifest
--- a/dom/ipc/PProcessHangMonitor.ipdl
+++ b/dom/ipc/PProcessHangMonitor.ipdl
@@ -29,16 +29,17 @@ union HangData
   PluginHangData;
 };
 
 protocol PProcessHangMonitor
 {
 parent:
   async HangEvidence(HangData data);
   async ClearHang();
+  async Ready();
 
 child:
   async TerminateScript();
 
   async BeginStartingDebugger();
   async EndStartingDebugger();
 
   async ForcePaint(TabId tabId, uint64_t aLayerObserverEpoch);
--- a/dom/ipc/ProcessHangMonitor.cpp
+++ b/dom/ipc/ProcessHangMonitor.cpp
@@ -7,21 +7,23 @@
 #include "mozilla/ProcessHangMonitor.h"
 #include "mozilla/ProcessHangMonitorIPC.h"
 
 #include "jsapi.h"
 #include "js/GCAPI.h"
 
 #include "mozilla/Atomics.h"
 #include "mozilla/BackgroundHangMonitor.h"
+#include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/TabParent.h"
+#include "mozilla/ipc/TaskFactory.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/plugins/PluginBridge.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Unused.h"
 
 #include "nsIFrameLoader.h"
 #include "nsIHangReport.h"
 #include "nsITabParent.h"
@@ -189,16 +191,18 @@ public:
     mDumpId = aDumpId;
   }
 
   void ClearHang() {
     mHangData = HangData();
     mDumpId.Truncate();
   }
 
+  void DispatchTabChildNotReady(TabId aTabId);
+
 private:
   ~HangMonitoredProcess() = default;
 
   // Everything here is main thread-only.
   HangMonitorParent* mActor;
   ContentParent* mContentParent;
   HangData mHangData;
   nsAutoString mDumpId;
@@ -208,16 +212,17 @@ class HangMonitorParent
   : public PProcessHangMonitorParent
 {
 public:
   explicit HangMonitorParent(ProcessHangMonitor* aMonitor);
   ~HangMonitorParent() override;
 
   void Bind(Endpoint<PProcessHangMonitorParent>&& aEndpoint);
 
+  mozilla::ipc::IPCResult RecvReady() override;
   mozilla::ipc::IPCResult RecvHangEvidence(const HangData& aHangData) override;
   mozilla::ipc::IPCResult RecvClearHang() override;
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
   void SetProcess(HangMonitoredProcess* aProcess) { mProcess = aProcess; }
 
   void Shutdown();
@@ -236,36 +241,45 @@ public:
    */
   void UpdateMinidump(uint32_t aPluginId, const nsString& aDumpId);
 
   MessageLoop* MonitorLoop() { return mHangMonitor->MonitorLoop(); }
 
 private:
   bool TakeBrowserMinidump(const PluginHangData& aPhd, nsString& aCrashId);
 
+  void DispatchTabChildNotReady(TabId aTabId);
+
   void ForcePaintOnThread(TabId aTabId, uint64_t aLayerObserverEpoch);
 
   void ShutdownOnThread();
 
   const RefPtr<ProcessHangMonitor> mHangMonitor;
 
   // This field is read-only after construction.
   bool mReportHangs;
 
+  // This field is only accessed on the hang thread. Inits to
+  // false, and will flip to true once the HangMonitorChild is
+  // constructed in the child process, and sends a message saying
+  // so.
+  bool mReady;
+
   // This field is only accessed on the hang thread.
   bool mIPCOpen;
 
   Monitor mMonitor;
 
   // Must be accessed with mMonitor held.
   RefPtr<HangMonitoredProcess> mProcess;
   bool mShutdownDone;
   // Map from plugin ID to crash dump ID. Protected by mBrowserCrashDumpHashLock.
   nsDataHashtable<nsUint32HashKey, nsString> mBrowserCrashDumpIds;
   Mutex mBrowserCrashDumpHashLock;
+  mozilla::ipc::TaskFactory<HangMonitorParent> mMainThreadTaskFactory;
 };
 
 } // namespace
 
 /* HangMonitorChild implementation */
 
 HangMonitorChild::HangMonitorChild(ProcessHangMonitor* aMonitor)
  : mHangMonitor(aMonitor),
@@ -313,16 +327,21 @@ HangMonitorChild::InterruptCallback()
     mForcePaint = false;
   }
 
   if (forcePaint) {
     RefPtr<TabChild> tabChild = TabChild::FindTabChild(forcePaintTab);
     if (tabChild) {
       js::AutoAssertNoContentJS nojs(mContext);
       tabChild->ForcePaint(forcePaintEpoch);
+    } else {
+      auto cc = ContentChild::GetSingleton();
+      if (cc) {
+        cc->SendTabChildNotReady(forcePaintTab);
+      }
     }
   }
 }
 
 void
 HangMonitorChild::Shutdown()
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
@@ -418,16 +437,18 @@ HangMonitorChild::Bind(Endpoint<PProcess
 {
   MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
 
   MOZ_ASSERT(!sInstance);
   sInstance = this;
 
   DebugOnly<bool> ok = aEndpoint.Bind(this);
   MOZ_ASSERT(ok);
+
+  Unused << SendReady();
 }
 
 void
 HangMonitorChild::NotifySlowScriptAsync(TabId aTabId,
                                         const nsCString& aFileName,
                                         unsigned aLineNo)
 {
   if (mIPCOpen) {
@@ -540,20 +561,22 @@ HangMonitorChild::ClearHangAsync()
     Unused << SendClearHang();
   }
 }
 
 /* HangMonitorParent implementation */
 
 HangMonitorParent::HangMonitorParent(ProcessHangMonitor* aMonitor)
  : mHangMonitor(aMonitor),
+   mReady(false),
    mIPCOpen(true),
    mMonitor("HangMonitorParent lock"),
    mShutdownDone(false),
-   mBrowserCrashDumpHashLock("mBrowserCrashDumpIds lock")
+   mBrowserCrashDumpHashLock("mBrowserCrashDumpIds lock"),
+   mMainThreadTaskFactory(this)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   mReportHangs = mozilla::Preferences::GetBool("dom.ipc.reportProcessHangs", false);
 }
 
 HangMonitorParent::~HangMonitorParent()
 {
 #ifdef MOZ_CRASHREPORTER
@@ -610,22 +633,47 @@ HangMonitorParent::ForcePaint(dom::TabPa
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   TabId id = aTab->GetTabId();
   MonitorLoop()->PostTask(NewNonOwningRunnableMethod<TabId, uint64_t>(
                             this, &HangMonitorParent::ForcePaintOnThread, id, aLayerObserverEpoch));
 }
 
 void
+HangMonitorParent::DispatchTabChildNotReady(TabId aTabId)
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+  if (!mProcess) {
+    return;
+  }
+
+  mProcess->DispatchTabChildNotReady(aTabId);
+}
+
+void
 HangMonitorParent::ForcePaintOnThread(TabId aTabId, uint64_t aLayerObserverEpoch)
 {
   MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
 
   if (mIPCOpen) {
-    Unused << SendForcePaint(aTabId, aLayerObserverEpoch);
+    if (mReady) {
+      Unused << SendForcePaint(aTabId, aLayerObserverEpoch);
+    } else {
+      // We've never heard from the HangMonitorChild before, so
+      // it's either not finished setting up, or has only recently
+      // finished setting up. In either case, we're dealing with
+      // a new content process that probably hasn't had time to
+      // get the ContentChild, let alone the TabChild for aTabId,
+      // set up, and so attempting to force paint on the non-existant
+      // TabChild is not going to work. Instead, we tell the main
+      // thread that we're waiting on a TabChild to be created.
+      NS_DispatchToMainThread(
+        mMainThreadTaskFactory.NewRunnableMethod(
+          &HangMonitorParent::DispatchTabChildNotReady, aTabId));
+    }
   }
 }
 
 void
 HangMonitorParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
   mIPCOpen = false;
@@ -714,16 +762,24 @@ HangMonitorParent::TakeBrowserMinidump(c
     }
   }
 #endif // MOZ_CRASHREPORTER
 
   return false;
 }
 
 mozilla::ipc::IPCResult
+HangMonitorParent::RecvReady()
+{
+  MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
+  mReady = true;
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
 HangMonitorParent::RecvHangEvidence(const HangData& aHangData)
 {
   // chrome process, background thread
   MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
 
   if (!mReportHangs) {
     return IPC_OK();
   }
@@ -1053,16 +1109,27 @@ HangMonitoredProcess::UserCanceled()
 
   if (mActor) {
     uint32_t id = mHangData.get_PluginHangData().pluginId();
     mActor->CleanupPluginHang(id, true);
   }
   return NS_OK;
 }
 
+void
+HangMonitoredProcess::DispatchTabChildNotReady(TabId aTabId)
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+  if (!mContentParent) {
+    return;
+  }
+
+  Unused << mContentParent->RecvTabChildNotReady(aTabId);
+}
+
 static bool
 InterruptCallback(JSContext* cx)
 {
   if (HangMonitorChild* child = HangMonitorChild::Get()) {
     child->InterruptCallback();
   }
 
   return true;
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -3327,16 +3327,35 @@ TabParent::LiveResizeStarted()
 }
 
 void
 TabParent::LiveResizeStopped()
 {
   SuppressDisplayport(false);
 }
 
+void
+TabParent::DispatchTabChildNotReadyEvent()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsCOMPtr<mozilla::dom::EventTarget> target = do_QueryInterface(mFrameElement);
+  if (!target) {
+    NS_WARNING("Could not locate target for tab child not ready event.");
+    return;
+  }
+
+  RefPtr<Event> event = NS_NewDOMEvent(mFrameElement, nullptr, nullptr);
+  event->InitEvent(NS_LITERAL_STRING("MozTabChildNotReady"), true, false);
+  event->SetTrusted(true);
+  event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
+  bool dummy;
+  mFrameElement->DispatchEvent(event, &dummy);
+}
+
 NS_IMETHODIMP
 FakeChannel::OnAuthAvailable(nsISupports *aContext, nsIAuthInformation *aAuthInfo)
 {
   nsAuthInformationHolder* holder =
     static_cast<nsAuthInformationHolder*>(aAuthInfo);
 
   if (!net::gNeckoChild->SendOnAuthAvailable(mCallbackId,
                                              holder->User(),
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -594,16 +594,18 @@ public:
                           uint64_t* aLayersId);
 
   mozilla::ipc::IPCResult RecvEnsureLayersConnected() override;
 
   // LiveResizeListener implementation
   void LiveResizeStarted() override;
   void LiveResizeStopped() override;
 
+  void DispatchTabChildNotReadyEvent();
+
 protected:
   bool ReceiveMessage(const nsString& aMessage,
                       bool aSync,
                       ipc::StructuredCloneData* aData,
                       mozilla::jsipc::CpowHolder* aCpows,
                       nsIPrincipal* aPrincipal,
                       nsTArray<ipc::StructuredCloneData>* aJSONRetVal = nullptr);