Bug 1342927 - Fire a MozTabChildNotReady event on a frameloader if force-painting a tab without a TabChild. r?billm
MozReview-Commit-ID: D8vgvQ3MLJN
--- 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);