Bug 1598775 - Ensure postMessage related optimizations during page load work also in Fission, r=farre
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Sat, 30 Nov 2019 21:13:54 +0000
changeset 566632 1c9bf0994ad0c291f1c92990aab97c81ed39185c
parent 566631 75c98623cd48fb023d9ce6fd4a35a8dbd951b958
child 566633 76d464c781b2ba2f322f5e061d12d8ce9bce71e8
push id12351
push userffxbld-merge
push dateMon, 02 Dec 2019 11:32:26 +0000
treeherdermozilla-beta@dba4410526a2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfarre
bugs1598775, 1534012
milestone72.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 1598775 - Ensure postMessage related optimizations during page load work also in Fission, r=farre Bug 1534012 added a test for postMessage during load and that revealed that mLoading flag isn't always updated soon enough, so add BrowsingContext::IsLoading which checks also possible local loading status. Depends on D54754 Differential Revision: https://phabricator.services.mozilla.com/D54836
docshell/base/BrowsingContext.cpp
docshell/base/BrowsingContext.h
docshell/base/BrowsingContextGroup.cpp
docshell/base/BrowsingContextGroup.h
dom/base/TabGroup.cpp
dom/base/TabGroup.h
dom/base/nsGlobalWindowInner.cpp
dom/base/nsGlobalWindowOuter.cpp
dom/fetch/Fetch.cpp
dom/xhr/XMLHttpRequestMainThread.cpp
--- a/docshell/base/BrowsingContext.cpp
+++ b/docshell/base/BrowsingContext.cpp
@@ -8,16 +8,17 @@
 
 #include "ipc/IPCMessageUtils.h"
 
 #include "mozilla/dom/CanonicalBrowsingContext.h"
 #include "mozilla/dom/BrowsingContextGroup.h"
 #include "mozilla/dom/BrowsingContextBinding.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/Document.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/Location.h"
 #include "mozilla/dom/LocationBinding.h"
 #include "mozilla/dom/PopupBlocker.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/StructuredCloneTags.h"
 #include "mozilla/dom/UserActivationIPCUtils.h"
 #include "mozilla/dom/WindowBinding.h"
@@ -1344,27 +1345,50 @@ bool BrowsingContext::MaySetIsPopupSpam(
 }
 
 void BrowsingContext::DidSetIsPopupSpam() {
   if (mIsPopupSpam) {
     PopupBlocker::RegisterOpenPopupSpam();
   }
 }
 
+bool BrowsingContext::IsLoading() {
+  if (GetLoading()) {
+    return true;
+  }
+
+  // If we're in the same process as the page, we're possibly just
+  // updating the flag.
+  nsIDocShell* shell = GetDocShell();
+  if (shell) {
+    Document* doc = shell->GetDocument();
+    return doc && doc->GetReadyStateEnum() < Document::READYSTATE_COMPLETE;
+  }
+
+  return false;
+}
+
 void BrowsingContext::DidSetLoading() {
-  if (!mLoading) {
-    while (!mDeprioritizedLoadRunner.isEmpty()) {
-      nsCOMPtr<nsIRunnable> runner = mDeprioritizedLoadRunner.popFirst();
-      NS_DispatchToCurrentThread(runner.forget());
-    }
+  if (mLoading) {
+    return;
+  }
+
+  while (!mDeprioritizedLoadRunner.isEmpty()) {
+    nsCOMPtr<nsIRunnable> runner = mDeprioritizedLoadRunner.popFirst();
+    NS_DispatchToCurrentThread(runner.forget());
+  }
+
+  if (StaticPrefs::dom_separate_event_queue_for_post_message_enabled() &&
+      Top() == this) {
+    Group()->FlushPostMessageEvents();
   }
 }
 
 void BrowsingContext::AddDeprioritizedLoadRunner(nsIRunnable* aRunner) {
-  MOZ_ASSERT(mLoading);
+  MOZ_ASSERT(IsLoading());
   MOZ_ASSERT(Top() == this);
 
   RefPtr<DeprioritizedLoadRunner> runner = new DeprioritizedLoadRunner(aRunner);
   mDeprioritizedLoadRunner.insertBack(runner);
   NS_DispatchToCurrentThreadQueue(
       runner.forget(), StaticPrefs::page_load_deprioritization_period(),
       EventQueuePriority::Idle);
 }
--- a/docshell/base/BrowsingContext.h
+++ b/docshell/base/BrowsingContext.h
@@ -267,16 +267,18 @@ class BrowsingContext : public nsISuppor
   void GetChildren(Children& aChildren);
 
   BrowsingContextGroup* Group() { return mGroup; }
 
   uint32_t SandboxFlags() { return mSandboxFlags; }
 
   bool InRDMPane() { return mInRDMPane; }
 
+  bool IsLoading();
+
   // Using the rules for choosing a browsing context we try to find
   // the browsing context with the given name in the set of
   // transitively reachable browsing contexts. Performs access control
   // checks with regard to this.
   // See
   // https://html.spec.whatwg.org/multipage/browsers.html#the-rules-for-choosing-a-browsing-context-given-a-browsing-context-name.
   //
   // BrowsingContext::FindWithName(const nsAString&) is equivalent to
--- a/docshell/base/BrowsingContextGroup.cpp
+++ b/docshell/base/BrowsingContextGroup.cpp
@@ -2,16 +2,18 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/BrowsingContextGroup.h"
 #include "mozilla/dom/BrowsingContextBinding.h"
 #include "mozilla/dom/BindingUtils.h"
+#include "mozilla/StaticPrefs_dom.h"
+#include "mozilla/ThrottledEventQueue.h"
 
 namespace mozilla {
 namespace dom {
 
 BrowsingContextGroup::BrowsingContextGroup() {
   if (XRE_IsContentProcess()) {
     ContentChild::GetSingleton()->HoldBrowsingContextGroup(this);
   }
@@ -128,16 +130,56 @@ nsISupports* BrowsingContextGroup::GetPa
   return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
 }
 
 JSObject* BrowsingContextGroup::WrapObject(JSContext* aCx,
                                            JS::Handle<JSObject*> aGivenProto) {
   return BrowsingContextGroup_Binding::Wrap(aCx, this, aGivenProto);
 }
 
+nsresult BrowsingContextGroup::QueuePostMessageEvent(
+    already_AddRefed<nsIRunnable>&& aRunnable) {
+  if (StaticPrefs::dom_separate_event_queue_for_post_message_enabled()) {
+    if (!mPostMessageEventQueue) {
+      nsCOMPtr<nsISerialEventTarget> target = GetMainThreadSerialEventTarget();
+      mPostMessageEventQueue = ThrottledEventQueue::Create(
+          target, "PostMessage Queue",
+          nsIRunnablePriority::PRIORITY_DEFERRED_TIMERS);
+      nsresult rv = mPostMessageEventQueue->SetIsPaused(false);
+      MOZ_ALWAYS_SUCCEEDS(rv);
+    }
+
+    // Ensure the queue is enabled
+    if (mPostMessageEventQueue->IsPaused()) {
+      nsresult rv = mPostMessageEventQueue->SetIsPaused(false);
+      MOZ_ALWAYS_SUCCEEDS(rv);
+    }
+
+    if (mPostMessageEventQueue) {
+      mPostMessageEventQueue->Dispatch(std::move(aRunnable),
+                                       NS_DISPATCH_NORMAL);
+      return NS_OK;
+    }
+  }
+  return NS_ERROR_FAILURE;
+}
+
+void BrowsingContextGroup::FlushPostMessageEvents() {
+  if (StaticPrefs::dom_separate_event_queue_for_post_message_enabled()) {
+    if (mPostMessageEventQueue) {
+      nsresult rv = mPostMessageEventQueue->SetIsPaused(true);
+      MOZ_ALWAYS_SUCCEEDS(rv);
+      nsCOMPtr<nsIRunnable> event;
+      while ((event = mPostMessageEventQueue->GetEvent())) {
+        NS_DispatchToMainThread(event.forget());
+      }
+    }
+  }
+}
+
 static StaticRefPtr<BrowsingContextGroup> sChromeGroup;
 
 /* static */
 BrowsingContextGroup* BrowsingContextGroup::GetChromeGroup() {
   MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
   if (!sChromeGroup && XRE_IsParentProcess()) {
     sChromeGroup = new BrowsingContextGroup();
     ClearOnShutdown(&sChromeGroup);
--- a/docshell/base/BrowsingContextGroup.h
+++ b/docshell/base/BrowsingContextGroup.h
@@ -10,16 +10,18 @@
 #include "mozilla/dom/BrowsingContext.h"
 #include "mozilla/dom/ContentParent.h"
 #include "nsHashKeys.h"
 #include "nsTArray.h"
 #include "nsTHashtable.h"
 #include "nsWrapperCache.h"
 
 namespace mozilla {
+class ThrottledEventQueue;
+
 namespace dom {
 
 class BrowsingContext;
 
 // A BrowsingContextGroup represents the Unit of Related Browsing Contexts in
 // the standard. This object currently serves roughly the same purpose as the
 // TabGroup class which already exists, and at some point will likely merge with
 // it.
@@ -103,16 +105,20 @@ class BrowsingContextGroup final : publi
   template <typename Func>
   void EachParent(Func&& aCallback) {
     MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
     for (auto iter = mSubscribers.Iter(); !iter.Done(); iter.Next()) {
       aCallback(iter.Get()->GetKey());
     }
   }
 
+  nsresult QueuePostMessageEvent(already_AddRefed<nsIRunnable>&& aRunnable);
+
+  void FlushPostMessageEvents();
+
   static BrowsingContextGroup* GetChromeGroup();
 
  private:
   friend class CanonicalBrowsingContext;
 
   ~BrowsingContextGroup();
 
   void UnsubscribeAllContentParents();
@@ -124,14 +130,18 @@ class BrowsingContextGroup final : publi
 
   // The set of toplevel browsing contexts in the current BrowsingContextGroup.
   BrowsingContext::Children mToplevels;
 
   ContentParents mSubscribers;
 
   // Map of cached contexts that need to stay alive due to bfcache.
   nsTHashtable<nsRefPtrHashKey<BrowsingContext>> mCachedContexts;
+
+  // A queue to store postMessage events during page load, the queue will be
+  // flushed once the page is loaded
+  RefPtr<mozilla::ThrottledEventQueue> mPostMessageEventQueue;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // !defined(mozilla_dom_BrowsingContextGroup_h)
--- a/dom/base/TabGroup.cpp
+++ b/dom/base/TabGroup.cpp
@@ -250,56 +250,16 @@ bool TabGroup::IsBackground() const {
     }
   }
   MOZ_ASSERT(foregrounded == mForegroundCount);
 #endif
 
   return mForegroundCount == 0;
 }
 
-nsresult TabGroup::QueuePostMessageEvent(
-    already_AddRefed<nsIRunnable>&& aRunnable) {
-  if (StaticPrefs::dom_separate_event_queue_for_post_message_enabled()) {
-    if (!mPostMessageEventQueue) {
-      nsCOMPtr<nsISerialEventTarget> target = GetMainThreadSerialEventTarget();
-      mPostMessageEventQueue = ThrottledEventQueue::Create(
-          target, "PostMessage Queue",
-          nsIRunnablePriority::PRIORITY_DEFERRED_TIMERS);
-      nsresult rv = mPostMessageEventQueue->SetIsPaused(false);
-      MOZ_ALWAYS_SUCCEEDS(rv);
-    }
-
-    // Ensure the queue is enabled
-    if (mPostMessageEventQueue->IsPaused()) {
-      nsresult rv = mPostMessageEventQueue->SetIsPaused(false);
-      MOZ_ALWAYS_SUCCEEDS(rv);
-    }
-
-    if (mPostMessageEventQueue) {
-      mPostMessageEventQueue->Dispatch(std::move(aRunnable),
-                                       NS_DISPATCH_NORMAL);
-      return NS_OK;
-    }
-  }
-  return NS_ERROR_FAILURE;
-}
-
-void TabGroup::FlushPostMessageEvents() {
-  if (StaticPrefs::dom_separate_event_queue_for_post_message_enabled()) {
-    if (mPostMessageEventQueue) {
-      nsresult rv = mPostMessageEventQueue->SetIsPaused(true);
-      MOZ_ALWAYS_SUCCEEDS(rv);
-      nsCOMPtr<nsIRunnable> event;
-      while ((event = mPostMessageEventQueue->GetEvent())) {
-        Dispatch(TaskCategory::Other, event.forget());
-      }
-    }
-  }
-}
-
 uint32_t TabGroup::Count(bool aActiveOnly) const {
   if (!aActiveOnly) {
     return mDocGroups.Count();
   }
 
   uint32_t count = 0;
   for (auto iter = mDocGroups.ConstIter(); !iter.Done(); iter.Next()) {
     if (iter.Get()->mDocGroup->IsActive()) {
--- a/dom/base/TabGroup.h
+++ b/dom/base/TabGroup.h
@@ -125,20 +125,16 @@ class TabGroup final : public SchedulerG
 
   static LinkedList<TabGroup>* GetTabGroupList() { return sTabGroups; }
 
   // This returns true if all the window objects in all the TabGroups are
   // either inactive (for example in bfcache) or are in background tabs which
   // can be throttled.
   static bool HasOnlyThrottableTabs();
 
-  nsresult QueuePostMessageEvent(already_AddRefed<nsIRunnable>&& aRunnable);
-
-  void FlushPostMessageEvents();
-
  private:
   virtual AbstractThread* AbstractMainThreadForImpl(
       TaskCategory aCategory) override;
 
   TabGroup* AsTabGroup() override { return this; }
 
   void EnsureThrottledEventQueues();
 
@@ -152,18 +148,14 @@ class TabGroup final : public SchedulerG
   const bool mIsChrome;
 
   // Main thread only
   DocGroupMap mDocGroups;
   nsTArray<nsPIDOMWindowOuter*> mWindows;
   uint32_t mForegroundCount;
 
   static LinkedList<TabGroup>* sTabGroups;
-
-  // A queue to store postMessage events during page load, the queue will be
-  // flushed once the page is loaded
-  RefPtr<mozilla::ThrottledEventQueue> mPostMessageEventQueue;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // defined(TabGroup_h)
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -2559,28 +2559,16 @@ void nsPIDOMWindowInner::SetAudioCapture
   }
 }
 
 void nsGlobalWindowInner::SetActiveLoadingState(bool aIsLoading) {
   if (GetBrowsingContext()) {
     GetBrowsingContext()->SetLoading(aIsLoading);
   }
 
-  if (StaticPrefs::dom_separate_event_queue_for_post_message_enabled()) {
-    if (!aIsLoading) {
-      Document* doc = GetExtantDoc();
-      if (doc) {
-        if (doc->IsTopLevelContentDocument()) {
-          mozilla::dom::TabGroup* tabGroup = doc->GetDocGroup()->GetTabGroup();
-          tabGroup->FlushPostMessageEvents();
-        }
-      }
-    }
-  }
-
   if (!nsGlobalWindowInner::Cast(this)->IsChromeWindow()) {
     mTimeoutManager->SetLoading(aIsLoading);
   }
 }
 
 // nsISpeechSynthesisGetter
 
 #ifdef MOZ_WEBSPEECH
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -214,16 +214,17 @@
 #include "nsWrapperCacheInlines.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "prrng.h"
 #include "nsSandboxFlags.h"
 #include "nsBaseCommandController.h"
 #include "nsXULControllers.h"
 #include "mozilla/dom/AudioContext.h"
 #include "mozilla/dom/BrowserElementDictionariesBinding.h"
+#include "mozilla/dom/BrowsingContextGroup.h"
 #include "mozilla/dom/cache/CacheStorage.h"
 #include "mozilla/dom/Console.h"
 #include "mozilla/dom/Fetch.h"
 #include "mozilla/dom/FunctionBinding.h"
 #include "mozilla/dom/HashChangeEvent.h"
 #include "mozilla/dom/IntlUtils.h"
 #include "mozilla/dom/PopStateEvent.h"
 #include "mozilla/dom/PopupBlockedEvent.h"
@@ -6071,43 +6072,43 @@ void nsGlobalWindowOuter::PostMessageMoz
       callerInnerWindow->IsSharedMemoryAllowed()) {
     clonePolicy.allowSharedMemory();
   }
   event->Write(aCx, aMessage, aTransfer, clonePolicy, aError);
   if (NS_WARN_IF(aError.Failed())) {
     return;
   }
 
-  if (mDoc &&
-      StaticPrefs::dom_separate_event_queue_for_post_message_enabled() &&
+  if (StaticPrefs::dom_separate_event_queue_for_post_message_enabled() &&
       !DocGroup::TryToLoadIframesInBackground()) {
-    Document* doc = mDoc->GetTopLevelContentDocument();
-    if (doc && doc->GetReadyStateEnum() < Document::READYSTATE_COMPLETE) {
+    BrowsingContext* bc = GetBrowsingContext();
+    bc = bc ? bc->Top() : nullptr;
+    if (bc && bc->IsLoading()) {
       // As long as the top level is loading, we can dispatch events to the
       // queue because the queue will be flushed eventually
-      mozilla::dom::TabGroup* tabGroup = TabGroup();
-      aError = tabGroup->QueuePostMessageEvent(event.forget());
+      aError = bc->Group()->QueuePostMessageEvent(event.forget());
       return;
     }
   }
 
-  if (mDoc && DocGroup::TryToLoadIframesInBackground()) {
+  if (DocGroup::TryToLoadIframesInBackground()) {
     RefPtr<nsIDocShell> docShell = GetDocShell();
     RefPtr<nsDocShell> dShell = nsDocShell::Cast(docShell);
 
-    // PostMessage that are added to the tabGroup are the ones that
-    // can be flushed when the top level document is loaded
+    // PostMessage that are added to the BrowsingContextGroup are the ones that
+    // can be flushed when the top level document is loaded.
+    // TreadAsBackgroundLoad DocShells are treated specially.
     if (dShell) {
       if (!dShell->TreatAsBackgroundLoad()) {
-        Document* doc = mDoc->GetTopLevelContentDocument();
-        if (doc && doc->GetReadyStateEnum() < Document::READYSTATE_COMPLETE) {
+        BrowsingContext* bc = GetBrowsingContext();
+        bc = bc ? bc->Top() : nullptr;
+        if (bc && bc->IsLoading()) {
           // As long as the top level is loading, we can dispatch events to the
           // queue because the queue will be flushed eventually
-          mozilla::dom::TabGroup* tabGroup = TabGroup();
-          aError = tabGroup->QueuePostMessageEvent(event.forget());
+          aError = bc->Group()->QueuePostMessageEvent(event.forget());
           return;
         }
       } else if (mDoc->GetReadyStateEnum() < Document::READYSTATE_COMPLETE) {
         mozilla::dom::DocGroup* docGroup = GetDocGroup();
         aError = docGroup->QueueIframePostMessages(event.forget(),
                                                    dShell->GetOuterWindowID());
         return;
       }
--- a/dom/fetch/Fetch.cpp
+++ b/dom/fetch/Fetch.cpp
@@ -580,17 +580,17 @@ void MainThreadFetchResolver::OnResponse
       mFetchObserver->SetState(FetchState::Complete);
     }
 
     nsCOMPtr<nsIGlobalObject> go = mPromise->GetParentObject();
     mResponse = new Response(go, aResponse, mSignalImpl);
     nsCOMPtr<nsPIDOMWindowInner> inner = do_QueryInterface(go);
     BrowsingContext* bc = inner ? inner->GetBrowsingContext() : nullptr;
     bc = bc ? bc->Top() : nullptr;
-    if (bc && bc->GetLoading()) {
+    if (bc && bc->IsLoading()) {
       bc->AddDeprioritizedLoadRunner(
           new ResolveFetchPromise(mPromise, mResponse));
     } else {
       mPromise->MaybeResolve(mResponse);
     }
   } else {
     if (mFetchObserver) {
       mFetchObserver->SetState(FetchState::Errored);
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -2305,17 +2305,17 @@ void XMLHttpRequestMainThread::ChangeSta
     // If the top level page is loading, try to postpone the handling of the
     // final events.
     nsLoadFlags loadFlags = 0;
     mChannel->GetLoadFlags(&loadFlags);
     if (loadFlags & nsIRequest::LOAD_BACKGROUND) {
       nsPIDOMWindowInner* owner = GetOwner();
       BrowsingContext* bc = owner ? owner->GetBrowsingContext() : nullptr;
       bc = bc ? bc->Top() : nullptr;
-      if (bc && bc->GetLoading()) {
+      if (bc && bc->IsLoading()) {
         MOZ_ASSERT(!mDelayedDoneNotifier);
         RefPtr<XMLHttpRequestDoneNotifier> notifier =
             new XMLHttpRequestDoneNotifier(this);
         mDelayedDoneNotifier = notifier;
         bc->AddDeprioritizedLoadRunner(notifier);
         return;
       }
     }