From bae06d41c923bfa4752f601b085212a3bd679315 Mon Sep 17 00:00:00 2001 From: Mike Conley Date: Tue, 28 Feb 2017 17:22:02 -0500 Subject: [PATCH] Bug 1342927 - Fire a MozTabChildNotReady event on a frameloader if force-painting a tab without a TabChild. r=billm MozReview-Commit-ID: D8vgvQ3MLJN --HG-- extra : rebase_source : 29192f938b57018eb947cb9d55a3ac3ab8621e76 --- dom/ipc/ContentParent.cpp | 17 ++++++++ dom/ipc/ContentParent.h | 2 + dom/ipc/PContent.ipdl | 3 ++ dom/ipc/PProcessHangMonitor.ipdl | 1 + dom/ipc/ProcessHangMonitor.cpp | 71 +++++++++++++++++++++++++++++++- dom/ipc/TabParent.cpp | 19 +++++++++ dom/ipc/TabParent.h | 2 + 7 files changed, 113 insertions(+), 2 deletions(-) diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index abd7f1d76874..7855298439c3 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -4250,6 +4250,23 @@ ContentParent::RecvNotifyTabDestroying(const TabId& aTabId, return IPC_OK(); } +mozilla::ipc::IPCResult +ContentParent::RecvTabChildNotReady(const TabId& aTabId) +{ + ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); + RefPtr 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 ContentParent::GetManagedTabContext() { diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index bcce037cb2ad..277af8e043c9 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -491,6 +491,8 @@ public: virtual mozilla::ipc::IPCResult RecvNotifyTabDestroying(const TabId& aTabId, const ContentParentId& aCpId) override; + virtual mozilla::ipc::IPCResult RecvTabChildNotReady(const TabId& aTabId) override; + nsTArray GetManagedTabContext(); virtual POfflineCacheUpdateParent* diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 40eabfd39d99..276c266d6c19 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -1022,6 +1022,9 @@ parent: */ async NotifyTabDestroying(TabId tabId, ContentParentId cpId); + + async TabChildNotReady(TabId tabId); + /** * Starts an offline application cache update. * @param manifestURI diff --git a/dom/ipc/PProcessHangMonitor.ipdl b/dom/ipc/PProcessHangMonitor.ipdl index 07ee7a21118d..0ad30fe4e8ec 100644 --- a/dom/ipc/PProcessHangMonitor.ipdl +++ b/dom/ipc/PProcessHangMonitor.ipdl @@ -34,6 +34,7 @@ protocol PProcessHangMonitor parent: async HangEvidence(HangData data); async ClearHang(); + async Ready(); child: async TerminateScript(); diff --git a/dom/ipc/ProcessHangMonitor.cpp b/dom/ipc/ProcessHangMonitor.cpp index 3e69f94907ed..7e5d3b921f29 100644 --- a/dom/ipc/ProcessHangMonitor.cpp +++ b/dom/ipc/ProcessHangMonitor.cpp @@ -12,11 +12,13 @@ #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" @@ -194,6 +196,8 @@ public: mDumpId.Truncate(); } + void DispatchTabChildNotReady(TabId aTabId); + private: ~HangMonitoredProcess() = default; @@ -213,6 +217,7 @@ public: void Bind(Endpoint&& aEndpoint); + mozilla::ipc::IPCResult RecvReady() override; mozilla::ipc::IPCResult RecvHangEvidence(const HangData& aHangData) override; mozilla::ipc::IPCResult RecvClearHang() override; @@ -241,6 +246,8 @@ public: private: bool TakeBrowserMinidump(const PluginHangData& aPhd, nsString& aCrashId); + void DispatchTabChildNotReady(TabId aTabId); + void ForcePaintOnThread(TabId aTabId, uint64_t aLayerObserverEpoch); void ShutdownOnThread(); @@ -250,6 +257,12 @@ private: // 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; @@ -261,6 +274,7 @@ private: // Map from plugin ID to crash dump ID. Protected by mBrowserCrashDumpHashLock. nsDataHashtable mBrowserCrashDumpIds; Mutex mBrowserCrashDumpHashLock; + mozilla::ipc::TaskFactory mMainThreadTaskFactory; }; } // namespace @@ -318,6 +332,11 @@ HangMonitorChild::InterruptCallback() if (tabChild) { js::AutoAssertNoContentJS nojs(mContext); tabChild->ForcePaint(forcePaintEpoch); + } else { + auto cc = ContentChild::GetSingleton(); + if (cc) { + cc->SendTabChildNotReady(forcePaintTab); + } } } } @@ -423,6 +442,8 @@ HangMonitorChild::Bind(Endpoint&& aEndpoint) DebugOnly ok = aEndpoint.Bind(this); MOZ_ASSERT(ok); + + Unused << SendReady(); } void @@ -545,10 +566,12 @@ HangMonitorChild::ClearHangAsync() 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); @@ -614,13 +637,38 @@ HangMonitorParent::ForcePaint(dom::TabParent* aTab, uint64_t aLayerObserverEpoch 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)); + } } } @@ -718,6 +766,14 @@ HangMonitorParent::TakeBrowserMinidump(const PluginHangData& aPhd, 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) { @@ -1058,6 +1114,17 @@ HangMonitoredProcess::UserCanceled() 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) { diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index cdebe620619b..b5696ac07a3b 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -3332,6 +3332,25 @@ TabParent::LiveResizeStopped() SuppressDisplayport(false); } +void +TabParent::DispatchTabChildNotReadyEvent() +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsCOMPtr target = do_QueryInterface(mFrameElement); + if (!target) { + NS_WARNING("Could not locate target for tab child not ready event."); + return; + } + + RefPtr 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) { diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index 548ec4a32ca4..ebf36d56363c 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -599,6 +599,8 @@ public: void LiveResizeStarted() override; void LiveResizeStopped() override; + void DispatchTabChildNotReadyEvent(); + protected: bool ReceiveMessage(const nsString& aMessage, bool aSync,