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
--- 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);