diff options
Diffstat (limited to 'www/firefox/files/patch-bug1438678')
| -rw-r--r-- | www/firefox/files/patch-bug1438678 | 1000 |
1 files changed, 1000 insertions, 0 deletions
diff --git a/www/firefox/files/patch-bug1438678 b/www/firefox/files/patch-bug1438678 new file mode 100644 index 000000000000..47e8dec9ad5b --- /dev/null +++ b/www/firefox/files/patch-bug1438678 @@ -0,0 +1,1000 @@ +commit 68124009fc5a +Author: Nicholas Nethercote <nnethercote@mozilla.com> +Date: Fri Feb 16 17:54:16 2018 +1100 + + Bug 1438678 - Pass early prefs via shared memory instead of the command line. r=bobowen,jld,glandium. + + This patch replaces the large -intPrefs/-boolPrefs/-stringPrefs flags with + a short-lived, anonymous, shared memory segment that is used to pass the early + prefs. + + Removing the bloat from the command line is nice, but more important is the + fact that this will let us pass more prefs at content process start-up, which + will allow us to remove the early/late prefs split (bug 1436911). + + Although this mechanism is only used for prefs, it's conceivable that it could + be used for other data that must be received very early by children, and for + which the command line isn't ideal. + + Notable details: + + - Much of the patch deals with the various platform-specific ways of passing + handles/fds to children. + + - Linux and Mac: we use a fixed fd (8) in combination with the new + GeckoChildProcessHost::AddFdToRemap() function (which ensures the child + won't close the fd). + + - Android: like Linux and Mac, but the handles get passed via "parcels" and + we use the new SetPrefsFd() function instead of the fixed fd. + + - Windows: there is no need to duplicate the handle because Windows handles + are system-wide. But we do use the new + GeckoChildProcessHost::AddHandleToShare() function to add it to the list of + inheritable handles. We also ensure that list is processed on all paths + (MOZ_SANDBOX with sandbox, MOZ_SANDBOX without sandbox, non-MOZ_SANDBOX) so + that the handles are marked as inheritable. The handle is passed via the + -prefsHandle flag. + + The -prefsLen flag is used on all platforms to indicate the size of the + shared memory segment. + + - The patch also moves the serialization/deserialization of the prefs in/out of + the shared memory into libpref, which is a better spot for it. (This means + Preferences::MustSendToContentProcesses() can be removed.) + + MozReview-Commit-ID: 8fREEBiYFvc + + --HG-- + extra : rebase_source : 7e4c8ebdbcd7d74d6bd2ab3c9e75a6a17dbd8dfe +--- + dom/ipc/ContentParent.cpp | 91 +++++++------- + dom/ipc/ContentProcess.cpp | 121 ++++++++++--------- + dom/ipc/ContentProcess.h | 5 + + ipc/chromium/src/base/process_util_win.cc | 4 + + ipc/glue/GeckoChildProcessHost.cpp | 36 +++--- + ipc/glue/GeckoChildProcessHost.h | 10 ++ + .../org/mozilla/gecko/process/IChildProcess.aidl | 3 +- + .../main/java/org/mozilla/gecko/GeckoThread.java | 13 +- + .../org/mozilla/gecko/mozglue/GeckoLoader.java | 2 +- + .../mozilla/gecko/process/GeckoProcessManager.java | 19 +-- + .../gecko/process/GeckoServiceChildProcess.java | 4 +- + modules/libpref/Preferences.cpp | 134 +++++++++++++++++++-- + modules/libpref/Preferences.h | 17 +-- + mozglue/android/APKOpen.cpp | 4 +- + toolkit/xre/Bootstrap.cpp | 4 +- + toolkit/xre/Bootstrap.h | 2 +- + toolkit/xre/nsEmbedFunctions.cpp | 3 +- + widget/android/GeneratedJNIWrappers.cpp | 4 +- + widget/android/GeneratedJNIWrappers.h | 5 +- + xpcom/build/nsXULAppAPI.h | 2 +- + 20 files changed, 318 insertions(+), 165 deletions(-) + +diff --git dom/ipc/ContentParent.cpp dom/ipc/ContentParent.cpp +index e27f3eedc1b1..60be7005354b 100644 +--- dom/ipc/ContentParent.cpp ++++ dom/ipc/ContentParent.cpp +@@ -7,6 +7,7 @@ + #include "mozilla/DebugOnly.h" + + #include "base/basictypes.h" ++#include "base/shared_memory.h" + + #include "ContentParent.h" + #include "TabParent.h" +@@ -1998,61 +1999,56 @@ ContentParent::LaunchSubprocess(ProcessPriority aInitialPriority /* = PROCESS_PR + extraArgs.push_back(idStr); + extraArgs.push_back(IsForBrowser() ? "-isForBrowser" : "-notForBrowser"); + +- nsAutoCStringN<1024> boolPrefs; +- nsAutoCStringN<1024> intPrefs; +- nsAutoCStringN<1024> stringPrefs; ++ // Prefs information is passed via anonymous shared memory to avoid bloating ++ // the command line. + +- size_t prefsLen; +- ContentPrefs::GetEarlyPrefs(&prefsLen); ++ // Serialize the early prefs. ++ nsAutoCStringN<1024> prefs; ++ Preferences::SerializeEarlyPreferences(prefs); + +- for (unsigned int i = 0; i < prefsLen; i++) { +- const char* prefName = ContentPrefs::GetEarlyPref(i); +- MOZ_ASSERT(i == 0 || strcmp(prefName, ContentPrefs::GetEarlyPref(i - 1)) > 0, +- "Content process preferences should be sorted alphabetically."); +- +- if (!Preferences::MustSendToContentProcesses(prefName)) { +- continue; +- } +- +- switch (Preferences::GetType(prefName)) { +- case nsIPrefBranch::PREF_INT: +- intPrefs.Append(nsPrintfCString("%u:%d|", i, Preferences::GetInt(prefName))); +- break; +- case nsIPrefBranch::PREF_BOOL: +- boolPrefs.Append(nsPrintfCString("%u:%d|", i, Preferences::GetBool(prefName))); +- break; +- case nsIPrefBranch::PREF_STRING: { +- nsAutoCString value; +- Preferences::GetCString(prefName, value); +- stringPrefs.Append(nsPrintfCString("%u:%d;%s|", i, value.Length(), value.get())); +- } +- break; +- case nsIPrefBranch::PREF_INVALID: +- break; +- default: +- printf("preference type: %x\n", Preferences::GetType(prefName)); +- MOZ_CRASH(); +- } ++ // Set up the shared memory. ++ base::SharedMemory shm; ++ if (!shm.Create("", /* read_only */ false, /* open_existing */ false, ++ prefs.Length())) { ++ NS_ERROR("failed to create shared memory in the parent"); ++ MarkAsDead(); ++ return false; ++ } ++ if (!shm.Map(prefs.Length())) { ++ NS_ERROR("failed to map shared memory in the parent"); ++ MarkAsDead(); ++ return false; + } + +- nsCString schedulerPrefs = Scheduler::GetPrefs(); ++ // Copy the serialized prefs into the shared memory. ++ memcpy(static_cast<char*>(shm.memory()), prefs.get(), prefs.Length()); + +- // Only do these ones if they're non-empty. +- if (!intPrefs.IsEmpty()) { +- extraArgs.push_back("-intPrefs"); +- extraArgs.push_back(intPrefs.get()); +- } +- if (!boolPrefs.IsEmpty()) { +- extraArgs.push_back("-boolPrefs"); +- extraArgs.push_back(boolPrefs.get()); +- } +- if (!stringPrefs.IsEmpty()) { +- extraArgs.push_back("-stringPrefs"); +- extraArgs.push_back(stringPrefs.get()); +- } ++#if defined(XP_WIN) ++ // Record the handle as to-be-shared, and pass it via a command flag. This ++ // works because Windows handles are system-wide. ++ HANDLE prefsHandle = shm.handle(); ++ mSubprocess->AddHandleToShare(prefsHandle); ++ extraArgs.push_back("-prefsHandle"); ++ extraArgs.push_back( ++ nsPrintfCString("%zu", reinterpret_cast<uintptr_t>(prefsHandle)).get()); ++#else ++ // In contrast, Unix fds are per-process. So remap the fd to a fixed one that ++ // will be used in the child. ++ // XXX: bug 1440207 is about improving how fixed fds are used. ++ // ++ // Note: on Android, AddFdToRemap() sets up the fd to be passed via a Parcel, ++ // and the fixed fd isn't used. However, we still need to mark it for ++ // remapping so it doesn't get closed in the child. ++ mSubprocess->AddFdToRemap(shm.handle().fd, kPrefsFileDescriptor); ++#endif ++ ++ // Pass the length via a command flag. ++ extraArgs.push_back("-prefsLen"); ++ extraArgs.push_back(nsPrintfCString("%zu", uintptr_t(prefs.Length())).get()); + + // Scheduler prefs need to be handled differently because the scheduler needs + // to start up in the content process before the normal preferences service. ++ nsCString schedulerPrefs = Scheduler::GetPrefs(); + extraArgs.push_back("-schedulerPrefs"); + extraArgs.push_back(schedulerPrefs.get()); + +@@ -2061,6 +2057,7 @@ ContentParent::LaunchSubprocess(ProcessPriority aInitialPriority /* = PROCESS_PR + } + + if (!mSubprocess->LaunchAndWaitForProcessHandle(extraArgs)) { ++ NS_ERROR("failed to launch child in the parent"); + MarkAsDead(); + return false; + } +diff --git dom/ipc/ContentProcess.cpp dom/ipc/ContentProcess.cpp +index e3c1f16910c6..2441c8cb9224 100644 +--- dom/ipc/ContentProcess.cpp ++++ dom/ipc/ContentProcess.cpp +@@ -8,6 +8,8 @@ + + #include "ContentProcess.h" + #include "ContentPrefs.h" ++#include "base/shared_memory.h" ++#include "mozilla/Preferences.h" + #include "mozilla/Scheduler.h" + + #if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX) +@@ -15,7 +17,6 @@ + #endif + + #if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX) +-#include "mozilla/Preferences.h" + #include "mozilla/SandboxSettings.h" + #include "nsAppDirectoryServiceDefs.h" + #include "nsDirectoryService.h" +@@ -81,6 +82,16 @@ SetUpSandboxEnvironment() + } + #endif + ++#ifdef ANDROID ++static int gPrefsFd = -1; ++ ++void ++SetPrefsFd(int aFd) ++{ ++ gPrefsFd = aFd; ++} ++#endif ++ + bool + ContentProcess::Init(int aArgc, char* aArgv[]) + { +@@ -88,9 +99,10 @@ ContentProcess::Init(int aArgc, char* aArgv[]) + bool foundAppdir = false; + bool foundChildID = false; + bool foundIsForBrowser = false; +- bool foundIntPrefs = false; +- bool foundBoolPrefs = false; +- bool foundStringPrefs = false; ++#ifdef XP_WIN ++ bool foundPrefsHandle = false; ++#endif ++ bool foundPrefsLen = false; + bool foundSchedulerPrefs = false; + + uint64_t childID; +@@ -103,7 +115,8 @@ ContentProcess::Init(int aArgc, char* aArgv[]) + #endif + + char* schedulerPrefs = nullptr; +- InfallibleTArray<Pref> prefsArray; ++ base::SharedMemoryHandle prefsHandle = base::SharedMemory::NULLHandle(); ++ size_t prefsLen = 0; + for (int idx = aArgc; idx > 0; idx--) { + if (!aArgv[idx]) { + continue; +@@ -134,54 +147,24 @@ ContentProcess::Init(int aArgc, char* aArgv[]) + } + isForBrowser = strcmp(aArgv[idx], "-notForBrowser"); + foundIsForBrowser = true; +- } else if (!strcmp(aArgv[idx], "-intPrefs")) { +- char* str = aArgv[idx + 1]; +- while (*str) { +- int32_t index = strtol(str, &str, 10); +- MOZ_ASSERT(str[0] == ':'); +- str++; +- MaybePrefValue value(PrefValue(static_cast<int32_t>(strtol(str, &str, 10)))); +- MOZ_ASSERT(str[0] == '|'); +- str++; +- // XXX: we assume these values as default values, which may not be +- // true. We also assume they are unlocked. Fortunately, these prefs +- // get reset properly by the first IPC message. +- Pref pref(nsCString(ContentPrefs::GetEarlyPref(index)), +- /* isLocked */ false, value, MaybePrefValue()); +- prefsArray.AppendElement(pref); +- } +- foundIntPrefs = true; +- } else if (!strcmp(aArgv[idx], "-boolPrefs")) { ++#ifdef XP_WIN ++ } else if (!strcmp(aArgv[idx], "-prefsHandle")) { + char* str = aArgv[idx + 1]; +- while (*str) { +- int32_t index = strtol(str, &str, 10); +- MOZ_ASSERT(str[0] == ':'); +- str++; +- MaybePrefValue value(PrefValue(!!strtol(str, &str, 10))); +- MOZ_ASSERT(str[0] == '|'); +- str++; +- Pref pref(nsCString(ContentPrefs::GetEarlyPref(index)), +- /* isLocked */ false, value, MaybePrefValue()); +- prefsArray.AppendElement(pref); +- } +- foundBoolPrefs = true; +- } else if (!strcmp(aArgv[idx], "-stringPrefs")) { ++ MOZ_ASSERT(str[0] != '\0'); ++ // ContentParent uses %zu to print a word-sized unsigned integer. So even ++ // though strtoull() returns a long long int, it will fit in a uintptr_t. ++ prefsHandle = reinterpret_cast<HANDLE>(strtoull(str, &str, 10)); ++ MOZ_ASSERT(str[0] == '\0'); ++ foundPrefsHandle = true; ++#endif ++ } else if (!strcmp(aArgv[idx], "-prefsLen")) { + char* str = aArgv[idx + 1]; +- while (*str) { +- int32_t index = strtol(str, &str, 10); +- MOZ_ASSERT(str[0] == ':'); +- str++; +- int32_t length = strtol(str, &str, 10); +- MOZ_ASSERT(str[0] == ';'); +- str++; +- MaybePrefValue value(PrefValue(nsCString(str, length))); +- Pref pref(nsCString(ContentPrefs::GetEarlyPref(index)), +- /* isLocked */ false, value, MaybePrefValue()); +- prefsArray.AppendElement(pref); +- str += length + 1; +- MOZ_ASSERT(*(str - 1) == '|'); +- } +- foundStringPrefs = true; ++ MOZ_ASSERT(str[0] != '\0'); ++ // ContentParent uses %zu to print a word-sized unsigned integer. So even ++ // though strtoull() returns a long long int, it will fit in a uintptr_t. ++ prefsLen = strtoull(str, &str, 10); ++ MOZ_ASSERT(str[0] == '\0'); ++ foundPrefsLen = true; + } else if (!strcmp(aArgv[idx], "-schedulerPrefs")) { + schedulerPrefs = aArgv[idx + 1]; + foundSchedulerPrefs = true; +@@ -209,21 +192,43 @@ ContentProcess::Init(int aArgc, char* aArgv[]) + bool allFound = foundAppdir + && foundChildID + && foundIsForBrowser +- && foundIntPrefs +- && foundBoolPrefs +- && foundStringPrefs +- && foundSchedulerPrefs; +- ++ && foundPrefsLen ++ && foundSchedulerPrefs ++#ifdef XP_WIN ++ && foundPrefsHandle ++#endif + #if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX) +- allFound &= foundProfile; ++ && foundProfile + #endif ++ && true; + + if (allFound) { + break; + } + } + +- Preferences::SetEarlyPreferences(&prefsArray); ++#ifdef ANDROID ++ // Android is different; get the FD via gPrefsFd instead of a fixed fd. ++ MOZ_RELEASE_ASSERT(gPrefsFd != -1); ++ prefsHandle = base::FileDescriptor(gPrefsFd, /* auto_close */ true); ++#elif XP_UNIX ++ prefsHandle = base::FileDescriptor(kPrefsFileDescriptor, ++ /* auto_close */ true); ++#endif ++ ++ // Set up early prefs from the shared memory. ++ base::SharedMemory shm; ++ if (!shm.SetHandle(prefsHandle, /* read_only */ true)) { ++ NS_ERROR("failed to open shared memory in the child"); ++ return false; ++ } ++ if (!shm.Map(prefsLen)) { ++ NS_ERROR("failed to map shared memory in the child"); ++ return false; ++ } ++ Preferences::DeserializeEarlyPreferences(static_cast<char*>(shm.memory()), ++ prefsLen); ++ + Scheduler::SetPrefs(schedulerPrefs); + mContent.Init(IOThreadChild::message_loop(), + ParentPid(), +diff --git dom/ipc/ContentProcess.h dom/ipc/ContentProcess.h +index a3854c761e10..6582c94da496 100644 +--- dom/ipc/ContentProcess.h ++++ dom/ipc/ContentProcess.h +@@ -49,6 +49,11 @@ private: + DISALLOW_EVIL_CONSTRUCTORS(ContentProcess); + }; + ++#ifdef ANDROID ++// Android doesn't use -prefsHandle, it gets that FD another way. ++void SetPrefsFd(int aFd); ++#endif ++ + } // namespace dom + } // namespace mozilla + +diff --git ipc/chromium/src/base/process_util_win.cc ipc/chromium/src/base/process_util_win.cc +index 3ed54cd744ac..46667985cd71 100644 +--- ipc/chromium/src/base/process_util_win.cc ++++ ipc/chromium/src/base/process_util_win.cc +@@ -354,6 +354,10 @@ bool LaunchApp(const std::wstring& cmdline, + LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList = NULL; + std::vector<HANDLE> handlesToInherit; + for (HANDLE h : options.handles_to_inherit) { ++ if (SetHandleInformation(h, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT) == 0) { ++ MOZ_DIAGNOSTIC_ASSERT(false, "SetHandleInformation failed"); ++ return false; ++ } + handlesToInherit.push_back(h); + } + +diff --git ipc/glue/GeckoChildProcessHost.cpp ipc/glue/GeckoChildProcessHost.cpp +index d18ed9edd4ca..3be1c51d10bb 100644 +--- ipc/glue/GeckoChildProcessHost.cpp ++++ ipc/glue/GeckoChildProcessHost.cpp +@@ -1030,9 +1030,6 @@ GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExt + + if (!CrashReporter::IsDummy()) { + PROsfd h = PR_FileDesc2NativeHandle(crashAnnotationWritePipe); +-# if defined(MOZ_SANDBOX) +- mSandboxBroker.AddHandleToShare(reinterpret_cast<HANDLE>(h)); +-# endif // defined(MOZ_SANDBOX) + mLaunchOptions->handles_to_inherit.push_back(reinterpret_cast<HANDLE>(h)); + std::string hStr = std::to_string(h); + cmdLine.AppendLooseValue(UTF8ToWide(hStr)); +@@ -1043,6 +1040,11 @@ GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExt + + # if defined(MOZ_SANDBOX) + if (shouldSandboxCurrentProcess) { ++ // Mark the handles to inherit as inheritable. ++ for (HANDLE h : mLaunchOptions->handles_to_inherit) { ++ mSandboxBroker.AddHandleToShare(h); ++ } ++ + if (mSandboxBroker.LaunchApp(cmdLine.program().c_str(), + cmdLine.command_line_string().c_str(), + mLaunchOptions->env_map, +@@ -1180,7 +1182,7 @@ GeckoChildProcessHost::LaunchAndroidService(const char* type, + const base::file_handle_mapping_vector& fds_to_remap, + ProcessHandle* process_handle) + { +- MOZ_ASSERT((fds_to_remap.size() > 0) && (fds_to_remap.size() <= 3)); ++ MOZ_RELEASE_ASSERT((2 <= fds_to_remap.size()) && (fds_to_remap.size() <= 4)); + JNIEnv* const env = mozilla::jni::GetEnvForThread(); + MOZ_ASSERT(env); + +@@ -1189,21 +1191,25 @@ GeckoChildProcessHost::LaunchAndroidService(const char* type, + for (int ix = 0; ix < argvSize; ix++) { + jargs->SetElement(ix, jni::StringParam(argv[ix].c_str(), env)); + } +- base::file_handle_mapping_vector::const_iterator it = fds_to_remap.begin(); +- int32_t ipcFd = it->first; +- it++; +- // If the Crash Reporter is disabled, there will not be a second file descriptor. ++ ++ // XXX: this processing depends entirely on the internals of ++ // ContentParent::LaunchSubprocess() ++ // GeckoChildProcessHost::PerformAsyncLaunchInternal(), and the order in ++ // which they append to fds_to_remap. There must be a better way to do it. ++ // See bug 1440207. ++ int32_t prefsFd = fds_to_remap[0].first; ++ int32_t ipcFd = fds_to_remap[1].first; + int32_t crashFd = -1; + int32_t crashAnnotationFd = -1; +- if (it != fds_to_remap.end() && !CrashReporter::IsDummy()) { +- crashFd = it->first; +- it++; ++ if (fds_to_remap.size() == 3) { ++ crashAnnotationFd = fds_to_remap[2].first; + } +- if (it != fds_to_remap.end()) { +- crashAnnotationFd = it->first; +- it++; ++ if (fds_to_remap.size() == 4) { ++ crashFd = fds_to_remap[2].first; ++ crashAnnotationFd = fds_to_remap[3].first; + } +- int32_t handle = java::GeckoProcessManager::Start(type, jargs, ipcFd, crashFd, crashAnnotationFd); ++ ++ int32_t handle = java::GeckoProcessManager::Start(type, jargs, prefsFd, ipcFd, crashFd, crashAnnotationFd); + + if (process_handle) { + *process_handle = handle; +diff --git ipc/glue/GeckoChildProcessHost.h ipc/glue/GeckoChildProcessHost.h +index 631c42066bc7..0345e221abcc 100644 +--- ipc/glue/GeckoChildProcessHost.h ++++ ipc/glue/GeckoChildProcessHost.h +@@ -103,6 +103,16 @@ public: + } + #endif + ++#ifdef XP_WIN ++ void AddHandleToShare(HANDLE aHandle) { ++ mLaunchOptions->handles_to_inherit.push_back(aHandle); ++ } ++#else ++ void AddFdToRemap(int aSrcFd, int aDstFd) { ++ mLaunchOptions->fds_to_remap.push_back(std::make_pair(aSrcFd, aDstFd)); ++ } ++#endif ++ + /** + * Must run on the IO thread. Cause the OS process to exit and + * ensure its OS resources are cleaned up. +diff --git mobile/android/geckoview/src/main/aidl/org/mozilla/gecko/process/IChildProcess.aidl mobile/android/geckoview/src/main/aidl/org/mozilla/gecko/process/IChildProcess.aidl +index ba26ae1ba06b..a2535f44c72b 100644 +--- mobile/android/geckoview/src/main/aidl/org/mozilla/gecko/process/IChildProcess.aidl ++++ mobile/android/geckoview/src/main/aidl/org/mozilla/gecko/process/IChildProcess.aidl +@@ -12,6 +12,7 @@ import android.os.ParcelFileDescriptor; + interface IChildProcess { + int getPid(); + boolean start(in IProcessManager procMan, in String[] args, in Bundle extras, +- in ParcelFileDescriptor ipcPfd, in ParcelFileDescriptor crashReporterPfd, ++ in ParcelFileDescriptor prefsPfd, in ParcelFileDescriptor ipcPfd, ++ in ParcelFileDescriptor crashReporterPfd, + in ParcelFileDescriptor crashAnnotationPfd); + } +diff --git mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java +index dfabfd05daf0..8311920afeec 100644 +--- mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java ++++ mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java +@@ -128,6 +128,7 @@ public class GeckoThread extends Thread { + public static final int FLAG_PRELOAD_CHILD = 2; // Preload child during main thread start. + + private static final String EXTRA_ARGS = "args"; ++ private static final String EXTRA_PREFS_FD = "prefsFd"; + private static final String EXTRA_IPC_FD = "ipcFd"; + private static final String EXTRA_CRASH_FD = "crashFd"; + private static final String EXTRA_CRASH_ANNOTATION_FD = "crashAnnotationFd"; +@@ -149,7 +150,8 @@ public class GeckoThread extends Thread { + + private synchronized boolean init(final GeckoProfile profile, final String[] args, + final Bundle extras, final int flags, +- final int ipcFd, final int crashFd, ++ final int prefsFd, final int ipcFd, ++ final int crashFd, + final int crashAnnotationFd) { + ThreadUtils.assertOnUiThread(); + uiThreadId = android.os.Process.myTid(); +@@ -163,6 +165,7 @@ public class GeckoThread extends Thread { + mFlags = flags; + + mExtras = (extras != null) ? new Bundle(extras) : new Bundle(3); ++ mExtras.putInt(EXTRA_PREFS_FD, prefsFd); + mExtras.putInt(EXTRA_IPC_FD, ipcFd); + mExtras.putInt(EXTRA_CRASH_FD, crashFd); + mExtras.putInt(EXTRA_CRASH_ANNOTATION_FD, crashAnnotationFd); +@@ -174,15 +177,16 @@ public class GeckoThread extends Thread { + + public static boolean initMainProcess(final GeckoProfile profile, final String[] args, + final Bundle extras, final int flags) { +- return INSTANCE.init(profile, args, extras, flags, ++ return INSTANCE.init(profile, args, extras, flags, /* fd */ -1, + /* fd */ -1, /* fd */ -1, /* fd */ -1); + } + + public static boolean initChildProcess(final String[] args, final Bundle extras, +- final int ipcFd, final int crashFd, ++ final int prefsFd, final int ipcFd, ++ final int crashFd, + final int crashAnnotationFd) { + return INSTANCE.init(/* profile */ null, args, extras, /* flags */ 0, +- ipcFd, crashFd, crashAnnotationFd); ++ prefsFd, ipcFd, crashFd, crashAnnotationFd); + } + + private static boolean canUseProfile(final Context context, final GeckoProfile profile, +@@ -442,6 +446,7 @@ public class GeckoThread extends Thread { + + // And go. + GeckoLoader.nativeRun(args, ++ mExtras.getInt(EXTRA_PREFS_FD, -1), + mExtras.getInt(EXTRA_IPC_FD, -1), + mExtras.getInt(EXTRA_CRASH_FD, -1), + mExtras.getInt(EXTRA_CRASH_ANNOTATION_FD, -1)); +diff --git mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/GeckoLoader.java mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/GeckoLoader.java +index b1830fd86945..ac128b651e7b 100644 +--- mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/GeckoLoader.java ++++ mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/GeckoLoader.java +@@ -463,7 +463,7 @@ public final class GeckoLoader { + public static native boolean verifyCRCs(String apkName); + + // These methods are implemented in mozglue/android/APKOpen.cpp +- public static native void nativeRun(String[] args, int ipcFd, int crashFd, int crashAnnotationFd); ++ public static native void nativeRun(String[] args, int prefsFd, int ipcFd, int crashFd, int crashAnnotationFd); + private static native void loadGeckoLibsNative(String apkName); + private static native void loadSQLiteLibsNative(String apkName); + private static native void loadNSSLibsNative(String apkName); +diff --git mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoProcessManager.java mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoProcessManager.java +index b762e1c9a3eb..dba329ba8f92 100644 +--- mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoProcessManager.java ++++ mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoProcessManager.java +@@ -169,14 +169,14 @@ public final class GeckoProcessManager extends IProcessManager.Stub { + + @WrapForJNI + private static int start(final String type, final String[] args, +- final int ipcFd, final int crashFd, +- final int crashAnnotationFd) { +- return INSTANCE.start(type, args, ipcFd, crashFd, crashAnnotationFd, /* retry */ false); ++ final int prefsFd, final int ipcFd, ++ final int crashFd, final int crashAnnotationFd) { ++ return INSTANCE.start(type, args, prefsFd, ipcFd, crashFd, crashAnnotationFd, /* retry */ false); + } + +- private int start(final String type, final String[] args, final int ipcFd, +- final int crashFd, final int crashAnnotationFd, +- final boolean retry) { ++ private int start(final String type, final String[] args, final int prefsFd, ++ final int ipcFd, final int crashFd, ++ final int crashAnnotationFd, final boolean retry) { + final ChildConnection connection = getConnection(type); + final IChildProcess child = connection.bind(); + if (child == null) { +@@ -184,10 +184,12 @@ public final class GeckoProcessManager extends IProcessManager.Stub { + } + + final Bundle extras = GeckoThread.getActiveExtras(); ++ final ParcelFileDescriptor prefsPfd; + final ParcelFileDescriptor ipcPfd; + final ParcelFileDescriptor crashPfd; + final ParcelFileDescriptor crashAnnotationPfd; + try { ++ prefsPfd = ParcelFileDescriptor.fromFd(prefsFd); + ipcPfd = ParcelFileDescriptor.fromFd(ipcFd); + crashPfd = (crashFd >= 0) ? ParcelFileDescriptor.fromFd(crashFd) : null; + crashAnnotationPfd = (crashAnnotationFd >= 0) ? ParcelFileDescriptor.fromFd(crashAnnotationFd) : null; +@@ -198,7 +200,8 @@ public final class GeckoProcessManager extends IProcessManager.Stub { + + boolean started = false; + try { +- started = child.start(this, args, extras, ipcPfd, crashPfd, crashAnnotationPfd); ++ started = child.start(this, args, extras, prefsPfd, ipcPfd, crashPfd, ++ crashAnnotationPfd); + } catch (final RemoteException e) { + } + +@@ -209,7 +212,7 @@ public final class GeckoProcessManager extends IProcessManager.Stub { + } + Log.w(LOGTAG, "Attempting to kill running child " + type); + connection.unbind(); +- return start(type, args, ipcFd, crashFd, crashAnnotationFd, /* retry */ true); ++ return start(type, args, prefsFd, ipcFd, crashFd, crashAnnotationFd, /* retry */ true); + } + + try { +diff --git mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoServiceChildProcess.java mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoServiceChildProcess.java +index f1f6ce109fda..6dc19813fc10 100644 +--- mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoServiceChildProcess.java ++++ mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoServiceChildProcess.java +@@ -63,6 +63,7 @@ public class GeckoServiceChildProcess extends Service { + public boolean start(final IProcessManager procMan, + final String[] args, + final Bundle extras, ++ final ParcelFileDescriptor prefsPfd, + final ParcelFileDescriptor ipcPfd, + final ParcelFileDescriptor crashReporterPfd, + final ParcelFileDescriptor crashAnnotationPfd) { +@@ -74,6 +75,7 @@ public class GeckoServiceChildProcess extends Service { + sProcessManager = procMan; + } + ++ final int prefsFd = prefsPfd.detachFd(); + final int ipcFd = ipcPfd.detachFd(); + final int crashReporterFd = crashReporterPfd != null ? + crashReporterPfd.detachFd() : -1; +@@ -83,7 +85,7 @@ public class GeckoServiceChildProcess extends Service { + ThreadUtils.postToUiThread(new Runnable() { + @Override + public void run() { +- if (GeckoThread.initChildProcess(args, extras, ipcFd, crashReporterFd, ++ if (GeckoThread.initChildProcess(args, extras, prefsFd, ipcFd, crashReporterFd, + crashAnnotationFd)) { + GeckoThread.launch(); + } +diff --git modules/libpref/Preferences.cpp modules/libpref/Preferences.cpp +index 330ed4a09b54..b884591c9271 100644 +--- modules/libpref/Preferences.cpp ++++ modules/libpref/Preferences.cpp +@@ -2920,7 +2920,7 @@ public: + + } // namespace + +-// A list of prefs sent early from the parent, via the command line. ++// A list of prefs sent early from the parent, via shared memory. + static InfallibleTArray<dom::Pref>* gEarlyDomPrefs; + + /* static */ already_AddRefed<Preferences> +@@ -3081,11 +3081,130 @@ NS_IMPL_ISUPPORTS(Preferences, + nsISupportsWeakReference) + + /* static */ void +-Preferences::SetEarlyPreferences(const nsTArray<dom::Pref>* aDomPrefs) ++Preferences::SerializeEarlyPreferences(nsCString& aStr) ++{ ++ MOZ_RELEASE_ASSERT(InitStaticMembers()); ++ ++ nsAutoCStringN<256> boolPrefs, intPrefs, stringPrefs; ++ size_t numEarlyPrefs; ++ dom::ContentPrefs::GetEarlyPrefs(&numEarlyPrefs); ++ ++ for (unsigned int i = 0; i < numEarlyPrefs; i++) { ++ const char* prefName = dom::ContentPrefs::GetEarlyPref(i); ++ MOZ_ASSERT_IF(i > 0, ++ strcmp(prefName, dom::ContentPrefs::GetEarlyPref(i - 1)) > 0); ++ ++ Pref* pref = pref_HashTableLookup(prefName); ++ if (!pref || !pref->MustSendToContentProcesses()) { ++ continue; ++ } ++ ++ switch (pref->Type()) { ++ case PrefType::Bool: ++ boolPrefs.Append( ++ nsPrintfCString("%u:%d|", i, Preferences::GetBool(prefName))); ++ break; ++ case PrefType::Int: ++ intPrefs.Append( ++ nsPrintfCString("%u:%d|", i, Preferences::GetInt(prefName))); ++ break; ++ case PrefType::String: { ++ nsAutoCString value; ++ Preferences::GetCString(prefName, value); ++ stringPrefs.Append( ++ nsPrintfCString("%u:%d;%s|", i, value.Length(), value.get())); ++ } break; ++ case PrefType::None: ++ break; ++ default: ++ printf_stderr("preference type: %d\n", int(pref->Type())); ++ MOZ_CRASH(); ++ } ++ } ++ ++ aStr.Truncate(); ++ aStr.Append(boolPrefs); ++ aStr.Append('\n'); ++ aStr.Append(intPrefs); ++ aStr.Append('\n'); ++ aStr.Append(stringPrefs); ++ aStr.Append('\n'); ++ aStr.Append('\0'); ++} ++ ++/* static */ void ++Preferences::DeserializeEarlyPreferences(char* aStr, size_t aStrLen) + { + MOZ_ASSERT(!XRE_IsParentProcess()); + +- gEarlyDomPrefs = new InfallibleTArray<dom::Pref>(mozilla::Move(*aDomPrefs)); ++ MOZ_ASSERT(!gEarlyDomPrefs); ++ gEarlyDomPrefs = new InfallibleTArray<dom::Pref>(); ++ ++ char* p = aStr; ++ ++ // XXX: we assume these pref values are default values, which may not be ++ // true. We also assume they are unlocked. Fortunately, these prefs get reset ++ // properly by the first IPC message. ++ ++ // Get the bool prefs. ++ while (*p != '\n') { ++ int32_t index = strtol(p, &p, 10); ++ MOZ_ASSERT(p[0] == ':'); ++ p++; ++ int v = strtol(p, &p, 10); ++ MOZ_ASSERT(v == 0 || v == 1); ++ dom::MaybePrefValue value(dom::PrefValue(!!v)); ++ MOZ_ASSERT(p[0] == '|'); ++ p++; ++ dom::Pref pref(nsCString(dom::ContentPrefs::GetEarlyPref(index)), ++ /* isLocked */ false, ++ value, ++ dom::MaybePrefValue()); ++ gEarlyDomPrefs->AppendElement(pref); ++ } ++ p++; ++ ++ // Get the int prefs. ++ while (*p != '\n') { ++ int32_t index = strtol(p, &p, 10); ++ MOZ_ASSERT(p[0] == ':'); ++ p++; ++ dom::MaybePrefValue value( ++ dom::PrefValue(static_cast<int32_t>(strtol(p, &p, 10)))); ++ MOZ_ASSERT(p[0] == '|'); ++ p++; ++ dom::Pref pref(nsCString(dom::ContentPrefs::GetEarlyPref(index)), ++ /* isLocked */ false, ++ value, ++ dom::MaybePrefValue()); ++ gEarlyDomPrefs->AppendElement(pref); ++ } ++ p++; ++ ++ // Get the string prefs. ++ while (*p != '\n') { ++ int32_t index = strtol(p, &p, 10); ++ MOZ_ASSERT(p[0] == ':'); ++ p++; ++ int32_t length = strtol(p, &p, 10); ++ MOZ_ASSERT(p[0] == ';'); ++ p++; ++ dom::MaybePrefValue value(dom::PrefValue(nsCString(p, length))); ++ dom::Pref pref(nsCString(dom::ContentPrefs::GetEarlyPref(index)), ++ /* isLocked */ false, ++ value, ++ dom::MaybePrefValue()); ++ gEarlyDomPrefs->AppendElement(pref); ++ p += length + 1; ++ MOZ_ASSERT(*(p - 1) == '|'); ++ } ++ p++; ++ ++ MOZ_ASSERT(*p == '\0'); ++ ++ // We finished parsing on a '\0'. That should be the last char in the shared ++ // memory. ++ MOZ_ASSERT(aStr + aStrLen - 1 == p); + + #ifdef DEBUG + MOZ_ASSERT(gPhase == ContentProcessPhase::eNoPrefsSet); +@@ -4298,15 +4417,6 @@ Preferences::HasUserValue(const char* aPrefName) + return pref && pref->HasUserValue(); + } + +-/* static */ bool +-Preferences::MustSendToContentProcesses(const char* aPrefName) +-{ +- NS_ENSURE_TRUE(InitStaticMembers(), false); +- +- Pref* pref = pref_HashTableLookup(aPrefName); +- return pref && pref->MustSendToContentProcesses(); +-} +- + /* static */ int32_t + Preferences::GetType(const char* aPrefName) + { +diff --git modules/libpref/Preferences.h modules/libpref/Preferences.h +index 1cb825ecbfe5..c149db62b525 100644 +--- modules/libpref/Preferences.h ++++ modules/libpref/Preferences.h +@@ -41,6 +41,11 @@ class PrefValue; + + struct PrefsSizes; + ++#ifdef XP_UNIX ++// XXX: bug 1440207 is about improving how fixed fds such as this are used. ++static const int kPrefsFileDescriptor = 8; ++#endif ++ + // Keep this in sync with PrefType in parser/src/lib.rs. + enum class PrefValueKind : uint8_t + { +@@ -230,9 +235,6 @@ public: + // Whether the pref has a user value or not. + static bool HasUserValue(const char* aPref); + +- // Must the pref be sent to content processes when they start? +- static bool MustSendToContentProcesses(const char* aPref); +- + // Adds/Removes the observer for the root pref branch. See nsIPrefBranch.idl + // for details. + static nsresult AddStrongObserver(nsIObserver* aObserver, const char* aPref); +@@ -328,11 +330,12 @@ public: + + // When a content process is created these methods are used to pass prefs in + // bulk from the parent process. "Early" preferences are ones that are needed +- // very early on in the content process's lifetime; they are passed via the +- // command line. "Late" preferences are the remainder, which are passed via +- // IPC message. ++ // very early on in the content process's lifetime; they are passed via a ++ // special shared memory segment. "Late" preferences are the remainder, which ++ // are passed via a standard IPC message. ++ static void SerializeEarlyPreferences(nsCString& aStr); ++ static void DeserializeEarlyPreferences(char* aStr, size_t aStrLen); + static void GetPreferences(InfallibleTArray<dom::Pref>* aSettings); +- static void SetEarlyPreferences(const nsTArray<dom::Pref>* aSettings); + static void SetLatePreferences(const nsTArray<dom::Pref>* aSettings); + + // When a single pref is changed in the parent process, these methods are +diff --git mozglue/android/APKOpen.cpp mozglue/android/APKOpen.cpp +index 5f1ef55b605e..b57192488725 100644 +--- mozglue/android/APKOpen.cpp ++++ mozglue/android/APKOpen.cpp +@@ -392,7 +392,7 @@ FreeArgv(char** argv, int argc) + } + + extern "C" APKOPEN_EXPORT void MOZ_JNICALL +-Java_org_mozilla_gecko_mozglue_GeckoLoader_nativeRun(JNIEnv *jenv, jclass jc, jobjectArray jargs, int ipcFd, int crashFd, int crashAnnotationFd) ++Java_org_mozilla_gecko_mozglue_GeckoLoader_nativeRun(JNIEnv *jenv, jclass jc, jobjectArray jargs, int prefsFd, int ipcFd, int crashFd, int crashAnnotationFd) + { + int argc = 0; + char** argv = CreateArgvFromObjectArray(jenv, jargs, &argc); +@@ -407,7 +407,7 @@ Java_org_mozilla_gecko_mozglue_GeckoLoader_nativeRun(JNIEnv *jenv, jclass jc, jo + gBootstrap->GeckoStart(jenv, argv, argc, sAppData); + ElfLoader::Singleton.ExpectShutdown(true); + } else { +- gBootstrap->XRE_SetAndroidChildFds(jenv, ipcFd, crashFd, crashAnnotationFd); ++ gBootstrap->XRE_SetAndroidChildFds(jenv, prefsFd, ipcFd, crashFd, crashAnnotationFd); + gBootstrap->XRE_SetProcessType(argv[argc - 1]); + + XREChildData childData; +diff --git toolkit/xre/Bootstrap.cpp toolkit/xre/Bootstrap.cpp +index 5688519822a9..7e857969a4fb 100644 +--- toolkit/xre/Bootstrap.cpp ++++ toolkit/xre/Bootstrap.cpp +@@ -78,8 +78,8 @@ public: + ::GeckoStart(aEnv, argv, argc, aAppData); + } + +- virtual void XRE_SetAndroidChildFds(JNIEnv* aEnv, int aIPCFd, int aCrashFd, int aCrashAnnotationFd) override { +- ::XRE_SetAndroidChildFds(aEnv, aIPCFd, aCrashFd, aCrashAnnotationFd); ++ virtual void XRE_SetAndroidChildFds(JNIEnv* aEnv, int aPrefsFd, int aIPCFd, int aCrashFd, int aCrashAnnotationFd) override { ++ ::XRE_SetAndroidChildFds(aEnv, aPrefsFd, aIPCFd, aCrashFd, aCrashAnnotationFd); + } + #endif + +diff --git toolkit/xre/Bootstrap.h toolkit/xre/Bootstrap.h +index 686d0a38e324..77adcef80e1f 100644 +--- toolkit/xre/Bootstrap.h ++++ toolkit/xre/Bootstrap.h +@@ -113,7 +113,7 @@ public: + #ifdef MOZ_WIDGET_ANDROID + virtual void GeckoStart(JNIEnv* aEnv, char** argv, int argc, const StaticXREAppData& aAppData) = 0; + +- virtual void XRE_SetAndroidChildFds(JNIEnv* aEnv, int aIPCFd, int aCrashFd, int aCrashAnnotationFd) = 0; ++ virtual void XRE_SetAndroidChildFds(JNIEnv* aEnv, int aPrefsFd, int aIPCFd, int aCrashFd, int aCrashAnnotationFd) = 0; + #endif + + #ifdef LIBFUZZER +diff --git toolkit/xre/nsEmbedFunctions.cpp toolkit/xre/nsEmbedFunctions.cpp +index 53bd2bc2eb47..83184e97ba92 100644 +--- toolkit/xre/nsEmbedFunctions.cpp ++++ toolkit/xre/nsEmbedFunctions.cpp +@@ -243,9 +243,10 @@ GeckoProcessType sChildProcessType = GeckoProcessType_Default; + + #if defined(MOZ_WIDGET_ANDROID) + void +-XRE_SetAndroidChildFds (JNIEnv* env, int ipcFd, int crashFd, int crashAnnotationFd) ++XRE_SetAndroidChildFds (JNIEnv* env, int prefsFd, int ipcFd, int crashFd, int crashAnnotationFd) + { + mozilla::jni::SetGeckoThreadEnv(env); ++ mozilla::dom::SetPrefsFd(prefsFd); + IPC::Channel::SetClientChannelFd(ipcFd); + CrashReporter::SetNotificationPipeForChild(crashFd); + CrashReporter::SetCrashAnnotationPipeForChild(crashAnnotationFd); +diff --git widget/android/GeneratedJNIWrappers.cpp widget/android/GeneratedJNIWrappers.cpp +index e3f6af0cc575..4165df59f0e8 100644 +--- widget/android/GeneratedJNIWrappers.cpp ++++ widget/android/GeneratedJNIWrappers.cpp +@@ -2355,9 +2355,9 @@ constexpr char GeckoProcessManager::GetEditableParent_t::signature[]; + constexpr char GeckoProcessManager::Start_t::name[]; + constexpr char GeckoProcessManager::Start_t::signature[]; + +-auto GeckoProcessManager::Start(mozilla::jni::String::Param a0, mozilla::jni::ObjectArray::Param a1, int32_t a2, int32_t a3, int32_t a4) -> int32_t ++auto GeckoProcessManager::Start(mozilla::jni::String::Param a0, mozilla::jni::ObjectArray::Param a1, int32_t a2, int32_t a3, int32_t a4, int32_t a5) -> int32_t + { +- return mozilla::jni::Method<Start_t>::Call(GeckoProcessManager::Context(), nullptr, a0, a1, a2, a3, a4); ++ return mozilla::jni::Method<Start_t>::Call(GeckoProcessManager::Context(), nullptr, a0, a1, a2, a3, a4, a5); + } + + const char GeckoServiceChildProcess::name[] = +diff --git widget/android/GeneratedJNIWrappers.h widget/android/GeneratedJNIWrappers.h +index ece79ac94a71..228affa1e550 100644 +--- widget/android/GeneratedJNIWrappers.h ++++ widget/android/GeneratedJNIWrappers.h +@@ -6696,10 +6696,11 @@ public: + mozilla::jni::ObjectArray::Param, + int32_t, + int32_t, ++ int32_t, + int32_t> Args; + static constexpr char name[] = "start"; + static constexpr char signature[] = +- "(Ljava/lang/String;[Ljava/lang/String;III)I"; ++ "(Ljava/lang/String;[Ljava/lang/String;IIII)I"; + static const bool isStatic = true; + static const mozilla::jni::ExceptionMode exceptionMode = + mozilla::jni::ExceptionMode::ABORT; +@@ -6709,7 +6710,7 @@ public: + mozilla::jni::DispatchTarget::CURRENT; + }; + +- static auto Start(mozilla::jni::String::Param, mozilla::jni::ObjectArray::Param, int32_t, int32_t, int32_t) -> int32_t; ++ static auto Start(mozilla::jni::String::Param, mozilla::jni::ObjectArray::Param, int32_t, int32_t, int32_t, int32_t) -> int32_t; + + static const mozilla::jni::CallingThread callingThread = + mozilla::jni::CallingThread::ANY; +diff --git xpcom/build/nsXULAppAPI.h xpcom/build/nsXULAppAPI.h +index 94f6daf864c9..d6ac10d51d76 100644 +--- xpcom/build/nsXULAppAPI.h ++++ xpcom/build/nsXULAppAPI.h +@@ -398,7 +398,7 @@ XRE_API(const char*, + + #if defined(MOZ_WIDGET_ANDROID) + XRE_API(void, +- XRE_SetAndroidChildFds, (JNIEnv* env, int ipcFd, int crashFd, int crashAnnotationFd)) ++ XRE_SetAndroidChildFds, (JNIEnv* env, int prefsFd, int ipcFd, int crashFd, int crashAnnotationFd)) + #endif // defined(MOZ_WIDGET_ANDROID) + + XRE_API(void, |
