1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ProcessUtils.h"
9 #include "mozilla/Preferences.h"
10 #include "mozilla/GeckoArgs.h"
11 #include "mozilla/ipc/GeckoChildProcessHost.h"
12 #include "mozilla/UniquePtrExtensions.h"
13 #include "nsPrintfCString.h"
15 #include "XPCSelfHostedShmem.h"
20 SharedPreferenceSerializer::SharedPreferenceSerializer(
21 std::function
<bool(const char*)>&& aShouldSerializeFn
)
22 : mPrefMapSize(0), mPrefsLength(0), mShouldSerializeFn(aShouldSerializeFn
) {
23 MOZ_COUNT_CTOR(SharedPreferenceSerializer
);
26 SharedPreferenceSerializer::~SharedPreferenceSerializer() {
27 MOZ_COUNT_DTOR(SharedPreferenceSerializer
);
30 SharedPreferenceSerializer::SharedPreferenceSerializer(
31 SharedPreferenceSerializer
&& aOther
)
32 : mPrefMapSize(aOther
.mPrefMapSize
),
33 mPrefsLength(aOther
.mPrefsLength
),
34 mPrefMapHandle(std::move(aOther
.mPrefMapHandle
)),
35 mPrefsHandle(std::move(aOther
.mPrefsHandle
)) {
36 MOZ_COUNT_CTOR(SharedPreferenceSerializer
);
39 bool SharedPreferenceSerializer::SerializeToSharedMemory() {
41 Preferences::EnsureSnapshot(&mPrefMapSize
).TakePlatformHandle();
43 // Serialize the early prefs.
44 nsAutoCStringN
<1024> prefs
;
45 Preferences::SerializePreferences(prefs
, mShouldSerializeFn
);
46 mPrefsLength
= prefs
.Length();
48 base::SharedMemory shm
;
49 // Set up the shared memory.
50 if (!shm
.Create(prefs
.Length())) {
51 NS_ERROR("failed to create shared memory in the parent");
54 if (!shm
.Map(prefs
.Length())) {
55 NS_ERROR("failed to map shared memory in the parent");
59 // Copy the serialized prefs into the shared memory.
60 memcpy(static_cast<char*>(shm
.memory()), prefs
.get(), mPrefsLength
);
62 mPrefsHandle
= shm
.TakeHandle();
66 void SharedPreferenceSerializer::AddSharedPrefCmdLineArgs(
67 mozilla::ipc::GeckoChildProcessHost
& procHost
,
68 std::vector
<std::string
>& aExtraOpts
) const {
70 // Record the handle as to-be-shared, and pass it via a command flag. This
71 // works because Windows handles are system-wide.
72 procHost
.AddHandleToShare(GetPrefsHandle().get());
73 procHost
.AddHandleToShare(GetPrefMapHandle().get());
74 geckoargs::sPrefsHandle
.Put((uintptr_t)(GetPrefsHandle().get()), aExtraOpts
);
75 geckoargs::sPrefMapHandle
.Put((uintptr_t)(GetPrefMapHandle().get()),
78 // In contrast, Unix fds are per-process. So remap the fd to a fixed one that
79 // will be used in the child.
80 // XXX: bug 1440207 is about improving how fixed fds are used.
82 // Note: on Android, AddFdToRemap() sets up the fd to be passed via a Parcel,
83 // and the fixed fd isn't used. However, we still need to mark it for
84 // remapping so it doesn't get closed in the child.
85 procHost
.AddFdToRemap(GetPrefsHandle().get(), kPrefsFileDescriptor
);
86 procHost
.AddFdToRemap(GetPrefMapHandle().get(), kPrefMapFileDescriptor
);
89 // Pass the lengths via command line flags.
90 geckoargs::sPrefsLen
.Put((uintptr_t)(GetPrefsLength()), aExtraOpts
);
91 geckoargs::sPrefMapSize
.Put((uintptr_t)(GetPrefMapSize()), aExtraOpts
);
95 static int gPrefsFd
= -1;
96 static int gPrefMapFd
= -1;
98 void SetPrefsFd(int aFd
) { gPrefsFd
= aFd
; }
100 void SetPrefMapFd(int aFd
) { gPrefMapFd
= aFd
; }
103 SharedPreferenceDeserializer::SharedPreferenceDeserializer() {
104 MOZ_COUNT_CTOR(SharedPreferenceDeserializer
);
107 SharedPreferenceDeserializer::~SharedPreferenceDeserializer() {
108 MOZ_COUNT_DTOR(SharedPreferenceDeserializer
);
111 bool SharedPreferenceDeserializer::DeserializeFromSharedMemory(
112 uint64_t aPrefsHandle
, uint64_t aPrefMapHandle
, uint64_t aPrefsLen
,
113 uint64_t aPrefMapSize
) {
114 Maybe
<base::SharedMemoryHandle
> prefsHandle
;
117 prefsHandle
= Some(UniqueFileHandle(HANDLE((uintptr_t)(aPrefsHandle
))));
122 FileDescriptor::UniquePlatformHandle
handle(
123 HANDLE((uintptr_t)(aPrefMapHandle
)));
124 if (!aPrefMapHandle
) {
128 mPrefMapHandle
.emplace(std::move(handle
));
131 mPrefsLen
= Some((uintptr_t)(aPrefsLen
));
136 mPrefMapSize
= Some((uintptr_t)(aPrefMapSize
));
142 // Android is different; get the FD via gPrefsFd instead of a fixed fd.
143 MOZ_RELEASE_ASSERT(gPrefsFd
!= -1);
144 prefsHandle
= Some(UniqueFileHandle(gPrefsFd
));
146 mPrefMapHandle
.emplace(UniqueFileHandle(gPrefMapFd
));
148 prefsHandle
= Some(UniqueFileHandle(kPrefsFileDescriptor
));
150 mPrefMapHandle
.emplace(UniqueFileHandle(kPrefMapFileDescriptor
));
153 if (prefsHandle
.isNothing() || mPrefsLen
.isNothing() ||
154 mPrefMapHandle
.isNothing() || mPrefMapSize
.isNothing()) {
158 // Init the shared-memory base preference mapping first, so that only changed
159 // preferences wind up in heap memory.
160 Preferences::InitSnapshot(mPrefMapHandle
.ref(), *mPrefMapSize
);
162 // Set up early prefs from the shared memory.
163 if (!mShmem
.SetHandle(std::move(*prefsHandle
), /* read_only */ true)) {
164 NS_ERROR("failed to open shared memory in the child");
167 if (!mShmem
.Map(*mPrefsLen
)) {
168 NS_ERROR("failed to map shared memory in the child");
171 Preferences::DeserializePreferences(static_cast<char*>(mShmem
.memory()),
177 const FileDescriptor
& SharedPreferenceDeserializer::GetPrefMapHandle() const {
178 MOZ_ASSERT(mPrefMapHandle
.isSome());
180 return mPrefMapHandle
.ref();
184 // On Unix, file descriptors are per-process. This value is used when mapping
185 // a parent process handle to a content process handle.
186 static const int kJSInitFileDescriptor
= 11;
189 void ExportSharedJSInit(mozilla::ipc::GeckoChildProcessHost
& procHost
,
190 std::vector
<std::string
>& aExtraOpts
) {
192 // The code to support Android is added in a follow-up patch.
195 auto& shmem
= xpc::SelfHostedShmem::GetSingleton();
196 const mozilla::UniqueFileHandle
& uniqHandle
= shmem
.Handle();
197 size_t len
= shmem
.Content().Length();
199 // If the file is not found or the content is empty, then we would start the
200 // content process without this optimization.
201 if (!uniqHandle
|| !len
) {
205 mozilla::detail::FileHandleType handle
= uniqHandle
.get();
206 // command line: [-jsInitHandle handle] -jsInitLen length
208 // Record the handle as to-be-shared, and pass it via a command flag.
209 procHost
.AddHandleToShare(HANDLE(handle
));
210 geckoargs::sJsInitHandle
.Put((uintptr_t)(HANDLE(handle
)), aExtraOpts
);
212 // In contrast, Unix fds are per-process. So remap the fd to a fixed one that
213 // will be used in the child.
214 // XXX: bug 1440207 is about improving how fixed fds are used.
216 // Note: on Android, AddFdToRemap() sets up the fd to be passed via a Parcel,
217 // and the fixed fd isn't used. However, we still need to mark it for
218 // remapping so it doesn't get closed in the child.
219 procHost
.AddFdToRemap(handle
, kJSInitFileDescriptor
);
222 // Pass the lengths via command line flags.
223 geckoargs::sJsInitLen
.Put((uintptr_t)(len
), aExtraOpts
);
227 bool ImportSharedJSInit(uint64_t aJsInitHandle
, uint64_t aJsInitLen
) {
228 // This is an optimization, and as such we can safely recover if the command
229 // line argument are not provided.
235 if (!aJsInitHandle
) {
241 base::SharedMemoryHandle
handle(HANDLE((uintptr_t)(aJsInitHandle
)));
242 if (!aJsInitHandle
) {
247 size_t len
= (uintptr_t)(aJsInitLen
);
253 auto handle
= UniqueFileHandle(kJSInitFileDescriptor
);
256 // Initialize the shared memory with the file handle and size of the content
257 // of the self-hosted Xdr.
258 auto& shmem
= xpc::SelfHostedShmem::GetSingleton();
259 if (!shmem
.InitFromChild(std::move(handle
), len
)) {
260 NS_ERROR("failed to open shared memory in the child");
268 } // namespace mozilla