Bug 1192255: Clean up ContentParent's observer topics for the Nuwa process. r=khuey
authorCervantes Yu <cyu@mozilla.com>
Fri, 14 Aug 2015 15:41:54 +0800
changeset 260585 c46f4154cceac710d34729778b2528f1422962e0
parent 260584 7b1ba1e20dd9c55cfc80cc1c553cafa90ce0b8f9
child 260586 86347848ca3b600c0601e77f244be42040ecc227
push id17295
push usercyu@mozilla.com
push dateThu, 03 Sep 2015 10:46:03 +0000
treeherderb2g-inbound@c46f4154ccea [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey
bugs1192255
milestone43.0a1
Bug 1192255: Clean up ContentParent's observer topics for the Nuwa process. r=khuey
dom/ipc/ContentChild.cpp
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
ipc/glue/MessageLink.cpp
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -2169,20 +2169,17 @@ ContentChild::RecvScreenSizeChanged(cons
 #endif
     return true;
 }
 
 bool
 ContentChild::RecvFlushMemory(const nsString& reason)
 {
 #ifdef MOZ_NUWA_PROCESS
-    if (IsNuwaProcess()) {
-        // Don't flush memory in the nuwa process: the GC thread could be frozen.
-        return true;
-    }
+    MOZ_ASSERT(!IsNuwaProcess() || !IsNuwaReady());
 #endif
     nsCOMPtr<nsIObserverService> os =
         mozilla::services::GetObserverService();
     if (os)
         os->NotifyObservers(nullptr, "memory-pressure", reason.get());
     return true;
 }
 
@@ -2411,21 +2408,17 @@ ContentChild::RecvNotifyProcessPriorityC
                         "ipc:process-priority-changed",  nullptr);
     return true;
 }
 
 bool
 ContentChild::RecvMinimizeMemoryUsage()
 {
 #ifdef MOZ_NUWA_PROCESS
-    if (IsNuwaProcess()) {
-        // Don't minimize memory in the nuwa process: it will perform GC, but the
-        // GC thread could be frozen.
-        return true;
-    }
+    MOZ_ASSERT(!IsNuwaProcess() || !IsNuwaReady());
 #endif
     nsCOMPtr<nsIMemoryReporterManager> mgr =
         do_GetService("@mozilla.org/memory-reporter-manager;1");
     NS_ENSURE_TRUE(mgr, true);
 
     mgr->MinimizeMemoryUsage(/* callback = */ nullptr);
     return true;
 }
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -677,16 +677,32 @@ static const char* sObserverTopics[] = {
     "profiler-stopped",
     "profiler-paused",
     "profiler-resumed",
     "profiler-subprocess-gather",
     "profiler-subprocess",
 #endif
 };
 
+#ifdef MOZ_NUWA_PROCESS
+// Contains the observer topics that can be sent to the Nuwa process after it
+// becomes ready. The ContentParent instance will unregister sObserverTopics
+// if not listed in sNuwaSafeObserverTopics.
+static const char* sNuwaSafeObserverTopics[] = {
+    "xpcom-shutdown",
+    "profile-before-change",
+#ifdef MOZ_WIDGET_GONK
+    "phone-state-changed",
+#endif
+#ifdef ACCESSIBILITY
+    "a11y-init-or-shutdown",
+#endif
+    "nsPref:Changed"
+};
+#endif
 /* static */ already_AddRefed<ContentParent>
 ContentParent::RunNuwaProcess()
 {
     MOZ_ASSERT(NS_IsMainThread());
     nsRefPtr<ContentParent> nuwaProcess =
         new ContentParent(/* aApp = */ nullptr,
                           /* aOpener = */ nullptr,
                           /* aIsForBrowser = */ false,
@@ -2918,25 +2934,62 @@ ContentParent::ForkNewProcess(bool aBloc
   if (mNuwaParent->ForkNewProcess(pid, mozilla::Move(fds), aBlocking)) {
     OnNewProcessCreated(pid, mozilla::Move(fds));
   }
 #else
   NS_ERROR("ContentParent::ForkNewProcess() not implemented!");
 #endif
 }
 
+#ifdef MOZ_NUWA_PROCESS
+// Keep only observer topics listed in sNuwaSafeObserverTopics and unregister
+// all the other registered topics.
+static void
+KeepNuwaSafeObserverTopics(ContentParent* aNuwaContentParent)
+{
+  MOZ_ASSERT(aNuwaContentParent && aNuwaContentParent->IsNuwaProcess());
+
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  if (!obs) {
+      return;
+  }
+
+  size_t topicLength = ArrayLength(sObserverTopics);
+  for (size_t i = 0; i < topicLength; ++i) {
+      bool nuwaSafe = false;
+      size_t safeTopicLength = ArrayLength(sNuwaSafeObserverTopics);
+
+      for (size_t j = 0; j < safeTopicLength; j++) {
+          if (!nsCRT::strcmp(sObserverTopics[i],
+                            sNuwaSafeObserverTopics[j])) {
+              // In the whitelist: don't need to unregister.
+              nuwaSafe = true;
+              break;
+          }
+      }
+
+      if (!nuwaSafe) {
+          obs->RemoveObserver(aNuwaContentParent, sObserverTopics[i]);
+      }
+  }
+
+}
+#endif
+
 void
 ContentParent::OnNuwaReady()
 {
 #ifdef MOZ_NUWA_PROCESS
     // Protection from unauthorized IPC message is done in PNuwa protocol.
     // Just assert that this actor is really for the Nuwa process.
     MOZ_ASSERT(IsNuwaProcess());
 
     sNuwaReady = true;
+    KeepNuwaSafeObserverTopics(this);
+
     PreallocatedProcessManager::OnNuwaReady();
     return;
 #else
     NS_ERROR("ContentParent::OnNuwaReady() not implemented!");
     return;
 #endif
 }
 
@@ -3020,69 +3073,67 @@ ContentParent::Observe(nsISupports* aSub
             NS_ProcessNextEvent(nullptr, true);
         }
         NS_ASSERTION(!mSubprocess, "Close should have nulled mSubprocess");
     }
 
     if (!mIsAlive || !mSubprocess)
         return NS_OK;
 
+    // The Nuwa process unregisters the topics after it becomes ready except for
+    // the ones listed in sNuwaSafeObserverTopics. If the topic needs to be
+    // observed by the Nuwa process, either for:
+    // 1. The topic is safe for the Nuwa process, either:
+    // 1.1 The state can safely happen (only run on the main thread) in the Nuwa
+    //     process (e.g. "a11y-init-or-shutdown"), or
+    // 1.2 The topic doesn't send an IPC message (e.g. "xpcom-shutdown").
+    // 2. The topic needs special handling (e.g. nsPref:Changed),
+    // add the topic to sNuwaSafeObserverTopics and then handle it if necessary.
+
     // listening for memory pressure event
     if (!strcmp(aTopic, "memory-pressure") &&
         !StringEndsWith(nsDependentString(aData),
                         NS_LITERAL_STRING("-no-forward"))) {
         unused << SendFlushMemory(nsDependentString(aData));
     }
     // listening for remotePrefs...
     else if (!strcmp(aTopic, "nsPref:changed")) {
         // We know prefs are ASCII here.
         NS_LossyConvertUTF16toASCII strData(aData);
 
         PrefSetting pref(strData, null_t(), null_t());
         Preferences::GetPreference(&pref);
 #ifdef MOZ_NUWA_PROCESS
-        if (IsNuwaProcess() && PreallocatedProcessManager::IsNuwaReady()) {
+        if (IsReadyNuwaProcess()) {
             // Don't send the pref update to the Nuwa process. Save the update
             // to send to the forked child.
             if (!sNuwaPrefUpdates) {
                 sNuwaPrefUpdates = new nsTArray<PrefSetting>();
             }
             sNuwaPrefUpdates->AppendElement(pref);
         } else if (!SendPreferenceUpdate(pref)) {
             return NS_ERROR_NOT_AVAILABLE;
         }
 #else
         if (!SendPreferenceUpdate(pref)) {
             return NS_ERROR_NOT_AVAILABLE;
         }
 #endif
     }
     else if (!strcmp(aTopic, NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC)) {
-      NS_ConvertUTF16toUTF8 dataStr(aData);
-      const char *offline = dataStr.get();
-#ifdef MOZ_NUWA_PROCESS
-      if (!(IsNuwaReady() && IsNuwaProcess())) {
-#endif
-          if (!SendSetOffline(!strcmp(offline, "true") ? true : false)) {
-              return NS_ERROR_NOT_AVAILABLE;
-          }
-#ifdef MOZ_NUWA_PROCESS
-      }
-#endif
+        NS_ConvertUTF16toUTF8 dataStr(aData);
+        const char *offline = dataStr.get();
+        if (!SendSetOffline(!strcmp(offline, "true") ? true : false)) {
+            return NS_ERROR_NOT_AVAILABLE;
+        }
     }
     else if (!strcmp(aTopic, NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC)) {
-#ifdef MOZ_NUWA_PROCESS
-        if (!(IsNuwaReady() && IsNuwaProcess())) {
-#endif
-            if (!SendSetConnectivity(NS_LITERAL_STRING("true").Equals(aData))) {
-                return NS_ERROR_NOT_AVAILABLE;
-            }
-#ifdef MOZ_NUWA_PROCESS
+        if (!SendSetConnectivity(NS_LITERAL_STRING("true").Equals(aData))) {
+            return NS_ERROR_NOT_AVAILABLE;
         }
-#endif
     }
     // listening for alert notifications
     else if (!strcmp(aTopic, "alertfinished") ||
              !strcmp(aTopic, "alertclickcallback") ||
              !strcmp(aTopic, "alertshow") ) {
         if (!SendNotifyAlertsObserver(nsDependentCString(aTopic),
                                       nsDependentString(aData)))
             return NS_ERROR_NOT_AVAILABLE;
@@ -3098,23 +3149,17 @@ ContentParent::Observe(nsISupports* aSub
     }
     else if (!strcmp(aTopic, "last-pb-context-exited")) {
         unused << SendLastPrivateDocShellDestroyed();
     }
     else if (!strcmp(aTopic, "file-watcher-update")) {
         nsCString creason;
         CopyUTF16toUTF8(aData, creason);
         DeviceStorageFile* file = static_cast<DeviceStorageFile*>(aSubject);
-
-#ifdef MOZ_NUWA_PROCESS
-        if (!(IsNuwaReady() && IsNuwaProcess()))
-#endif
-        {
-            unused << SendFilePathUpdate(file->mStorageType, file->mStorageName, file->mPath, creason);
-        }
+        unused << SendFilePathUpdate(file->mStorageType, file->mStorageName, file->mPath, creason);
     }
 #ifdef MOZ_WIDGET_GONK
     else if(!strcmp(aTopic, NS_VOLUME_STATE_CHANGED)) {
         nsCOMPtr<nsIVolume> vol = do_QueryInterface(aSubject);
         if (!vol) {
             return NS_ERROR_NOT_AVAILABLE;
         }
 
@@ -3137,37 +3182,27 @@ ContentParent::Observe(nsISupports* aSub
         vol->GetIsMediaPresent(&isMediaPresent);
         vol->GetIsSharing(&isSharing);
         vol->GetIsFormatting(&isFormatting);
         vol->GetIsFake(&isFake);
         vol->GetIsUnmounting(&isUnmounting);
         vol->GetIsRemovable(&isRemovable);
         vol->GetIsHotSwappable(&isHotSwappable);
 
-#ifdef MOZ_NUWA_PROCESS
-        if (!(IsNuwaReady() && IsNuwaProcess()))
-#endif
-        {
-            unused << SendFileSystemUpdate(volName, mountPoint, state,
-                                           mountGeneration, isMediaPresent,
-                                           isSharing, isFormatting, isFake,
-                                           isUnmounting, isRemovable, isHotSwappable);
-        }
+        unused << SendFileSystemUpdate(volName, mountPoint, state,
+                                       mountGeneration, isMediaPresent,
+                                       isSharing, isFormatting, isFake,
+                                       isUnmounting, isRemovable, isHotSwappable);
     } else if (!strcmp(aTopic, "phone-state-changed")) {
         nsString state(aData);
         unused << SendNotifyPhoneStateChange(state);
     }
     else if(!strcmp(aTopic, NS_VOLUME_REMOVED)) {
-#ifdef MOZ_NUWA_PROCESS
-        if (!(IsNuwaReady() && IsNuwaProcess()))
-#endif
-        {
-            nsString volName(aData);
-            unused << SendVolumeRemoved(volName);
-        }
+        nsString volName(aData);
+        unused << SendVolumeRemoved(volName);
     }
 #endif
 #ifdef ACCESSIBILITY
     // Make sure accessibility is running in content process when accessibility
     // gets initiated in chrome process.
     else if (aData && (*aData == '1') &&
              !strcmp(aTopic, "a11y-init-or-shutdown")) {
         unused << SendActivateA11y();
@@ -4463,22 +4498,20 @@ ContentParent::DoSendAsyncMessage(JSCont
     if (!BuildClonedMessageDataForParent(this, aData, data)) {
         return false;
     }
     InfallibleTArray<CpowEntry> cpows;
     jsipc::CPOWManager* mgr = GetCPOWManager();
     if (aCpows && (!mgr || !mgr->Wrap(aCx, aCpows, &cpows))) {
         return false;
     }
-#ifdef MOZ_NUWA_PROCESS
-    if (IsNuwaProcess() && IsNuwaReady()) {
+    if (IsReadyNuwaProcess()) {
         // Nuwa won't receive frame messages after it is frozen.
         return true;
     }
-#endif
     return SendAsyncMessage(nsString(aMessage), data, cpows, Principal(aPrincipal));
 }
 
 bool
 ContentParent::CheckPermission(const nsAString& aPermission)
 {
     return AssertAppProcessPermission(this, NS_ConvertUTF16toUTF8(aPermission).get());
 }
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -255,16 +255,25 @@ public:
     virtual bool IsForBrowser() override
     {
       return mIsForBrowser;
     }
 #ifdef MOZ_NUWA_PROCESS
     bool IsNuwaProcess();
 #endif
 
+    // A shorthand for checking if the Nuwa process is ready.
+    bool IsReadyNuwaProcess() {
+#ifdef MOZ_NUWA_PROCESS
+        return IsNuwaProcess() && IsNuwaReady();
+#else
+        return false;
+#endif
+    }
+
     GeckoChildProcessHost* Process() {
         return mSubprocess;
     }
 
     int32_t Pid();
 
     ContentParent* Opener() {
         return mOpener;
--- a/ipc/glue/MessageLink.cpp
+++ b/ipc/glue/MessageLink.cpp
@@ -182,16 +182,17 @@ ProcessLink::SendMessage(Message *msg)
 #ifdef MOZ_NUWA_PROCESS
     // Parent to child: check whether we are sending some unexpected message to
     // the Nuwa process.
     if (mIsToNuwaProcess && mozilla::dom::ContentParent::IsNuwaReady()) {
         switch (msg->type()) {
         case mozilla::dom::PNuwa::Msg_Fork__ID:
         case mozilla::dom::PNuwa::Reply_AddNewProcess__ID:
         case mozilla::dom::PContent::Msg_NotifyPhoneStateChange__ID:
+        case mozilla::dom::PContent::Msg_ActivateA11y__ID:
         case mozilla::hal_sandbox::PHal::Msg_NotifyNetworkChange__ID:
         case GOODBYE_MESSAGE_TYPE:
             break;
         default:
 #ifdef DEBUG
             MOZ_CRASH();
 #else
             // In optimized build, message will be dropped.