4 Before Creating a New Process
5 -----------------------------
7 Firefox started out as a one process application. Then, one became two as
8 NPAPI plugins like Flash were pushed into their own process (plugin processes)
9 for security and stability reasons. Then, it split again so that the browser
10 could also disentangle itself from web content (content processes). Then,
11 implementations on some platforms developed processes for graphics ("GPU"
12 processes). And for media codecs. And VR. And file URLs. And sockets. And
13 even more content processes. And so on...
15 Here is an incomplete list of *good* reasons we've created new processes:
17 * Separating HTML and JS from the browser makes it possible to secure the
18 browser and the rest of the system from them, even when those APIs are
20 * Browser stability was also improved by separating HTML and JS from the
21 browser, since catastrophic failures related to a tab could be limited to the
22 tab instead of crashing the browser.
23 * Site isolation requires additional processes to separate HTML and JS for
24 different sites. The separation of memory spaces undermines many types of
26 * Sandboxing processes offers great security guarantees but requires making
27 tradeoffs between power and protection. More processes means more options.
28 For example, we heavily sandbox content processes to protect from external
29 code, while the File process, which is a content process that can access
30 ``file://`` URLs, has a sandbox that is similar but allows access to local
32 * One of the benefits of the GPU process was that it improved browser
33 stability by separating a system component that had frequent stability
34 issues -- GPU drivers. The same logic inspired the NPAPI (Flash) plugin
37 Informed by this history, there is some of non-obvious preparation that you
38 should do before starting down this path. This falls under the category of
41 * **Consult the Platform and IPC teams** (#ipc) to develop the plan for the
42 way your process will integrate with the systems in which it will exist, as
43 well as how it will be handled on any platforms where it will *not* exist.
44 For example, an application's process hierarchy forms a tree where one process
45 spawns another. Currently, all processes in Firefox are spawned by the main
46 process (excepting the `launcher process`_). There is good reason for this,
47 mostly based on our sandboxing restrictions that forbid non-main processes
48 from launching new processes themselves. But it means that the main process
49 will need to know to create your process. If you make the decision to do
50 this from, say, a content process, you will need a safe, performant and
51 stable way to request this of the main process. You will also need a way to
52 efficiently communicate directly with your new process. And you will need to
53 consider limitations of some platforms (think Android) where you may not want
54 to or not be able to spawn the new process.
55 * **Consult the sandboxing team** (#hardening) to discuss what the sandbox for
56 your new process will look like. Anything that compromises security is a
57 non-starter. You may, for instance, want to create a new process to escape
58 the confines of the sandbox in a content process. This can be legitimate,
59 for example you may need access to some device API that is unavailable to a
60 content process, but the security for your new process will then have to come
61 from a different source. "I won't run Javascript" is not sufficient. Keep
62 in mind that your process will have to have some mechanism for communication
63 with other processes to be useful, so it is always a potential target.
66 Firefox has, to date, undergone exactly one occurrence of the *removal* of
67 a process type. In 2020, the NPAPI plugin process was removed when the
68 last supported plugin, Adobe's FlashPlayer, reached its end-of-life.
70 .. _launcher process: https://wiki.mozilla.org/Platform/Integration/InjectEject/Launcher_Process/
72 Firefox Process Hierarchy
73 -------------------------
75 This diagram shows the primary process types in Firefox.
80 RDD -->|PRemoteDecoderManager| Content
81 RDD(Data Decoder) ==>|PRDD| Main
85 Main ==>|PContent| Content
86 Main ==>|PSocketProcess| Socket(Network Socket)
87 Main ==>|PGMP| GMP(Gecko Media Plugins)
91 Socket -->|PSocketProcessBridge| Content
93 GPU -->|PCompositorManager| Main
94 GPU -->|PCompositorManager| Content
96 Content -->|PGMPContent| GMP
101 The main process is sometimes called the UI process, the chrome process,
102 the browser process or the parent process. This is true for documentation,
103 conversation and, most significantly, **code**. Due to the syntactic
104 overlap with IPDL actors, that last name can get pretty confusing. Less
105 commonly, the content process is called the renderer process, which is it's
106 name in Chromium code. Since the content process sandbox won't allow it,
107 Firefox never does (hardware) rendering in the content/rendering process!
109 The arrows point from the parent side to the child. Bolded arrows indicate the
110 first top-level actors for the various process types. The other arrows show
111 important actors that are usually the first connections established between the
112 two processes. These relationships difficult to discern from code. Processes
113 should clearly document their top-level connections in their IPDL files.
115 Some process types only exist on some platforms and some processes may only be
116 created on demand. For example, Mac builds do not use a GPU process but
117 instead fold the same actor connections into its main process (except ``PGPU``,
118 which it does not use). These exceptions are also very hard to learn from code
119 and should be clearly documented.
121 ``about:processes`` shows statistics for the processes in a currently running
122 browser. It is also useful to see the distribution of web pages across content
125 .. _Adding a New Type of Process:
127 Adding a New Type of Process
128 ----------------------------
130 Adding a new process type doesn't require any especially difficult steps but it
131 does require a lot of steps that are not obvious. This section will focus on
132 the steps as it builds an example. It will be light on the details of the
133 classes and protocols involved. Some implementations may need to seek out a
134 deeper understanding of the components set up here but most should instead
135 strive for simplicity.
137 In the spirit of creating a *responsible* process, the sample will connect
138 several components that any deployed Gecko process is likely to need. These
139 include configuring a sandbox, `registration with the CrashReporter service`_
140 and ("minimal") XPCOM initialization. Consult documentation for these
141 components for more information on their integration.
143 This example will be loosely based on the old (now defunct) IPDL **Extending a
144 Protocol** example for adding a new actor. We will add a command to the
145 browser's ``navigator`` JS object, ``navigator.getAssistance()``. When the
146 user enters the new command in, say, the browser's console window, it will
147 create a new process of our new **Demo** process type and ask that process for
148 "assistance" in the form of a string that it will then print to the console.
149 Once that is done, the new process will be cleanly destroyed.
151 Code for the complete demo can be found `here
152 <https://phabricator.services.mozilla.com/D119038>`_.
154 .. _registration with the CrashReporter service: `Crash Reporter`_
159 Every type of process (besides the launcher and main processes) needs two
160 classes and an actor pair to launch. This sample will be adding a process type
163 * An actor pair where the parent actor is a top-level actor in the main process
164 and the child is the (first) top-level actor in the new process. It is common
165 for this actor to simply take the name of the process type. The sample uses
166 ``PDemo``, so it creates ``DemoParent`` and ``DemoChild`` actor subclasses
167 as usual (see :ref:`IPDL: Inter-Thread and Inter-Process Message Passing`).
168 * A subclass of `GeckoChildProcessHost
169 <https://searchfox.org/mozilla-central/source/ipc/glue/GeckoChildProcessHost.h>`_
170 that exists in the main process (where new processes are created) and handles
171 most of the machinery needed for new process creation. It is common for these
172 names to be the process type plus ``ProcessParent`` or ``ProcessHost``. The
173 sample uses ``DemoParent::Host``, a private class, which keeps
174 ``GeckoChildProcessHost`` out of the **Demo** process' *public interface*
175 since it is large, complicated and mostly unimportant externally. This
176 complexity is also why it is a bad idea to add extra responsibilities to the
177 ``Host`` object that inherits it.
178 * A subclass of `ProcessChild
179 <https://searchfox.org/mozilla-central/source/ipc/glue/ProcessChild.h>`_ that
180 exists in the new process. These names are usually generated by affixing
181 ``ProcessChild`` or ``ProcessImpl`` to the type. The sample will use
182 ``DemoChild::Process``, another private class, for the same reasons it did
185 A fifth class is optional but integration with common services requires
188 * A singleton class that "manages" the collective of processes (usually the
189 Host objects) of the new type in the main process. In many instances, there
190 is at most one instance of a process type, so this becomes a singleton that
191 manages a singleton... that manages a singleton. Object ownership is often
192 hard to establish between manager objects and the hosts they manage. It is
193 wise to limit the power of these classes. This class will often get its name
194 by appending ``ProcessManager`` to the process type. The sample provides a
195 very simple manager in ``DemoParent::Manager``.
197 Finally, it is highly probable and usually desirable for the new process to
198 include another new top-level actor that represents the top-level operations
199 and communications of the new process. This actor will use the new process as
200 a child but may have any other process as the parent, unlike ``PDemo`` whose
201 parent is always the main process. This new actor will be created by the main
202 process, which creates a pair of ``Endpoint`` objects specifically for the
203 desired process pairing, and then sends those ``Endpoint`` objects to their
204 respective processes. The **Demo** example is interesting because the user can
205 issue the command from a content process or the main one, by opening the
206 console in a normal or a privileged page (e.g. ``about:sessionrestore``),
207 respectively. Supporting both of these cases will involve very little
208 additional effort. The sample will show this as part of implementing the
209 second top-level actor pair ``PDemoHelpline`` in `Connecting With Other
210 Processes`_, where the parent can be in either the main or a content process.
212 The rest of the sections will explain how to compose these classes and
213 integrate them with Gecko.
218 To begin with, look at the `geckoprocesstypes generator
219 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/xpcom/geckoprocesstypes_generator/geckoprocesstypes/__init__.py>`_
220 which adds the bones for a new process (by defining enum values and so on).
221 Some further manual intervention is still required, and you need to follow the
222 following checklists depending on your needs.
227 * Add a new entry to the `enum WebIDLProcType
228 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/dom/chrome-webidl/ChromeUtils.webidl#610-638>`_
229 * Update the `static_assert
230 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/toolkit/xre/nsAppRunner.cpp#988-990>`_
231 call checking for boundary against ``GeckoProcessType_End``
232 * Add your process to the correct ``MessageLoop::TYPE_x`` in the first
233 ``switch(XRE_GetProcessType())`` in `XRE_InitChildProcess
234 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/toolkit/xre/nsEmbedFunctions.cpp#572-590>`_.
235 You can get more information about that topic in `this comment
236 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/ipc/chromium/src/base/message_loop.h#159-187>`_
237 * Instantiate your child within the second ``switch (XRE_GetProcessType())`` in
238 `XRE_InitChildProcess
239 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/toolkit/xre/nsEmbedFunctions.cpp#615-671>`_
240 * Add a new entry ``PROCESS_TYPE_x`` in `nsIXULRuntime interface
241 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/xpcom/system/nsIXULRuntime.idl#183-196>`_
246 If you need graphics-related interaction, hack into `gfxPlatform
247 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/gfx/thebes/gfxPlatform.cpp>`_
249 - Add a call to your process manager init in ``gfxPlatform::Init()`` in
251 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/gfx/thebes/gfxPlatform.cpp#808-810>`_
252 - Add a call to your process manager shutdown in ``gfxPlatform::Shutdown()`` in
254 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/gfx/thebes/gfxPlatform.cpp#1255-1259>`_
259 You might want to talk with `#geckoview` maintainers to ensure if this is
260 required or applicable to your new process type.
262 - Add a new ``<service>`` entry against
263 ``org.mozilla.gecko.process.GeckoChildProcessServices$XXX`` in the
265 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/mobile/android/geckoview/src/main/AndroidManifest.xml#45-81>`_
266 - Add matching class inheritance from `GeckoChildProcessServices
267 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoChildProcessServices.jinja#10-13>`_
268 - Add new entry in `public enum GeckoProcessType
269 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoProcessType.java#11-23>`_
274 - Add ``InitCrashReporter`` message to the parent-side `InitCrashReporter
275 <https://searchfox.org/mozilla-central/rev/fc4d4a8d01b0e50d20c238acbb1739ccab317ebc/ipc/glue/PUtilityProcess.ipdl#30>`_
276 - Ensure your parent class inherits `public ipc::CrashReporterHelper<GeckoProcessType_Xxx>
277 <https://searchfox.org/mozilla-central/rev/fc4d4a8d01b0e50d20c238acbb1739ccab317ebc/ipc/glue/UtilityProcessParent.h#23>`_
278 - Add new ``Xxx*Status`` `annotations
279 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/toolkit/crashreporter/CrashAnnotations.yaml#968-971>`_
280 entry for your new process type description. The link here points to
281 `UtilityProcessStatus` so you can see the similar description you have to
282 write, but you might want to respect ordering in that file and put your new
283 code at the appropriate place.
284 - Add entry in `PROCESS_CRASH_SUBMIT_ATTEMPT
285 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/toolkit/components/telemetry/Histograms.json#13403-13422>`_
290 Throughout the linked code, please consider those methods more as boilerplate code that will require some trivial modification to fit your exact usecase.
292 - Add definition of memory reporter to your new :ref:`top-level actor <Top Level Actors>`
294 + Type inclusion `MemoryReportTypes <https://searchfox.org/mozilla-central/rev/fc4d4a8d01b0e50d20c238acbb1739ccab317ebc/ipc/glue/PUtilityProcess.ipdl#6>`_
295 + To parent-side `AddMemoryReport <https://searchfox.org/mozilla-central/rev/fc4d4a8d01b0e50d20c238acbb1739ccab317ebc/ipc/glue/PUtilityProcess.ipdl#32>`_
296 + To child-side `RequestMemoryReport <https://searchfox.org/mozilla-central/rev/fc4d4a8d01b0e50d20c238acbb1739ccab317ebc/ipc/glue/PUtilityProcess.ipdl#44-48>`_
298 - Add handling for your new process within `nsMemoryReporterManager::GetReportsExtended <https://searchfox.org/mozilla-central/rev/fc4d4a8d01b0e50d20c238acbb1739ccab317ebc/xpcom/base/nsMemoryReporterManager.cpp#1813-1819>`_
299 - Provide a process manager level abstraction
301 + Implement a new class deriving ``MemoryReportingProcess`` such as `UtilityMemoryReporter <https://searchfox.org/mozilla-central/rev/fc4d4a8d01b0e50d20c238acbb1739ccab317ebc/ipc/glue/UtilityProcessManager.cpp#253-292>`_
302 + Write a `GetProcessMemoryReport <https://searchfox.org/mozilla-central/rev/fc4d4a8d01b0e50d20c238acbb1739ccab317ebc/ipc/glue/UtilityProcessManager.cpp#294-300>`_
304 - On the child side, provide an implementation for `RequestMemoryReport <https://searchfox.org/mozilla-central/rev/fc4d4a8d01b0e50d20c238acbb1739ccab317ebc/ipc/glue/UtilityProcessChild.cpp#153-166>`_
307 + Provide an implementation for `RequestMemoryReport <https://searchfox.org/mozilla-central/rev/fc4d4a8d01b0e50d20c238acbb1739ccab317ebc/ipc/glue/UtilityProcessParent.cpp#41-69>`_
308 + Provide an implementation for `AddMemoryReport <https://searchfox.org/mozilla-central/rev/fc4d4a8d01b0e50d20c238acbb1739ccab317ebc/ipc/glue/UtilityProcessParent.cpp#71-77>`_
310 If you want to add a test that ensures proper behavior, you can have a look at the `utility process memory report test <https://searchfox.org/mozilla-central/rev/fc4d4a8d01b0e50d20c238acbb1739ccab317ebc/ipc/glue/test/browser/browser_utility_memoryReport.js>`_
315 Those elements will be used for exposing processes to users in some `about:`
316 pages. You might want to ping `#fluent-reviewers` to ensure if you need your
319 - Add a `user-facing localizable name
320 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/toolkit/locales/en-US/toolkit/global/processTypes.ftl#39-57>`_
321 for your process, if needed
322 - Hashmap from process type to user-facing string above in `const ProcessType
323 <https://searchfox.org/mozilla-central/rev/c5c002f81f08a73e04868e0c2bf0eb113f200b03/toolkit/modules/ProcessType.sys.mjs#10-16`_
324 - For `about:processes` you will probably want to follow the following steps:
326 + Add handling for your new process type producing a unique `fluentName <https://searchfox.org/mozilla-central/rev/be4604e4be8c71b3c1dbff2398a5b05f15411673/toolkit/components/aboutprocesses/content/aboutProcesses.js#472-539>`_, i.e., constructing a dynamic name is highly discouraged
327 + Add matching localization strings within `fluent localization file <https://searchfox.org/mozilla-central/rev/be4604e4be8c71b3c1dbff2398a5b05f15411673/toolkit/locales/en-US/toolkit/about/aboutProcesses.ftl#35-55>`_
332 - Add definition of ``PProfiler`` to your new IPDL
334 + Type inclusion `protocol PProfiler <https://searchfox.org/mozilla-central/rev/fc4d4a8d01b0e50d20c238acbb1739ccab317ebc/ipc/glue/PUtilityProcess.ipdl#9>`_
335 + Child-side `InitProfiler <https://searchfox.org/mozilla-central/rev/fc4d4a8d01b0e50d20c238acbb1739ccab317ebc/ipc/glue/PUtilityProcess.ipdl#42>`_
337 - Make sure your initialization path contains a `SendInitProfiler <https://searchfox.org/mozilla-central/rev/fc4d4a8d01b0e50d20c238acbb1739ccab317ebc/ipc/glue/UtilityProcessHost.cpp#222-223>`_. You will want to perform the call once a ``OnChannelConnected`` is issued, thus ensuring your new process is connected to IPC.
338 - Provide an implementation for `InitProfiler <https://searchfox.org/mozilla-central/rev/fc4d4a8d01b0e50d20c238acbb1739ccab317ebc/ipc/glue/UtilityProcessChild.cpp#147-151>`_
340 - You will probably want to make sure your child process code register within the profiler a proper name, otherwise it will default to ``GeckoMain`` ; this can be done by issuing ``profiler_set_process_name(nsCString("XxX"))`` on the child init side.
345 The amount of changes required here are significant, `Bug 1740485: Improve
346 StaticComponents code generation
347 <https://bugzilla.mozilla.org/show_bug.cgi?id=1740485>`_ tracks improving that.
349 - Update allowance in those configuration files to match new process selector
350 that includes your new process. When exploring those components definitions,
351 keep in mind that you are looking at updating `processes` field in the
352 `Classes` object. The `ProcessSelector` value will come from what the reader
353 writes based on the instructions below. Some of these also contains several
354 services, so you might have to ensure you have all your bases covered. Some of
355 the components might not need to be updated as well.
357 + `libpref <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/modules/libpref/components.conf>`_
358 + `telemetry <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/toolkit/components/telemetry/core/components.conf>`_
359 + `android <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/widget/android/components.conf>`_
360 + `gtk <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/widget/gtk/components.conf>`_
361 + `windows <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/widget/windows/components.conf>`_
362 + `base <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/xpcom/base/components.conf>`_
363 + `components <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/xpcom/components/components.conf>`_
364 + `ds <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/xpcom/ds/components.conf>`_
365 + `threads <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/xpcom/threads/components.conf>`_
366 + `cocoa kWidgetModule <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/widget/cocoa/nsWidgetFactory.mm#194-202>`_
367 + `build <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/xpcom/build/components.conf>`_
368 + `XPCOMinit kXPCOMModule <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/xpcom/build/XPCOMInit.cpp#172-180>`_
370 - Within `static components generator
371 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/xpcom/components/gen_static_components.py>`_
373 + Add new definition in ``ProcessSelector`` for your new process
374 ``ALLOW_IN_x_PROCESS = 0x..``
375 + Add new process selector masks including your new process definition
376 + Also add those into the ``PROCESSES`` structure
378 - Within `module definition <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/xpcom/components/Module.h>`_
380 + Add new definition in ``enum ProcessSelector``
381 + Add new process selector mask including the new definition
382 + Update ``kMaxProcessSelector``
384 - Within `nsComponentManager <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/xpcom/components/nsComponentManager.cpp>`_
386 + Add new selector match in ``ProcessSelectorMatches`` for your new process
388 + Add new process selector for ``gProcessMatchTable`` in
389 ``nsComponentManagerImpl::Init()``
394 - Ensure your new IPDL includes on the child side
397 <https://searchfox.org/mozilla-central/rev/fc4d4a8d01b0e50d20c238acbb1739ccab317ebc/ipc/glue/PUtilityProcess.ipdl#55>`_
398 + `TestTriggerMetrics
399 <https://searchfox.org/mozilla-central/rev/fc4d4a8d01b0e50d20c238acbb1739ccab317ebc/ipc/glue/PUtilityProcess.ipdl#60>`_
401 - Provide a parent-side implementation for `FOGData
402 <https://searchfox.org/mozilla-central/rev/fc4d4a8d01b0e50d20c238acbb1739ccab317ebc/ipc/glue/UtilityProcessParent.cpp#79-82>`_
403 - Provide a child-side implementation for `FlushFOGData
404 <https://searchfox.org/mozilla-central/rev/fc4d4a8d01b0e50d20c238acbb1739ccab317ebc/ipc/glue/UtilityProcessChild.cpp#179-183>`_
405 - Child-side should flush its FOG data at IPC `ActorDestroy
406 <https://searchfox.org/mozilla-central/rev/fc4d4a8d01b0e50d20c238acbb1739ccab317ebc/ipc/glue/UtilityProcessChild.cpp#199-201>`_
407 - Child-side `test metrics
408 <https://searchfox.org/mozilla-central/rev/fc4d4a8d01b0e50d20c238acbb1739ccab317ebc/ipc/glue/UtilityProcessChild.cpp#185-191>`_
410 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/toolkit/components/glean/ipc/FOGIPC.cpp>`_
412 + Add handling of your new process type within ``FlushAllChildData()`` `here
413 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/toolkit/components/glean/ipc/FOGIPC.cpp#106-121>`_
414 and ``SendFOGData()`` `here
415 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/toolkit/components/glean/ipc/FOGIPC.cpp#165-182>`_
416 + Add support for sending test metrics in ``TestTriggerMetrics()`` `here
417 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/toolkit/components/glean/ipc/FOGIPC.cpp#208-232>`_
419 - Handle process shutdown in ``register_process_shutdown()`` of `glean
420 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/toolkit/components/glean/api/src/ipc.rs>`_
425 - Ensure your new IPDL includes on the child side
427 + `GetUntrustedModulesData
428 <https://searchfox.org/mozilla-central/rev/2ce39261ea6a69e49d87f76a119494b2a7a7e42a/ipc/glue/PUtilityProcess.ipdl#106>`_
429 + `UnblockUntrustedModulesThread
430 <https://searchfox.org/mozilla-central/rev/2ce39261ea6a69e49d87f76a119494b2a7a7e42a/ipc/glue/PUtilityProcess.ipdl#113>`_
432 - Provide a parent side implementation for both
434 - Add handling of your new process type in ``MultiGetUntrustedModulesData::GetUntrustedModuleLoadEvents()`` `here <https://searchfox.org/mozilla-central/rev/2ce39261ea6a69e49d87f76a119494b2a7a7e42a/toolkit/components/telemetry/other/UntrustedModules.cpp#145-151>`_
436 - `Update your IPDL <https://searchfox.org/mozilla-central/rev/2ce39261ea6a69e49d87f76a119494b2a7a7e42a/ipc/glue/PUtilityProcess.ipdl#75>`_ and make sure your ``Init()`` can receive a boolean for
437 ``isReadyForBackgroundProcessing`` `like here <https://searchfox.org/mozilla-central/rev/2ce39261ea6a69e49d87f76a119494b2a7a7e42a/ipc/glue/UtilityProcessChild.cpp#157-160>`_, then within the child's ``RecvInit()``
438 make sure a call to ``DllServices``'s ``StartUntrustedModulesProcessor()`` `is
439 performed <https://searchfox.org/mozilla-central/rev/2ce39261ea6a69e49d87f76a119494b2a7a7e42a/ipc/glue/UtilityProcessChild.cpp#185-186>`_.
441 - Ensure your new IPDL includes for the parent side
443 + `GetModulesTrust <https://searchfox.org/mozilla-central/rev/2ce39261ea6a69e49d87f76a119494b2a7a7e42a/ipc/glue/PUtilityProcess.ipdl#60-61>`_
445 - Provide an implementation on the `parent side <https://searchfox.org/mozilla-central/rev/2ce39261ea6a69e49d87f76a119494b2a7a7e42a/ipc/glue/UtilityProcessParent.cpp#69-81>`_
447 - Expose your new process type as supported in ``UntrustedModulesProcessor::IsSupportedProcessType()`` `like others <https://searchfox.org/mozilla-central/rev/2ce39261ea6a69e49d87f76a119494b2a7a7e42a/toolkit/xre/dllservices/UntrustedModulesProcessor.cpp#76-91>`_
449 - Update ``UntrustedModulesProcessor::SendGetModulesTrust()`` to call `your new child process <https://searchfox.org/mozilla-central/rev/2ce39261ea6a69e49d87f76a119494b2a7a7e42a/toolkit/xre/dllservices/UntrustedModulesProcessor.cpp#757-761>`_
454 Sandboxing changes related to a new process can be non-trivial, so it is
455 strongly advised that you reach to the Sandboxing team in ``#hardening`` to
456 discuss your needs prior to making changes.
461 Linux sandboxing mostly works by allowing / blocking system calls for child
462 process and redirecting (brokering) some from the child to the parent. Rules
463 are written in a specific DSL: `BPF
464 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/chromium/sandbox/linux/bpf_dsl/bpf_dsl.h#21-72>`_.
466 - Add new ``SetXXXSandbox()`` function within `linux sandbox
467 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/linux/Sandbox.cpp#719-748>`_
468 - Within `sandbox filter
469 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/linux/SandboxFilter.cpp>`_
471 + Add new helper ``GetXXXSandboxPolicy()`` `like this one
472 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/linux/SandboxFilter.cpp#2036-2040>`_
473 called by ``SetXXXSandbox()``
474 + Derive new class `similar to this
475 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/linux/SandboxFilter.cpp#2000-2034>`_
476 inheriting ``SandboxPolicyCommon`` or ``SandboxPolicyBase`` and defining
477 the sandboxing policy
479 - Add new ``SandboxBrokerPolicyFactory::GetXXXProcessPolicy()`` in `sandbox
481 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp#881-932>`_
482 - Add new case handling in ``GetEffectiveSandboxLevel()`` in `sandbox launch
483 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/linux/launch/SandboxLaunch.cpp#243-271>`_
484 - Add new entry in ``enum class ProcType`` of `sandbox reporter header
485 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/linux/reporter/SandboxReporterCommon.h#32-39>`_
486 - Add new case handling in ``SubmitToTelemetry()`` in `sandbox reporter
487 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/linux/reporter/SandboxReporter.cpp#131-152>`_
488 - Add new case handling in ``SandboxReportWrapper::GetProcType()`` of `sandbox
490 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/linux/reporter/SandboxReporterWrappers.cpp#69-91>`_
495 - Add new case handling in ``GeckoChildProcessHost::StartMacSandbox()`` of
496 `GeckoChildProcessHost <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/ipc/glue/GeckoChildProcessHost.cpp#1720-1743>`_
497 - Add new entry in ``enum MacSandboxType`` defined in `macOS sandbox header
498 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/mac/Sandbox.h#12-20>`_
499 - Within `macOS sandbox core
500 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/mac/Sandbox.mm>`_
501 handle the new ``MacSandboxType`` in
503 + ``MacSandboxInfo::AppendAsParams()`` in the `switch statement
504 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/mac/Sandbox.mm#164-188>`_
505 + ``StartMacSandbox()`` in the `serie of if/else statements
506 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/mac/Sandbox.mm#286-436>`_.
507 This code sets template values for the sandbox string rendering, and is
508 running on the side of the main process.
509 + ``StartMacSandboxIfEnabled()`` in this `switch statement
510 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/mac/Sandbox.mm#753-782>`_.
511 You might also need a ``GetXXXSandboxParamsFromArgs()`` that performs CLI
512 parsing on behalf of ``StartMacSandbox()``.
514 - Create the new sandbox definition file
515 ``security/sandbox/mac/SandboxPolicy<XXX>.h`` for your new process ``<XXX>``,
516 and make it exposed in the ``EXPORTS.mozilla`` section of `moz.build
517 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/mac/moz.build#7-13>`_.
518 Those rules follows a specific Scheme-like language. You can learn more about
519 it in `Apple Sandbox Guide
520 <https://reverse.put.as/wp-content/uploads/2011/09/Apple-Sandbox-Guide-v1.0.pdf>`_
521 as well as on your system within ``/System/Library/Sandbox/Profiles/``.
526 - Introduce a new ``SandboxBroker::SetSecurityLevelForXXXProcess()`` that
527 defines the new sandbox in both
529 + the sandbox broker basing yourself on that `example
530 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp#1241-1344>`_
531 + the remote sandbox broker getting `inspired by
532 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/win/src/remotesandboxbroker/remoteSandboxBroker.cpp#161-165>`_
534 - Add new case handling in ``WindowsProcessLauncher::DoSetup()`` calling
535 ``SandboxBroker::SetSecurityLevelForXXXProcess()`` in `GeckoChildProcessHost
536 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/ipc/glue/GeckoChildProcessHost.cpp#1391-1470>`_.
537 This will apply actual sandboxing rules to your process.
542 - New process' first top level actor needs to `include PSandboxTesting
543 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/common/test/PSandboxTesting.ipdl>`_
544 and implement ``RecvInitSandboxTesting`` `like there
545 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/ipc/glue/UtilityProcessChild.cpp#165-174>`_.
546 - Add your new process ``string_name`` in the ``processTypes`` list of `sandbox
547 tests <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/test/browser_sandbox_test.js#17>`_
548 - Add a new case in ``SandboxTest::StartTests()`` in `test core
549 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/common/test/SandboxTest.cpp#100-232>`_
550 to handle your new process
551 - Add a new if branch for your new process in ``SandboxTestingChild::Bind()``
553 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/common/test/SandboxTestingChild.cpp#68-96>`_
554 - Add a new ``RunTestsXXX`` function for your new process (called by ``Bind()``
555 above) `similar to that implementation
556 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/common/test/SandboxTestingChildTests.h#333-363>`_
558 Creating the New Process
559 ~~~~~~~~~~~~~~~~~~~~~~~~
561 The sample does this in ``DemoParent::LaunchDemoProcess``. The core
562 behavior is fairly clear:
567 bool DemoParent::LaunchDemoProcess(
568 base::ProcessId aParentPid, LaunchDemoProcessResolver&& aResolver) {
569 UniqueHost host(new Host(aParentPid, std::move(aResolver)));
571 // Prepare "command line" startup args for new process
572 std::vector<std::string> extraArgs;
573 if (!host->BuildProcessArgs(&extraArgs)) {
577 // Async launch creates a promise that we use below.
578 if (!host->AsyncLaunch(extraArgs)) {
582 host->WhenProcessHandleReady()->Then(
583 GetCurrentSerialEventTarget(), __func__,
584 [host = std::move(host)](
585 const ipc::ProcessHandlePromise::ResolveOrRejectValue&
587 if (aResult.IsReject()) {
588 host->ResolveAsFailure();
592 auto actor = MakeRefPtr<DemoParent>(std::move(host));
597 First, it creates an object of our ``GeckoChildProcessHost`` subclass (storing
598 some stuff for later). ``GeckoChildProcessHost`` is a base class that
599 abstracts the system-level operations involved in launching the new process.
600 It is the most substantive part of the launch procedure. After its
601 construction, the code prepares a bunch of strings to pass on the "command
602 line", which is the only way to pass data to the new process before IPDL is
603 established. All new processes will at least include ``-parentBuildId`` for
604 validating that dynamic libraries are properly versioned, and shared memory for
605 passing user preferences, which can affect early process behavior. Finally, it
606 tells ``GeckoChildProcessHost`` to asynchronously launch the process and run
607 the given lambda when it has a result. The lambda creates ``DemoParent`` with
608 the new host, if successful.
610 In this sample, the ``DemoParent`` is owned (in the reference-counting sense)
611 by IPDL, which is why it doesn't get assigned to anything. This simplifies the
612 design dramatically. IPDL takes ownership when the actor calls ``Bind`` from
617 DemoParent::DemoParent(UniqueHost&& aHost)
618 : mHost(std::move(aHost)) {}
621 mHost->TakeInitialEndpoint().Bind(this);
623 mHost->MakeBridgeAndResolve();
626 After the ``Bind`` call, the actor is live and communication with the new
627 process can begin. The constructor concludes by initiating the process of
628 connecting the ``PDemoHelpline`` actors; ``Host::MakeBridgeAndResolve`` will be
629 covered in `Creating a New Top Level Actor`_. However, before we get into
630 that, we should finish defining the lifecycle of the process. In the next
631 section we look at launching the new process from the new process' perspective.
634 The code could have chosen to create a ``DemoChild`` instead of a
635 ``DemoParent`` and the choice may seem cosmetic but it has substantial
636 implications that could affect browser stability. The most
637 significant is that the prohibitibition on synchronous IPDL messages going
638 from parent to child can no longer guarantee freedom from multiprocess
641 Initializing the New Process
642 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
644 The new process first adopts the **Demo** process type in
645 ``XRE_InitChildProcess``, where it responds to the **Demo** values we added to
646 some enums above. Specifically, we need to choose the type of MessageLoop our
647 main thread will run (this is discussed later) and we need to create our
648 ``ProcessChild`` subclass. This is not an insignificant choice so pay close
649 attention to the `MessageLoop` options:
653 MessageLoop::Type uiLoopType;
654 switch (XRE_GetProcessType()) {
655 case GeckoProcessType_Demo:
656 uiLoopType = MessageLoop::TYPE_MOZILLA_CHILD; break;
662 UniquePtr<ProcessChild> process;
663 switch (XRE_GetProcessType()) {
665 case GeckoProcessType_Demo:
666 process = MakeUnique<DemoChild::Process>(parentPID);
670 We then need to create our singleton ``DemoChild`` object, which can occur in
671 the constructor or the ``Process::Init()`` call, which is common. We store a
672 strong reference to the actor (as does IPDL) so that we are guaranteed that it
673 exists as long as the ``ProcessChild`` does -- although the message channel may
674 be closed. We will release the reference either when the process is properly
675 shutting down or when an IPC error closes the channel.
677 ``Init`` is given the command line arguments constructed above so it will need
678 to be overridden to parse them. It does this, binds our actor by
679 calling ``Bind`` as was done with the parent, then initializes a bunch of
680 components that the process expects to use:
684 bool DemoChild::Init(int aArgc, char* aArgv[]) {
685 #if defined(MOZ_SANDBOX) && defined(XP_WIN)
686 mozilla::SandboxTarget::Instance()->StartSandbox();
687 #elif defined(__OpenBSD__) && defined(MOZ_SANDBOX)
688 StartOpenBSDSandbox(GeckoProcessType_Demo);
691 if (!mozilla::ipc::ProcessChild::InitPrefs(aArgc, aArgv)) {
695 if (NS_WARN_IF(NS_FAILED(nsThreadManager::get().Init()))) {
699 if (NS_WARN_IF(!TakeInitialEndpoint().Bind(this))) {
703 // ... initializing components ...
705 if (NS_FAILED(NS_InitMinimalXPCOM())) {
712 This is a slimmed down version of the real ``Init`` method. We see that it
713 establishes a sandbox (more on this later) and then reads the command line and
714 preferences that we sent from the main process. It then initializes the thread
715 manager, which is required by for the subsequent ``Bind`` call.
717 Among the list of components we initialize in the sample code, XPCOM is
718 special. XPCOM includes a suite of components, including the component
719 manager, and is usually required for serious Gecko development. It is also
720 heavyweight and should be avoided if possible. We will leave the details of
721 XPCOM development to that module but we mention XPCOM configuration that is
722 special to new processes, namely ``ProcessSelector``. ``ProcessSelector``
723 is used to determine what process types have access to what XPCOM components.
724 By default, a process has access to none. The code adds enums for selecting
725 a subset of process types, like
726 ``ALLOW_IN_GPU_RDD_VR_SOCKET_UTILITY_AND_DEMO_PROCESS``, to the
727 ``ProcessSelector`` enum in `gen_static_components.py
728 <https://searchfox.org/mozilla-central/source/xpcom/components/gen_static_components.py>`_
730 <https://searchfox.org/mozilla-central/source/xpcom/components/Module.h>`_.
731 It then updates the selectors in various ``components.conf`` files and
732 hardcoded spots like ``nsComponentManager.cpp`` to add the **Demo** processes
733 to the list that can use them. Some modules are required to bootstrap XPCOM
734 and will cause it to fail to initialize if they are not permitted.
736 At this point, the new process is idle, waiting for messages from the main
737 process that will start the ``PDemoHelpline`` actor. We discuss that in
738 `Creating a New Top Level Actor`_ below but, first, let's look at how the main
739 and **Demo** processes will handle clean destruction.
741 Destroying the New Process
742 ~~~~~~~~~~~~~~~~~~~~~~~~~~
744 Gecko processes have a clean way for clients to request that they shutdown.
745 Simply calling ``Close()`` on the top level actor at either endpoint will begin
746 the shutdown procedure (so, ``PDemoParent::Close`` or ``PDemoChild::Close``).
747 The only other way for a child process to terminate is to crash. Each of these
748 three options requires some special handling.
751 There is no need to consider the case where the parent (main) process
752 crashed, because the **Demo** process would be quickly terminated by Gecko.
754 In cases where ``Close()`` is called, the shutdown procedure is fairly
755 straightforward. Once the call completes, the actor is no longer connected to
756 a channel -- messages will not be sent or received, as is the case with any
757 normal top-level actor (or any managed actor after calling
758 ``Send__delete__()``). In the sample code, we ``Close`` the ``DemoChild``
759 when some (as yet unwritten) **Demo** process code calls
760 ``DemoChild::Shutdown``.
765 void DemoChild::Shutdown() {
767 // Wait for the other end to get everything we sent before shutting down.
768 // We never want to Close during a message (response) handler, so
769 // we dispatch a new runnable.
770 auto dc = gDemoChild;
771 RefPtr<nsIRunnable> runnable = NS_NewRunnableFunction(
772 "DemoChild::FinishShutdown",
773 [dc2 = std::move(gDemoChild)]() { dc2->Close(); });
774 dc->SendEmptyMessageQueue(
775 [runnable](bool) { NS_DispatchToMainThread(runnable); },
776 [runnable](mozilla::ipc::ResponseRejectReason) {
777 NS_DispatchToMainThread(runnable);
782 The comment in the code makes two important points:
784 * ``Close`` should never be called from a message handler (e.g. in a
785 ``RecvFoo`` method). We schedule it to run later.
786 * If the ``DemoParent`` hasn't finished handling messages the ``DemoChild``
787 sent, or vice-versa, those messages will be lost. For that reason, we have a
788 trivial sentinel message ``EmptyMessageQueue`` that we simply send and wait
789 to respond before we ``Close``. This guarantees that the main process will
790 have handled all of the messages we sent before it. Because we know the
791 details of the ``PDemo`` protocol, we know that this means we won't lose any
792 important messages this way. Note that we say "important" messages because
793 we could still lose messages sent *from* the main process. For example, a
794 ``RequestMemoryReport`` message sent by the MemoryReporter could be lost.
795 The actor would need a more complex shutdown protocol to catch all of these
796 messages but in our case there would be no point. A process that is
797 terminating is probably not going to produce useful memory consumption data.
798 Those messages can safely be lost.
800 `Debugging Process Startup`_ looks at what happens if we omit the
801 ``EmptyMessageQueue`` message.
803 We can also see that, once the ``EmptyMessageQueue`` response is run, we are
804 releasing ``gDemoChild``, which will result in the termination of the process.
808 DemoChild::~DemoChild() {
810 XRE_ShutdownChildProcess();
813 At this point, the ``DemoParent`` in the main process is alerted to the
814 channel closure because IPDL will call its :ref:`ActorDestroy <Actor Lifetimes
819 void DemoParent::ActorDestroy(ActorDestroyReason aWhy) {
820 if (aWhy == AbnormalShutdown) {
821 GenerateCrashReport(OtherPid());
826 IPDL then releases its (sole) reference to ``DemoParent`` and the destruction
827 of the process apparatus is complete.
829 The ``ActorDestroy`` code shows how we handle the one remaining shutdown case:
830 a crash in the **Demo** process. In this case, IPDL will *detect* the dead
831 process and free the ``DemoParent`` actor as above, only with an
832 ``AbnormalShutdown`` reason. We generate a crash report, which requires crash
833 reporter integration, but no additional "special" steps need to be taken.
835 Creating a New Top Level Actor
836 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
838 We now have a framework that creates the new process and connects it to the
839 main process. We now want to make another top-level actor but this one will be
840 responsible for our intended behavior, not just bootstrapping the new process.
841 Above, we saw that this is started by ``Host::MakeBridgeAndResolve`` after the
842 ``DemoParent`` connection is established.
846 bool DemoParent::Host::MakeBridgeAndResolve() {
847 ipc::Endpoint<PDemoHelplineParent> parent;
848 ipc::Endpoint<PDemoHelplineChild> child;
850 auto resolveFail = MakeScopeExit([&] { mResolver(Nothing()); });
852 // Parent side is first PID (main/content), child is second (demo).
853 nsresult rv = PDempHelpline::CreateEndpoints(
854 mParentPid, base::GetProcId(GetChildProcessHandle()), &parent, &child);
858 if (!mActor->SendCreateDemoHelplineChild(std::move(child))) {
859 NS_WARNING("Failed to SendCreateDemoHelplineChild");
863 resolveFail.release();
864 mResolver(Some(std::move(parent)));
868 Because the operation of launching a process is asynchronous, we have
869 configured this so that it creates the two endpoints for the new top-level
870 actors, then we send the child one to the new process and resolve a promise
871 with the other. The **Demo** process creates its ``PDemoHelplineChild``
876 mozilla::ipc::IPCResult DemoChild::RecvCreateDemoHelplineChild(
877 Endpoint<PDemoHelplineChild>&& aEndpoint) {
878 mDemoHelplineChild = new DemoHelplineChild();
879 if (!aEndpoint.Bind(mDemoHelplineChild)) {
880 return IPC_FAIL(this, "Unable to bind DemoHelplineChild");
885 ``MakeProcessAndGetAssistance`` binds the same way:
889 RefPtr<DemoHelplineParent> demoHelplineParent = new DemoHelplineParent();
890 if (!endpoint.Bind(demoHelplineParent)) {
891 NS_WARNING("Unable to bind DemoHelplineParent");
896 However, the parent may be in the main process or in content. We handle both
897 cases in the next section.
899 .. _Connecting With Other Processes:
901 Connecting With Other Processes
902 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
904 ``DemoHelplineParent::MakeProcessAndGetAssistance`` is the method that we run
905 from either the main or the content process and that should kick off the
906 procedure that will result in sending a string (that we get from a new **Demo**
907 process) to a DOM promise. It starts by constructing a different promise --
908 one like the ``mResolver`` in ``Host::MakeBridgeAndResolve`` in the last
909 section that produced a ``Maybe<Endpoint<PDemoHelplineParent>>``. In the main
910 process, we just make the promise ourselves and call
911 ``DemoParent::LaunchDemoProcess`` to start the procedure that will result in
912 it being resolved as already described. If we are calling from the content
913 process, we simply write an async ``PContent`` message that calls
914 ``DemoParent::LaunchDemoProcess`` and use the message handler's promise as
920 bool DemoHelplineParent::MakeProcessAndGetAssistance(
921 RefPtr<mozilla::dom::Promise> aPromise) {
922 RefPtr<LaunchDemoProcessPromise> resolver;
924 if (XRE_IsContentProcess()) {
925 auto* contentChild = mozilla::dom::ContentChild::GetSingleton();
926 MOZ_ASSERT(contentChild);
928 resolver = contentChild->SendLaunchDemoProcess();
930 MOZ_ASSERT(XRE_IsParentProcess());
931 auto promise = MakeRefPtr<LaunchDemoProcessPromise::Private>(__func__);
934 if (!DemoParent::LaunchDemoProcess(
935 base::GetCurrentProcId(),
936 [promise = std::move(promise)](
937 Maybe<Endpoint<PDemoHelplineParent>>&& aMaybeEndpoint) mutable {
938 promise->Resolve(std::move(aMaybeEndpoint), __func__);
940 NS_WARNING("Failed to launch Demo process");
941 resolver->Reject(NS_ERROR_FAILURE);
947 GetMainThreadSerialEventTarget(), __func__,
948 [aPromise](Maybe<Endpoint<PDemoHelplineParent>>&& maybeEndpoint) mutable {
949 if (!maybeEndpoint) {
950 aPromise->MaybeReject(NS_ERROR_FAILURE);
954 RefPtr<DemoHelplineParent> demoHelplineParent = new DemoHelplineParent();
955 Endpoint<PDemoHelplineParent> endpoint = maybeEndpoint.extract();
956 if (!endpoint.Bind(demoHelplineParent)) {
957 NS_WARNING("Unable to bind DemoHelplineParent");
962 // ... communicate with PDemoHelpline and write message to console ...
964 [aPromise](mozilla::ipc::ResponseRejectReason&& aReason) {
965 aPromise->MaybeReject(NS_ERROR_FAILURE);
971 mozilla::ipc::IPCResult ContentParent::RecvLaunchDemoProcess(
972 LaunchDemoProcessResolver&& aResolver) {
973 if (!DemoParent::LaunchDemoProcess(OtherPid(),
974 std::move(aResolver))) {
975 NS_WARNING("Failed to launch Demo process");
980 To summarize, connecting processes always requires endpoints to be constructed
981 by the main process, even when neither process being connected is the main
982 process. It is the only process that creates ``Endpoint`` objects. From that
983 point, connecting is just a matter of sending the endpoints to the right
984 processes, constructing an actor for them, and then calling ``Endpoint::Bind``.
986 Completing the Sample
987 ~~~~~~~~~~~~~~~~~~~~~
989 We have covered the main parts needed for the sample. Now we just need to wire
990 it all up. First, we add the new JS command to ``Navigator.webidl`` and
991 ``Navigator.h``/``Navigator.cpp``:
995 partial interface Navigator {
997 Promise<DOMString> getAssistance();
1000 already_AddRefed<Promise> Navigator::GetAssistance(ErrorResult& aRv) {
1001 if (!mWindow || !mWindow->GetDocShell()) {
1002 aRv.Throw(NS_ERROR_UNEXPECTED);
1006 RefPtr<Promise> echoPromise = Promise::Create(mWindow->AsGlobal(), aRv);
1007 if (NS_WARN_IF(aRv.Failed())) {
1011 if (!DemoHelplineParent::MakeProcessAndGetAssistance(echoPromise)) {
1012 aRv.Throw(NS_ERROR_FAILURE);
1016 return echoPromise.forget();
1019 Then, we need to add the part that gets the string we use to resolve the
1020 promise in ``MakeProcessAndGetAssistance`` (or reject it if it hasn't been
1021 resolved by the time ``ActorDestroy`` is called):
1025 using DemoPromise = MozPromise<nsString, nsresult, true>;
1028 bool DemoHelplineParent::MakeProcessAndGetAssistance(
1029 RefPtr<mozilla::dom::Promise> aPromise) {
1031 // ... construct and connect demoHelplineParent ...
1033 RefPtr<DemoPromise> promise = demoHelplineParent->mPromise.Ensure(__func__);
1035 GetMainThreadSerialEventTarget(), __func__,
1036 [demoHelplineParent, aPromise](nsString aMessage) mutable {
1037 aPromise->MaybeResolve(aMessage);
1039 [demoHelplineParent, aPromise](nsresult aErr) mutable {
1040 aPromise->MaybeReject(aErr);
1043 if (!demoHelplineParent->SendRequestAssistance()) {
1044 NS_WARNING("DemoHelplineParent::SendRequestAssistance failed");
1048 mozilla::ipc::IPCResult DemoHelplineParent::RecvAssistance(
1049 nsString&& aMessage, const AssistanceResolver& aResolver) {
1050 mPromise.Resolve(aMessage, __func__);
1055 void DemoHelplineParent::ActorDestroy(ActorDestroyReason aWhy) {
1056 mPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
1059 The ``DemoHelplineChild`` has to respond to the ``RequestAssistance`` method,
1060 which it does by returning a string and then calling ``Close`` on itself when
1061 the string has been received (but we do not call ``Close`` in the ``Recv``
1062 method!). We use an async response to the ``GiveAssistance`` message to detect
1063 that the string was received. During closing, the actor's ``ActorDestroy``
1064 method then calls the ``DemoChild::Shutdown`` method we defined in `Destroying
1069 mozilla::ipc::IPCResult DemoHelplineChild::RecvRequestAssistance() {
1070 RefPtr<DemoHelplineChild> me = this;
1071 RefPtr<nsIRunnable> runnable =
1072 NS_NewRunnableFunction("DemoHelplineChild::Close", [me]() { me->Close(); });
1075 nsString(HelpMessage()),
1076 [runnable](bool) { NS_DispatchToMainThread(runnable); },
1077 [runnable](mozilla::ipc::ResponseRejectReason) {
1078 NS_DispatchToMainThread(runnable);
1084 void DemoHelplineChild::ActorDestroy(ActorDestroyReason aWhy) {
1085 DemoChild::Shutdown();
1088 During the **Demo** process lifetime, there are two references to the
1089 ``DemoHelplineChild``, one from IPDL and one from the ``DemoChild``. The call
1090 to ``Close`` releases the one held by IPDL and the other isn't released until
1091 the ``DemoChild`` is destroyed.
1096 To run the sample, build and run and open the console. The new command is
1097 ``navigator.getAssistance().then(console.log)``. The message sent by
1098 ``SendAssistance`` is then logged to the console. The sample code also
1099 includes the name of the type of process that was used for the
1100 ``DemoHelplineParent`` so you can confirm that it works from main and from
1103 Debugging Process Startup
1104 -------------------------
1106 Debugging a child process at the start of its life is tricky. With most
1107 platforms/toolchains, it is surprisingly difficult to connect a debugger before
1108 the main routine begins execution. You may also find that console logging is
1109 not yet established by the operating system, especially when working with
1110 sandboxed child processes. Gecko has some facilities that make this less
1113 .. _Debugging with IPDL Logging:
1115 Debugging with IPDL Logging
1116 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
1118 This is also best seen with an example. To start, we can create a bug in the
1119 sample by removing the ``EmptyMessageQueue`` message sent to ``DemoParent``.
1120 This message was intended to guarantee that the ``DemoParent`` had handled all
1121 messages sent before it, so we could ``Close`` with the knowledge that we
1122 didn't miss anything. This sort of bug can be very difficult to track down
1123 because it is likely to be intermittent and may manifest more easily on some
1124 platforms/architectures than others. To create this bug, replace the
1125 ``SendEmptyMessageQueue`` call in ``DemoChild::Shutdown``:
1129 auto dc = gDemoChild;
1130 RefPtr<nsIRunnable> runnable = NS_NewRunnableFunction(
1131 "DemoChild::FinishShutdown",
1132 [dc2 = std::move(gDemoChild)]() { dc2->Close(); });
1133 dc->SendEmptyMessageQueue(
1134 [runnable](bool) { NS_DispatchToMainThread(runnable); },
1135 [runnable](mozilla::ipc::ResponseRejectReason) {
1136 NS_DispatchToMainThread(runnable);
1139 with just an (asynchronous) call to ``Close``:
1143 NS_DispatchToMainThread(NS_NewRunnableFunction(
1144 "DemoChild::FinishShutdown",
1145 [dc = std::move(gDemoChild)]() { dc->Close(); }));
1147 When we run the sample now, everything seems to behave ok but we see messages
1148 like these in the console: ::
1150 ###!!! [Parent][RunMessage] Error: (msgtype=0x410001,name=PDemo::Msg_InitCrashReporter) Channel closing: too late to send/recv, messages will be lost
1152 [Parent 16672, IPC I/O Parent] WARNING: file c:/mozilla-src/mozilla-unified/ipc/chromium/src/base/process_util_win.cc:167
1153 [Parent 16672, Main Thread] WARNING: Not resolving response because actor is dead.: file c:/mozilla-src/mozilla-unified/ipc/glue/ProtocolUtils.cpp:931
1154 [Parent 16672, Main Thread] WARNING: IPDL resolver dropped without being called!: file c:/mozilla-src/mozilla-unified/ipc/glue/ProtocolUtils.cpp:959
1156 We could probably figure out what is happening here from the messages but,
1157 with more complex protocols, understanding what led to this may not be so easy.
1158 To begin diagnosing, we can turn on IPC Logging, which was defined in the IPDL
1159 section on :ref:`Message Logging`. We just need to set an environment variable
1160 before starting the browser. Let's turn it on for all ``PDemo`` and
1161 ``PDemoHelpline`` actors: ::
1163 MOZ_IPC_MESSAGE_LOG="PDemoParent,PDemoChild,PDemoHelplineParent,PDemoHelplineChild"
1165 To underscore what we said above, when logging is active, the change in timing
1166 makes the error message go away and everything closes properly on a tested
1167 Windows desktop. However, the issue remains on a Macbook Pro and the log
1168 shows the issue rather clearly: ::
1170 [time: 1627075553937959][63096->63085] [PDemoChild] Sending PDemo::Msg_InitCrashReporter
1171 [time: 1627075553949441][63085->63096] [PDemoParent] Sending PDemo::Msg_CreateDemoHelplineChild
1172 [time: 1627075553950293][63092->63096] [PDemoHelplineParent] Sending PDemoHelpline::Msg_RequestAssistance
1173 [time: 1627075553979151][63096<-63085] [PDemoChild] Received PDemo::Msg_CreateDemoHelplineChild
1174 [time: 1627075553979433][63096<-63092] [PDemoHelplineChild] Received PDemoHelpline::Msg_RequestAssistance
1175 [time: 1627075553979498][63096->63092] [PDemoHelplineChild] Sending PDemoHelpline::Msg_GiveAssistance
1176 [time: 1627075553980105][63092<-63096] [PDemoHelplineParent] Received PDemoHelpline::Msg_GiveAssistance
1177 [time: 1627075553980181][63092->63096] [PDemoHelplineParent] Sending reply PDemoHelpline::Reply_GiveAssistance
1178 [time: 1627075553980449][63096<-63092] [PDemoHelplineChild] Received PDemoHelpline::Reply_GiveAssistance
1179 [tab 63092] NOTE: parent actor received `Goodbye' message. Closing channel.
1180 [default 63085] NOTE: parent actor received `Goodbye' message. Closing channel.
1182 ###!!! [Parent][RunMessage] Error: (msgtype=0x420001,name=PDemo::Msg_InitCrashReporter) Channel closing: too late to send/recv, messages will be lost
1184 [default 63085] NOTE: parent actor received `Goodbye' message. Closing channel.
1186 The imbalance with ``Msg_InitCrashReporter`` is clear. The message was not
1187 *Received* before the channel was closed. Note that the first ``Goodbye`` for
1188 the main (default) process is for the ``PDemoHelpline`` actor -- in this case,
1189 its child actor was in a content (tab) process. The second default process
1190 ``Goodbye`` is from the **Demo** process, sent when doing ``Close``. It might
1191 seem that it should handle the ``Msg_InitCrashReporter`` if it can handle the
1192 later ``Goodbye`` but this does not happen for safety reasons.
1194 Early Debugging For A New Process
1195 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1197 Let's assume now that we still don't understand the problem -- maybe we don't
1198 know that the ``InitCrashReporter`` message is sent internally by the
1199 ``CrashReporterClient`` we initialized. Or maybe we're only looking at Windows
1200 builds. We decide we'd like to be able to hook a debugger to the new process
1201 so that we can break on the ``SendInitCrashReporter`` call. Attaching the
1202 debugger has to happen fast -- process startup probably completes in under a
1203 second. Debugging this is not always easy.
1205 Windows users have options that work with both the Visual Studio and WinDbg
1206 debuggers. For Visual Studio users, there is an easy-to-use VS addon called
1207 the `Child Process Debugging Tool`_ that allows you to connect to *all*
1208 processes that are launched by a process you are debugging. So, if the VS
1209 debugger is connected to the main process, it will automatically connect to the
1210 new **Demo** process (and every other launched process) at the point that they
1211 are spawned. This way, the new process never does anything outside of the
1212 debugger. Breakpoints, etc work as expected. The addon mostly works like a
1213 toggle and will remain on until it is disabled from the VS menu.
1215 WinDbg users can achieve essentially the same behavior with the `.childdbg`_
1216 command. See the docs for details but essentially all there is to know is that
1217 ``.childdbg 1`` enables it and ``.childdbg 0`` disables it. You might add it
1218 to a startup config file (see the WinDbg ``-c`` command line option)
1220 Linux and mac users should reference gdb's ``detach-on-fork``. The command to
1221 debug child processes is ``set detach-on-fork off``. Again, the behavior is
1222 largely what you would expect -- that all spawned processes are added to the
1223 current debug session. The command can be added to ``.gdbinit`` for ease. At
1224 the time of this writing, lldb does not support automatically connecting to
1225 newly spawned processes.
1227 Finally, Linux users can use ``rr`` for time-travel debugging. See `Debugging
1228 Firefox with rr`_ for details.
1230 These solutions are not always desirable. For example, the fact that they hook
1231 *all* spawned processes can mean that targeting breakpoints to one process
1232 requires us to manually disconnect many other processes. In these cases, an
1233 easier solution may be to use Gecko environment variables that will cause the
1234 process to sleep for some number of seconds. During that time, you can find
1235 the process ID (PID) for the process you want to debug and connect your
1236 debugger to it. OS tools like ``ProcessMonitor`` can give you the PID but it
1237 will also be clearly logged to the console just before the process waits.
1239 Set ``MOZ_DEBUG_CHILD_PROCESS=1`` to turn on process startup pausing. You can
1240 also set ``MOZ_DEBUG_CHILD_PAUSE=N`` where N is the number of seconds to sleep.
1241 The default is 10 seconds on Windows and 30 on other platforms.
1243 Pausing for the debugger is not a panacea. Since the environmental variables
1244 are not specific to process type, you will be forced to wait for all of the
1245 processes Gecko creates before you wait for it to get to yours. The pauses can
1246 also end up exposing unknown concurrency bugs in the browser before it even
1247 gets to your issue, which is good to discover but doesn't fix your bug. That
1248 said, any of these strategies would be enough to facilitate easily breaking on
1249 ``SendInitCrashReporter`` and finding our sender.
1251 .. _Child Process Debugging Tool: https://marketplace.visualstudio.com/items?itemName=vsdbgplat.MicrosoftChildProcessDebuggingPowerTool
1252 .. _.childdbg: https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/-childdbg--debug-child-processes-