Bug 1342927 - Don't fire MozTabChildNotReady from TabParent if we've already presented. r?billm draft
authorMike Conley <mconley@mozilla.com>
Sat, 04 Mar 2017 14:12:38 -0500
changeset 494170 92536597298844d18f16541341030afe3c790a0f
parent 493711 5271f4ecec4ad889051007272541318e91db0186
child 494171 cecd4cacfafc4b3f2090048250748938ba8a0be3
child 494221 9c87296aae382c6813d32358aa59b82531a403cf
push id47944
push usermconley@mozilla.com
push dateMon, 06 Mar 2017 18:57:58 +0000
reviewersbillm
bugs1342927
milestone54.0a1
Bug 1342927 - Don't fire MozTabChildNotReady from TabParent if we've already presented. r?billm The MozTabChildNotReady event is queued from the ProcessHangMonitor thread, and that event might try to fire _after_ the main thread has received notification that a layer tree was made ready. If that occurs, firing the MozTabChildNotReady event makes no sense, as clearly the TabChild _became_ ready by the time the event was about to be dispatched. MozReview-Commit-ID: Iigtc0SqzeU
browser/base/content/tabbrowser.xml
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -4296,35 +4296,26 @@
 
             // The tab for this browser isn't currently set
             // up in the content process, so we have no chance
             // of painting it right away. We'll paint a blank
             // tab instead.
             onTabChildNotReady(browser) {
               let tab = this.tabbrowser.getTabForBrowser(browser);
 
-              let state = this.getTabState(tab);
-              this.assert(state == this.STATE_LOADING ||
-                          state == this.STATE_LOADED);
-
-              // Because the TabChildNotReady event is queued from
-              // off of the main thread, it's possible that while
-              // it was being queued, the layer tree became ready
-              // and the state changed to STATE_LOADED. In that
-              // case, there's nothing to do here.
-              if (state == this.STATE_LOADING) {
-                this.logState(`onTabChildNotReady(${tab._tPos})`);
-                this.pendingTabChild.add(browser);
-                this.maybeFinishTabSwitch();
-
-                if (this.loadingTab === tab) {
-                  this.clearTimer(this.loadTimer);
-                  this.loadTimer = null;
-                  this.loadingTab = null;
-                }
+              this.assert(this.getTabState(tab) == this.STATE_LOADING);
+
+              this.logState(`onTabChildNotReady(${tab._tPos})`);
+              this.pendingTabChild.add(browser);
+              this.maybeFinishTabSwitch();
+
+              if (this.loadingTab === tab) {
+                this.clearTimer(this.loadTimer);
+                this.loadTimer = null;
+                this.loadingTab = null;
               }
             },
 
             // Called when the user asks to switch to a given tab.
             requestTab(tab) {
               if (tab === this.requestedTab) {
                 return;
               }
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -162,16 +162,17 @@ TabParent::TabParent(nsIContentParent* a
   , mCursor(nsCursor(-1))
   , mTabSetsCursor(false)
   , mHasContentOpener(false)
 #ifdef DEBUG
   , mActiveSupressDisplayportCount(0)
 #endif
   , mLayerTreeEpoch(0)
   , mPreserveLayers(false)
+  , mHasPresented(false)
 {
   MOZ_ASSERT(aManager);
 }
 
 TabParent::~TabParent()
 {
 }
 
@@ -2848,16 +2849,17 @@ 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) {
+    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;
   mFrameElement->DispatchEvent(event, &dummy);
@@ -3338,16 +3340,23 @@ 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;
   }
 
+  if (mHasPresented) {
+    // We shouldn't dispatch this event because clearly the
+    // TabChild _became_ ready by the time we were told to
+    // dispatch.
+    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);
 }
 
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -775,16 +775,20 @@ 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
+  // at least once.
+  bool mHasPresented;
+
 public:
   static TabParent* GetTabParentFromLayersId(uint64_t aLayersId);
 };
 
 struct MOZ_STACK_CLASS TabParent::AutoUseNewTab final
 {
 public:
   AutoUseNewTab(TabParent* aNewTab, bool* aWindowIsNew, nsCString* aURLToLoad)