Bug 938470 - Part 1: Make nsFrameLoader async in loading a remote iframe to always use Nuwa-spawned app processes. r=smaug, a=1.3+
authorCervantes Yu <cyu@mozilla.com>
Tue, 19 Nov 2013 19:03:30 +0800
changeset 175542 4ff735ca9563c4e5a6910d953c0bcad16ee5dd42
parent 175541 22408820aa27fa0c475e2815a554efdd1f9f0c6c
child 175543 dd5dae1493c42cebfe0df44f85663e41f7a66ab9
push id445
push userffxbld
push dateMon, 10 Mar 2014 22:05:19 +0000
treeherdermozilla-release@dc38b741b04e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, 1
bugs938470
milestone28.0a2
Bug 938470 - Part 1: Make nsFrameLoader async in loading a remote iframe to always use Nuwa-spawned app processes. r=smaug, a=1.3+
content/base/src/nsFrameLoader.cpp
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PreallocatedProcessManager.cpp
dom/ipc/PreallocatedProcessManager.h
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -408,30 +408,68 @@ nsFrameLoader::ReallyStartLoading()
   nsresult rv = ReallyStartLoadingInternal();
   if (NS_FAILED(rv)) {
     FireErrorEvent();
   }
   
   return rv;
 }
 
+class DelayedStartLoadingRunnable : public nsRunnable
+{
+public:
+  DelayedStartLoadingRunnable(nsFrameLoader* aFrameLoader)
+    : mFrameLoader(aFrameLoader)
+  {
+  }
+
+  NS_IMETHOD Run()
+  {
+    // Retry the request.
+    mFrameLoader->ReallyStartLoading();
+
+    // We delayed nsFrameLoader::ReallyStartLoading() after the child process is
+    // ready and might not be able to notify the remote browser in
+    // UpdatePositionAndSize() when reflow finished. Retrigger reflow.
+    nsIFrame* frame = mFrameLoader->GetPrimaryFrameOfOwningContent();
+    if (!frame) {
+      return NS_OK;
+    }
+    frame->InvalidateFrame();
+    frame->PresContext()->PresShell()->
+      FrameNeedsReflow(frame, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
+
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<nsFrameLoader> mFrameLoader;
+};
+
 nsresult
 nsFrameLoader::ReallyStartLoadingInternal()
 {
   NS_ENSURE_STATE(mURIToLoad && mOwnerContent && mOwnerContent->IsInDoc());
 
   PROFILER_LABEL("nsFrameLoader", "ReallyStartLoading");
 
   nsresult rv = MaybeCreateDocShell();
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   if (mRemoteFrame) {
     if (!mRemoteBrowser) {
+      if (Preferences::GetBool("dom.ipc.processPrelaunch.enabled", false) &&
+          !ContentParent::PreallocatedProcessReady()) {
+        ContentParent::RunAfterPreallocatedProcessReady(
+            new DelayedStartLoadingRunnable(this));
+        return NS_ERROR_FAILURE;
+      }
+
       TryRemoteBrowser();
 
       if (!mRemoteBrowser) {
         NS_WARNING("Couldn't create child process for iframe.");
         return NS_ERROR_FAILURE;
       }
     }
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -481,16 +481,34 @@ ContentParent::GetInitialProcessPriority
         return PROCESS_PRIORITY_FOREGROUND;
     }
 
     return browserFrame->GetIsExpectingSystemMessage() ?
                PROCESS_PRIORITY_FOREGROUND_HIGH :
                PROCESS_PRIORITY_FOREGROUND;
 }
 
+bool
+ContentParent::PreallocatedProcessReady()
+{
+#ifdef MOZ_NUWA_PROCESS
+    return PreallocatedProcessManager::PreallocatedProcessReady();
+#else
+    return true;
+#endif
+}
+
+void
+ContentParent::RunAfterPreallocatedProcessReady(nsIRunnable* aRequest)
+{
+#ifdef MOZ_NUWA_PROCESS
+    PreallocatedProcessManager::RunAfterPreallocatedProcessReady(aRequest);
+#endif
+}
+
 /*static*/ TabParent*
 ContentParent::CreateBrowserOrApp(const TabContext& aContext,
                                   Element* aFrameElement)
 {
     if (!sCanLaunchSubprocesses) {
         return nullptr;
     }
 
@@ -555,16 +573,24 @@ ContentParent::CreateBrowserOrApp(const 
         }
     }
 
     if (!p) {
         ChildPrivileges privs = PrivilegesForApp(ownApp);
         p = MaybeTakePreallocatedAppProcess(manifestURL, privs,
                                             initialPriority);
         if (!p) {
+#ifdef MOZ_NUWA_PROCESS
+            if (Preferences::GetBool("dom.ipc.processPrelaunch.enabled",
+                                     false)) {
+                // Returning nullptr from here so the frame loader will retry
+                // later when we have a spare process.
+                return nullptr;
+            }
+#endif
             NS_WARNING("Unable to use pre-allocated app process");
             p = new ContentParent(ownApp,
                                   /* isForBrowserElement = */ false,
                                   /* isForPreallocated = */ false,
                                   privs,
                                   initialPriority);
             p->Init();
         }
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -79,16 +79,19 @@ public:
     /**
      * Ensure that all subprocesses are terminated and their OS
      * resources have been reaped.  This is synchronous and can be
      * very expensive in general.  It also bypasses the normal
      * shutdown process.
      */
     static void JoinAllSubprocesses();
 
+    static bool PreallocatedProcessReady();
+    static void RunAfterPreallocatedProcessReady(nsIRunnable* aRequest);
+
     static already_AddRefed<ContentParent>
     GetNewOrUsed(bool aForBrowserElement = false);
 
     /**
      * Create a subprocess suitable for use as a preallocated app process.
      */
     static already_AddRefed<ContentParent> PreallocateAppProcess();
 
--- a/dom/ipc/PreallocatedProcessManager.cpp
+++ b/dom/ipc/PreallocatedProcessManager.cpp
@@ -50,30 +50,34 @@ public:
 
 #ifdef MOZ_NUWA_PROCESS
 public:
   void ScheduleDelayedNuwaFork();
   void DelayedNuwaFork();
   void PublishSpareProcess(ContentParent* aContent);
   void MaybeForgetSpare(ContentParent* aContent);
   void OnNuwaReady();
+  bool PreallocatedProcessReady();
   already_AddRefed<ContentParent> GetSpareProcess();
+  void RunAfterPreallocatedProcessReady(nsIRunnable* aRunnable);
 
 private:
   void OnNuwaForkTimeout();
   void NuwaFork();
 
   // initialization off the critical path of app startup.
   CancelableTask* mPreallocateAppProcessTask;
 
   // The array containing the preallocated processes. 4 as the inline storage size
   // should be enough so we don't need to grow the nsAutoTArray.
   nsAutoTArray<nsRefPtr<ContentParent>, 4> mSpareProcesses;
   nsTArray<CancelableTask*> mNuwaForkWaitTasks;
 
+  nsTArray<nsCOMPtr<nsIRunnable> > mDelayedContentParentRequests;
+
   // Nuwa process is ready for creating new process.
   bool mIsNuwaReady;
 #endif
 
 private:
   static mozilla::StaticRefPtr<PreallocatedProcessManagerImpl> sSingleton;
 
   PreallocatedProcessManagerImpl();
@@ -216,16 +220,28 @@ PreallocatedProcessManagerImpl::Allocate
   }
 
   mPreallocatedAppProcess = ContentParent::PreallocateAppProcess();
 }
 
 #ifdef MOZ_NUWA_PROCESS
 
 void
+PreallocatedProcessManagerImpl::RunAfterPreallocatedProcessReady(nsIRunnable* aRequest)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  mDelayedContentParentRequests.AppendElement(aRequest);
+
+  if (!mPreallocateAppProcessTask) {
+    // This is an urgent NuwaFork() request.
+    DelayedNuwaFork();
+  }
+}
+
+void
 PreallocatedProcessManagerImpl::ScheduleDelayedNuwaFork()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (mPreallocateAppProcessTask) {
     // Make sure there is only one request running.
     return;
   }
@@ -297,23 +313,36 @@ PreallocatedProcessManagerImpl::PublishS
   }
 
   if (!mNuwaForkWaitTasks.IsEmpty()) {
     mNuwaForkWaitTasks.ElementAt(0)->Cancel();
     mNuwaForkWaitTasks.RemoveElementAt(0);
   }
 
   mSpareProcesses.AppendElement(aContent);
+
+  if (!mDelayedContentParentRequests.IsEmpty()) {
+    nsCOMPtr<nsIRunnable> runnable = mDelayedContentParentRequests[0];
+    mDelayedContentParentRequests.RemoveElementAt(0);
+    NS_DispatchToMainThread(runnable);
+  }
 }
 
 void
 PreallocatedProcessManagerImpl::MaybeForgetSpare(ContentParent* aContent)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
+  if (!mDelayedContentParentRequests.IsEmpty()) {
+    if (!mPreallocateAppProcessTask) {
+      // This NuwaFork request is urgent. Don't delay it.
+      DelayedNuwaFork();
+    }
+  }
+
   if (mSpareProcesses.RemoveElement(aContent)) {
     return;
   }
 
   if (aContent == mPreallocatedAppProcess) {
     mPreallocatedAppProcess = nullptr;
     mIsNuwaReady = false;
     ScheduleDelayedNuwaFork();
@@ -333,16 +362,23 @@ PreallocatedProcessManagerImpl::OnNuwaRe
       do_GetService("@mozilla.org/parentprocessmessagemanager;1");
     nsresult rv = ppmm->BroadcastAsyncMessage(
       NS_LITERAL_STRING("TEST-ONLY:nuwa-ready"),
       JSVAL_NULL, JSVAL_NULL, cx, 1);
   }
   NuwaFork();
 }
 
+bool
+PreallocatedProcessManagerImpl::PreallocatedProcessReady()
+{
+  return !mSpareProcesses.IsEmpty();
+}
+
+
 void
 PreallocatedProcessManagerImpl::OnNuwaForkTimeout()
 {
   if (!mNuwaForkWaitTasks.IsEmpty()) {
     mNuwaForkWaitTasks.RemoveElementAt(0);
   }
 
   // We haven't RecvAddNewProcess() after NuwaFork(). Maybe the main
@@ -468,11 +504,24 @@ PreallocatedProcessManager::MaybeForgetS
   GetPPMImpl()->MaybeForgetSpare(aContent);
 }
 
 /* static */ void
 PreallocatedProcessManager::OnNuwaReady()
 {
   GetPPMImpl()->OnNuwaReady();
 }
+
+/*static */ bool
+PreallocatedProcessManager::PreallocatedProcessReady()
+{
+  return GetPPMImpl()->PreallocatedProcessReady();
+}
+
+/* static */ void
+PreallocatedProcessManager::RunAfterPreallocatedProcessReady(nsIRunnable* aRequest)
+{
+  GetPPMImpl()->RunAfterPreallocatedProcessReady(aRequest);
+}
+
 #endif
 
 } // namespace mozilla
--- a/dom/ipc/PreallocatedProcessManager.h
+++ b/dom/ipc/PreallocatedProcessManager.h
@@ -6,16 +6,18 @@
 
 #ifndef mozilla_PreallocatedProcessManager_h
 #define mozilla_PreallocatedProcessManager_h
 
 #include "base/basictypes.h"
 #include "nsCOMPtr.h"
 #include "nsIObserver.h"
 
+class nsIRunnable;
+
 namespace mozilla {
 namespace dom {
 class ContentParent;
 }
 
 /**
  * This class manages a ContentParent that it starts up ahead of any particular
  * need.  You can then call Take() to get this process and use it.  Since we
@@ -77,16 +79,18 @@ public:
    * false to true) before we'll create a new process.
    */
   static already_AddRefed<ContentParent> Take();
 
 #ifdef MOZ_NUWA_PROCESS
   static void PublishSpareProcess(ContentParent* aContent);
   static void MaybeForgetSpare(ContentParent* aContent);
   static void OnNuwaReady();
+  static bool PreallocatedProcessReady();
+  static void RunAfterPreallocatedProcessReady(nsIRunnable* aRunnable);
 #endif
 
 private:
   PreallocatedProcessManager();
   DISALLOW_EVIL_CONSTRUCTORS(PreallocatedProcessManager);
 };
 
 } // namespace mozilla