Bug 1020204 - Allow creating nested oop MozApp iframe. r=khuey, r=kanru
authorKershaw Chang <kechang@mozilla.com>
Wed, 13 Aug 2014 02:18:00 -0400
changeset 222440 15eb43f20248bccffee7a125478864a02cfd13a5
parent 222439 5e56b47fbaba8301d19974f5bd463456c73e58e0
child 222441 1db4be77a40971b5387ff6cb6b130cb02530dcaf
push id583
push userbhearsum@mozilla.com
push dateMon, 24 Nov 2014 19:04:58 +0000
treeherdermozilla-release@c107e74250f4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey, kanru
bugs1020204
milestone34.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 1020204 - Allow creating nested oop MozApp iframe. r=khuey, r=kanru
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
toolkit/xre/nsAppRunner.cpp
toolkit/xre/nsEmbedFunctions.cpp
old mode 100644
new mode 100755
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -570,46 +570,72 @@ ContentParent::RunNuwaProcess()
                           PROCESS_PRIORITY_BACKGROUND,
                           /* aIsNuwaProcess = */ true);
     nuwaProcess->Init();
     return nuwaProcess.forget();
 }
 
 // PreallocateAppProcess is called by the PreallocatedProcessManager.
 // ContentParent then takes this process back within
-// MaybeTakePreallocatedAppProcess.
+// GetNewOrPreallocatedAppProcess.
 /*static*/ already_AddRefed<ContentParent>
 ContentParent::PreallocateAppProcess()
 {
     nsRefPtr<ContentParent> process =
         new ContentParent(/* app = */ nullptr,
                           /* aOpener = */ nullptr,
                           /* isForBrowserElement = */ false,
                           /* isForPreallocated = */ true,
                           PROCESS_PRIORITY_PREALLOC);
     process->Init();
     return process.forget();
 }
 
 /*static*/ already_AddRefed<ContentParent>
-ContentParent::MaybeTakePreallocatedAppProcess(const nsAString& aAppManifestURL,
-                                               ProcessPriority aInitialPriority)
+ContentParent::GetNewOrPreallocatedAppProcess(mozIApplication* aApp,
+                                              ProcessPriority aInitialPriority,
+                                              ContentParent* aOpener,
+                                              /*out*/ bool* aTookPreAllocated)
 {
+    MOZ_ASSERT(aApp);
     nsRefPtr<ContentParent> process = PreallocatedProcessManager::Take();
-    if (!process) {
-        return nullptr;
+
+    if (process) {
+        if (!process->SetPriorityAndCheckIsAlive(aInitialPriority)) {
+            // Kill the process just in case it's not actually dead; we don't want
+            // to "leak" this process!
+            process->KillHard();
+        }
+        else {
+            nsAutoString manifestURL;
+            if (NS_FAILED(aApp->GetManifestURL(manifestURL))) {
+                NS_ERROR("Failed to get manifest URL");
+                return nullptr;
+            }
+            process->TransformPreallocatedIntoApp(manifestURL);
+            if (aTookPreAllocated) {
+                *aTookPreAllocated = true;
+            }
+            return process.forget();
+        }
     }
 
-    if (!process->SetPriorityAndCheckIsAlive(aInitialPriority)) {
-        // Kill the process just in case it's not actually dead; we don't want
-        // to "leak" this process!
-        process->KillHard();
-        return nullptr;
+    // XXXkhuey Nuwa wants the frame loader to try again later, but the
+    // frame loader is really not set up to do that ...
+    NS_WARNING("Unable to use pre-allocated app process");
+    process = new ContentParent(aApp,
+                                /* aOpener = */ aOpener,
+                                /* isForBrowserElement = */ false,
+                                /* isForPreallocated = */ false,
+                                aInitialPriority);
+    process->Init();
+
+    if (aTookPreAllocated) {
+        *aTookPreAllocated = false;
     }
-    process->TransformPreallocatedIntoApp(aAppManifestURL);
 
     return process.forget();
 }
 
 /*static*/ void
 ContentParent::StartUp()
 {
     // We could launch sub processes from content process
@@ -688,19 +714,19 @@ ContentParent::JoinAllSubprocesses()
             lock.Wait();
         }
     }
 
     sCanLaunchSubprocesses = false;
 }
 
 /*static*/ already_AddRefed<ContentParent>
-ContentParent::GetNewOrUsed(bool aForBrowserElement,
-                            ProcessPriority aPriority,
-                            ContentParent* aOpener)
+ContentParent::GetNewOrUsedBrowserProcess(bool aForBrowserElement,
+                                          ProcessPriority aPriority,
+                                          ContentParent* aOpener)
 {
     if (!sNonAppContentParents)
         sNonAppContentParents = new nsTArray<ContentParent*>();
 
     int32_t maxContentProcesses = Preferences::GetInt("dom.ipc.processCount", 1);
     if (maxContentProcesses < 1)
         maxContentProcesses = 1;
 
@@ -800,20 +826,41 @@ ContentParent::RecvCreateChildProcess(co
                                       bool* aIsForApp,
                                       bool* aIsForBrowser)
 {
 #if 0
     if (!CanOpenBrowser(aContext)) {
         return false;
     }
 #endif
-
-    nsRefPtr<ContentParent> cp = GetNewOrUsed(/* isBrowserElement = */ true,
-                                              aPriority,
-                                              this);
+    nsRefPtr<ContentParent> cp;
+    MaybeInvalidTabContext tc(aContext);
+    if (!tc.IsValid()) {
+        NS_ERROR(nsPrintfCString("Received an invalid TabContext from "
+                                 "the child process. (%s)",
+                                 tc.GetInvalidReason()).get());
+        return false;
+    }
+
+    nsCOMPtr<mozIApplication> ownApp = tc.GetTabContext().GetOwnApp();
+    if (ownApp) {
+        cp = GetNewOrPreallocatedAppProcess(ownApp,
+                                            aPriority,
+                                            this);
+    }
+    else {
+        cp = GetNewOrUsedBrowserProcess(/* isBrowserElement = */ true,
+                                        aPriority,
+                                        this);
+    }
+
+    if (!cp) {
+        return false;
+    }
+
     *aId = cp->ChildID();
     *aIsForApp = cp->IsForApp();
     *aIsForBrowser = cp->IsForBrowser();
     sContentParentMap[*aId] = cp;
     auto iter = sGrandchildProcessMap.find(this);
     if (iter == sGrandchildProcessMap.end()) {
         std::set<ContentParent*> children;
         children.insert(cp);
@@ -844,46 +891,30 @@ ContentParent::CreateBrowserOrApp(const 
                                   Element* aFrameElement,
                                   ContentParent* aOpenerContentParent)
 {
     if (!sCanLaunchSubprocesses) {
         return nullptr;
     }
 
     ProcessPriority initialPriority = GetInitialProcessPriority(aFrameElement);
+    bool isInContentProcess = (XRE_GetProcessType() != GeckoProcessType_Default);
 
     if (aContext.IsBrowserElement() || !aContext.HasOwnApp()) {
         nsRefPtr<TabParent> tp;
         nsRefPtr<nsIContentParent> constructorSender;
-        if (XRE_GetProcessType() != GeckoProcessType_Default) {
+        if (isInContentProcess) {
             MOZ_ASSERT(aContext.IsBrowserElement());
-            ContentChild* child = ContentChild::GetSingleton();
-            uint64_t id;
-            bool isForApp;
-            bool isForBrowser;
-            if (!child->SendCreateChildProcess(aContext.AsIPCTabContext(),
-                                               initialPriority,
-                                               &id,
-                                               &isForApp,
-                                               &isForBrowser)) {
-                return nullptr;
-            }
-            if (!child->CallBridgeToChildProcess(id)) {
-                return nullptr;
-            }
-            ContentBridgeParent* parent = child->GetLastBridge();
-            parent->SetChildID(id);
-            parent->SetIsForApp(isForApp);
-            parent->SetIsForBrowser(isForBrowser);
-            constructorSender = parent;
+            constructorSender = CreateContentBridgeParent(aContext, initialPriority);
         } else {
           if (aOpenerContentParent) {
             constructorSender = aOpenerContentParent;
           } else {
-            constructorSender = GetNewOrUsed(aContext.IsBrowserElement(), initialPriority);
+            constructorSender = GetNewOrUsedBrowserProcess(aContext.IsBrowserElement(),
+                                                                    initialPriority);
           }
         }
         if (constructorSender) {
             uint32_t chromeFlags = 0;
 
             // Propagate the private-browsing status of the element's parent
             // docshell to the remote docshell, via the chrome flags.
             nsCOMPtr<Element> frameElement = do_QueryInterface(aFrameElement);
@@ -923,136 +954,171 @@ ContentParent::CreateBrowserOrApp(const 
             return static_cast<TabParent*>(browser);
         }
         return nullptr;
     }
 
     // If we got here, we have an app and we're not a browser element.  ownApp
     // shouldn't be null, because we otherwise would have gone into the
     // !HasOwnApp() branch above.
-    nsCOMPtr<mozIApplication> ownApp = aContext.GetOwnApp();
-
-    if (!sAppContentParents) {
-        sAppContentParents =
-            new nsDataHashtable<nsStringHashKey, ContentParent*>();
-    }
-
-    // Each app gets its own ContentParent instance unless it shares it with
-    // a parent app.
+    nsRefPtr<nsIContentParent> parent;
+    bool reused = false;
+    bool tookPreallocated = false;
     nsAutoString manifestURL;
-    if (NS_FAILED(ownApp->GetManifestURL(manifestURL))) {
-        NS_ERROR("Failed to get manifest URL");
-        return nullptr;
+
+    if (isInContentProcess) {
+      parent = CreateContentBridgeParent(aContext, initialPriority);
     }
-
-    nsRefPtr<ContentParent> p = sAppContentParents->Get(manifestURL);
-
-    if (!p && Preferences::GetBool("dom.ipc.reuse_parent_app")) {
-        nsAutoString parentAppManifestURL;
-        aFrameElement->GetAttr(kNameSpaceID_None,
-                               nsGkAtoms::parentapp, parentAppManifestURL);
-        nsAdoptingString systemAppManifestURL =
-            Preferences::GetString("b2g.system_manifest_url");
-        nsCOMPtr<nsIAppsService> appsService =
-            do_GetService(APPS_SERVICE_CONTRACTID);
-        if (!parentAppManifestURL.IsEmpty() &&
-            !parentAppManifestURL.Equals(systemAppManifestURL) &&
-            appsService) {
-            nsCOMPtr<mozIApplication> parentApp;
-            nsCOMPtr<mozIApplication> app;
-            appsService->GetAppByManifestURL(parentAppManifestURL,
-                                             getter_AddRefs(parentApp));
-            appsService->GetAppByManifestURL(manifestURL,
-                                             getter_AddRefs(app));
-
-            // Only let certified apps re-use the same process.
-            unsigned short parentAppStatus = 0;
-            unsigned short appStatus = 0;
-            if (app &&
-                NS_SUCCEEDED(app->GetAppStatus(&appStatus)) &&
-                appStatus == nsIPrincipal::APP_STATUS_CERTIFIED &&
-                parentApp &&
-                NS_SUCCEEDED(parentApp->GetAppStatus(&parentAppStatus)) &&
-                parentAppStatus == nsIPrincipal::APP_STATUS_CERTIFIED) {
-                // Check if we can re-use the process of the parent app.
-                p = sAppContentParents->Get(parentAppManifestURL);
+    else {
+        nsCOMPtr<mozIApplication> ownApp = aContext.GetOwnApp();
+
+        if (!sAppContentParents) {
+            sAppContentParents =
+                new nsDataHashtable<nsStringHashKey, ContentParent*>();
+        }
+
+        // Each app gets its own ContentParent instance unless it shares it with
+        // a parent app.
+        if (NS_FAILED(ownApp->GetManifestURL(manifestURL))) {
+            NS_ERROR("Failed to get manifest URL");
+            return nullptr;
+        }
+
+        nsRefPtr<ContentParent> p = sAppContentParents->Get(manifestURL);
+
+        if (!p && Preferences::GetBool("dom.ipc.reuse_parent_app")) {
+            nsAutoString parentAppManifestURL;
+            aFrameElement->GetAttr(kNameSpaceID_None,
+                                   nsGkAtoms::parentapp, parentAppManifestURL);
+            nsAdoptingString systemAppManifestURL =
+                Preferences::GetString("b2g.system_manifest_url");
+            nsCOMPtr<nsIAppsService> appsService =
+                do_GetService(APPS_SERVICE_CONTRACTID);
+            if (!parentAppManifestURL.IsEmpty() &&
+                !parentAppManifestURL.Equals(systemAppManifestURL) &&
+                appsService) {
+                nsCOMPtr<mozIApplication> parentApp;
+                nsCOMPtr<mozIApplication> app;
+                appsService->GetAppByManifestURL(parentAppManifestURL,
+                                                getter_AddRefs(parentApp));
+                appsService->GetAppByManifestURL(manifestURL,
+                                                getter_AddRefs(app));
+
+                // Only let certified apps re-use the same process.
+                unsigned short parentAppStatus = 0;
+                unsigned short appStatus = 0;
+                if (app &&
+                    NS_SUCCEEDED(app->GetAppStatus(&appStatus)) &&
+                    appStatus == nsIPrincipal::APP_STATUS_CERTIFIED &&
+                    parentApp &&
+                    NS_SUCCEEDED(parentApp->GetAppStatus(&parentAppStatus)) &&
+                    parentAppStatus == nsIPrincipal::APP_STATUS_CERTIFIED) {
+                    // Check if we can re-use the process of the parent app.
+                    p = sAppContentParents->Get(parentAppManifestURL);
+                }
             }
         }
-    }
-
-    if (p) {
-        // Check that the process is still alive and set its priority.
-        // Hopefully the process won't die after this point, if this call
-        // succeeds.
-        if (!p->SetPriorityAndCheckIsAlive(initialPriority)) {
-            p = nullptr;
+
+        if (p) {
+            // Check that the process is still alive and set its priority.
+            // Hopefully the process won't die after this point, if this call
+            // succeeds.
+            if (!p->SetPriorityAndCheckIsAlive(initialPriority)) {
+                p = nullptr;
+            }
         }
+
+        reused = !!p;
+        if (!p) {
+            p = GetNewOrPreallocatedAppProcess(ownApp,
+                                               initialPriority,
+                                               nullptr,
+                                               &tookPreallocated);
+            MOZ_ASSERT(p);
+            sAppContentParents->Put(manifestURL, p);
+        }
+        parent = static_cast<nsIContentParent*>(p);
     }
 
-    bool reused = !!p;
-    bool tookPreallocated = false;
-    if (!p) {
-        p = MaybeTakePreallocatedAppProcess(manifestURL,
-                                            initialPriority);
-        tookPreallocated = !!p;
-        if (!tookPreallocated) {
-            // XXXkhuey Nuwa wants the frame loader to try again later, but the
-            // frame loader is really not set up to do that ...
-            NS_WARNING("Unable to use pre-allocated app process");
-            p = new ContentParent(ownApp,
-                                  /* aOpener = */ nullptr,
-                                  /* isForBrowserElement = */ false,
-                                  /* isForPreallocated = */ false,
-                                  initialPriority);
-            p->Init();
-        }
-        sAppContentParents->Put(manifestURL, p);
+    if (!parent) {
+        return nullptr;
     }
 
     uint32_t chromeFlags = 0;
 
-    nsRefPtr<TabParent> tp = new TabParent(p, aContext, chromeFlags);
+    nsRefPtr<TabParent> tp = new TabParent(parent, aContext, chromeFlags);
     tp->SetOwnerElement(aFrameElement);
-    PBrowserParent* browser = p->SendPBrowserConstructor(
+    PBrowserParent* browser = parent->SendPBrowserConstructor(
         // DeallocPBrowserParent() releases this ref.
         nsRefPtr<TabParent>(tp).forget().take(),
         aContext.AsIPCTabContext(),
         chromeFlags,
-        p->ChildID(),
-        p->IsForApp(),
-        p->IsForBrowser());
+        parent->ChildID(),
+        parent->IsForApp(),
+        parent->IsForBrowser());
+
+    if (isInContentProcess) {
+        // Just return directly without the following check in content process.
+        return static_cast<TabParent*>(browser);
+    }
+
     if (!browser) {
         // We failed to actually start the PBrowser.  This can happen if the
         // other process has already died.
         if (!reused) {
             // Don't leave a broken ContentParent in the hashtable.
-            p->KillHard();
+            parent->AsContentParent()->KillHard();
             sAppContentParents->Remove(manifestURL);
-            p = nullptr;
+            parent = nullptr;
         }
 
         // If we took the preallocated process and it was already dead, try
         // again with a non-preallocated process.  We can be sure this won't
         // loop forever, because the next time through there will be no
         // preallocated process to take.
         if (tookPreallocated) {
           return ContentParent::CreateBrowserOrApp(aContext,
                                                    aFrameElement,
                                                    aOpenerContentParent);
         }
 
         // Otherwise just give up.
         return nullptr;
     }
 
-    p->MaybeTakeCPUWakeLock(aFrameElement);
+    parent->AsContentParent()->MaybeTakeCPUWakeLock(aFrameElement);
 
     return static_cast<TabParent*>(browser);
 }
 
+/*static*/ ContentBridgeParent*
+ContentParent::CreateContentBridgeParent(const TabContext& aContext,
+                                         const hal::ProcessPriority& aPriority)
+{
+    ContentChild* child = ContentChild::GetSingleton();
+    uint64_t id;
+    bool isForApp;
+    bool isForBrowser;
+    if (!child->SendCreateChildProcess(aContext.AsIPCTabContext(),
+                                       aPriority,
+                                       &id,
+                                       &isForApp,
+                                       &isForBrowser)) {
+        return nullptr;
+    }
+    if (!child->CallBridgeToChildProcess(id)) {
+        return nullptr;
+    }
+    ContentBridgeParent* parent = child->GetLastBridge();
+    parent->SetChildID(id);
+    parent->SetIsForApp(isForApp);
+    parent->SetIsForBrowser(isForBrowser);
+    return parent;
+}
+
 void
 ContentParent::GetAll(nsTArray<ContentParent*>& aArray)
 {
     aArray.Clear();
 
     if (!sContentParents) {
         return;
     }
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -58,16 +58,17 @@ namespace dom {
 
 class Element;
 class TabParent;
 class PStorageParent;
 class ClonedMessageData;
 class MemoryReport;
 class TabContext;
 class PFileDescriptorSetParent;
+class ContentBridgeParent;
 
 class ContentParent : public PContentParent
                     , public nsIContentParent
                     , public nsIObserver
                     , public nsIDOMGeoPositionCallback
                     , public mozilla::LinkedListElement<ContentParent>
 {
     typedef mozilla::ipc::GeckoChildProcessHost GeckoChildProcessHost;
@@ -91,21 +92,27 @@ public:
      * very expensive in general.  It also bypasses the normal
      * shutdown process.
      */
     static void JoinAllSubprocesses();
 
     static bool PreallocatedProcessReady();
     static void RunAfterPreallocatedProcessReady(nsIRunnable* aRequest);
 
+    /**
+     * Get or create a content process for:
+     * 1. browser iframe
+     * 2. remote xul <browser>
+     * 3. normal iframe
+     */
     static already_AddRefed<ContentParent>
-    GetNewOrUsed(bool aForBrowserElement = false,
-                 hal::ProcessPriority aPriority =
-                   hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND,
-                 ContentParent* aOpener = nullptr);
+    GetNewOrUsedBrowserProcess(bool aForBrowserElement = false,
+                               hal::ProcessPriority aPriority =
+                               hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND,
+                               ContentParent* aOpener = nullptr);
 
     /**
      * Create a subprocess suitable for use as a preallocated app process.
      */
     static already_AddRefed<ContentParent> PreallocateAppProcess();
 
     static already_AddRefed<ContentParent> RunNuwaProcess();
 
@@ -281,23 +288,28 @@ private:
     static nsTArray<ContentParent*>* sPrivateContent;
     static StaticAutoPtr<LinkedList<ContentParent> > sContentParents;
 
     static void JoinProcessesIOThread(const nsTArray<ContentParent*>* aProcesses,
                                       Monitor* aMonitor, bool* aDone);
 
     // Take the preallocated process and transform it into a "real" app process,
     // for the specified manifest URL.  If there is no preallocated process (or
-    // if it's dead), this returns false.
+    // if it's dead), create a new one and set aTookPreAllocated to false.
     static already_AddRefed<ContentParent>
-    MaybeTakePreallocatedAppProcess(const nsAString& aAppManifestURL,
-                                    hal::ProcessPriority aInitialPriority);
+    GetNewOrPreallocatedAppProcess(mozIApplication* aApp,
+                                   hal::ProcessPriority aInitialPriority,
+                                   ContentParent* aOpener,
+                                   /*out*/ bool* aTookPreAllocated = nullptr);
 
     static hal::ProcessPriority GetInitialProcessPriority(Element* aFrameElement);
 
+    static ContentBridgeParent* CreateContentBridgeParent(const TabContext& aContext,
+                                                          const hal::ProcessPriority& aPriority);
+
     // Hide the raw constructor methods since we don't want client code
     // using them.
     virtual PBrowserParent* SendPBrowserConstructor(
         PBrowserParent* actor,
         const IPCTabContext& context,
         const uint32_t& chromeFlags,
         const uint64_t& aId,
         const bool& aIsForApp,
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -815,17 +815,17 @@ nsXULAppInfo::GetBrowserTabsRemote(bool*
 }
 
 NS_IMETHODIMP
 nsXULAppInfo::EnsureContentProcess()
 {
   if (XRE_GetProcessType() != GeckoProcessType_Default)
     return NS_ERROR_NOT_AVAILABLE;
 
-  nsRefPtr<ContentParent> unused = ContentParent::GetNewOrUsed();
+  nsRefPtr<ContentParent> unused = ContentParent::GetNewOrUsedBrowserProcess();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXULAppInfo::InvalidateCachesOnRestart()
 {
   nsCOMPtr<nsIFile> file;
   nsresult rv = NS_GetSpecialDirectory(NS_APP_PROFILE_DIR_STARTUP, 
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -736,17 +736,17 @@ XRE_ShutdownChildProcess()
 #endif // XP_MACOSX
 }
 
 namespace {
 ContentParent* gContentParent; //long-lived, manually refcounted
 TestShellParent* GetOrCreateTestShellParent()
 {
     if (!gContentParent) {
-        nsRefPtr<ContentParent> parent = ContentParent::GetNewOrUsed();
+        nsRefPtr<ContentParent> parent = ContentParent::GetNewOrUsedBrowserProcess();
         parent.forget(&gContentParent);
     } else if (!gContentParent->IsAlive()) {
         return nullptr;
     }
     TestShellParent* tsp = gContentParent->GetTestShellSingleton();
     if (!tsp) {
         tsp = gContentParent->CreateTestShell();
     }