Bug 1858509 add thread-safety annotations around MediaSourceDemuxer::mMonitor r=alwu
[gecko.git] / ipc / docs / processes.rst
blobb9c3cb263d1dc0300da08b474ef9bcc5cee47b38
1 Gecko Processes
2 ===============
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
19   compromised.
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
25   exploits.
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
31   files.
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
35   process.
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
39 "First, do no harm":
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.
65 .. note::
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.
77 .. mermaid::
79     graph TD
80         RDD -->|PRemoteDecoderManager| Content
81         RDD(Data Decoder) ==>|PRDD| Main
83         Launcher --> Main
85         Main ==>|PContent| Content
86         Main ==>|PSocketProcess| Socket(Network Socket)
87         Main ==>|PGMP| GMP(Gecko Media Plugins)
88         VR ==>|PVR| Main
89         GPU ==>|PGPU| Main
91         Socket -->|PSocketProcessBridge| Content
93         GPU -->|PCompositorManager| Main
94         GPU -->|PCompositorManager| Content
96         Content -->|PGMPContent| GMP
98         VR -->|PVRGPU| GPU
100 .. warning::
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
123 processes.
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`_
156 Common Architecture
157 ~~~~~~~~~~~~~~~~~~~
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
161 we call **Demo**.
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
183   with the ``Host``.
185 A fifth class is optional but integration with common services requires
186 something like it:
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.
215 Process Bookkeeping
216 ~~~~~~~~~~~~~~~~~~~
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.
224 Basic requirements
225 ^^^^^^^^^^^^^^^^^^
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>`_
243 Graphics
244 ########
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
250   `gfxPlatform
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
253   `gfxPlatform
254   <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/gfx/thebes/gfxPlatform.cpp#1255-1259>`_
256 Android
257 #######
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
264   `AndroidManifest
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>`_
271 Crash reporting
272 ###############
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>`_
287 Memory reporting
288 ################
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>`_
305 - On the parent side
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>`_
312 Process reporting
313 #################
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
317 process there.
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>`_
329 Profiler
330 ########
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.
342 Static Components
343 #################
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
387     (needed?)
388   + Add new process selector for ``gProcessMatchTable`` in
389     ``nsComponentManagerImpl::Init()``
391 Glean telemetry
392 ###############
394 - Ensure your new IPDL includes on the child side
396   + `FlushFOGData
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>`_
409 - Within `FOGIPC
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>`_
422 Third-Party Modules
423 ###################
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>`_
451 Sandboxing
452 ##########
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.
458 Linux Sandbox
459 _____________
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
480   broker
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
489   reporter wrapper
490   <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/linux/reporter/SandboxReporterWrappers.cpp#69-91>`_
492 MacOS Sandbox
493 _____________
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 `series 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/``.
523 Windows Sandbox
524 _______________
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.
539 Sandbox tests
540 _____________
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()``
552   in `testing child
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:
564 .. code-block:: c++
566     /* static */
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)) {
574           return false;
575         }
577         // Async launch creates a promise that we use below.
578         if (!host->AsyncLaunch(extraArgs)) {
579           return false;
580         }
582         host->WhenProcessHandleReady()->Then(
583           GetCurrentSerialEventTarget(), __func__,
584           [host = std::move(host)](
585               const ipc::ProcessHandlePromise::ResolveOrRejectValue&
586                   aResult) mutable {
587             if (aResult.IsReject()) {
588               host->ResolveAsFailure();
589               return;
590             }
592             auto actor = MakeRefPtr<DemoParent>(std::move(host));
593             actor->Init();
594           });
595     }
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
613 the ``Init`` method:
615 .. code-block:: c++
617     DemoParent::DemoParent(UniqueHost&& aHost)
618         : mHost(std::move(aHost)) {}
620     DemoParent::Init() {
621       mHost->TakeInitialEndpoint().Bind(this);
622       // ...
623       mHost->MakeBridgeAndResolve();
624     }
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.
633 .. warning::
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
639     deadlock.
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:
651 .. code-block:: c++
653     MessageLoop::Type uiLoopType;
654     switch (XRE_GetProcessType()) {
655       case GeckoProcessType_Demo:
656         uiLoopType = MessageLoop::TYPE_MOZILLA_CHILD;  break;
657       // ...
658     }
660     // ...
662     UniquePtr<ProcessChild> process;
663     switch (XRE_GetProcessType()) {
664         // ...
665         case GeckoProcessType_Demo:
666           process = MakeUnique<DemoChild::Process>(parentPID);
667           break;
668     }
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:
682 .. code-block:: c++
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);
689     #endif
691       if (!mozilla::ipc::ProcessChild::InitPrefs(aArgc, aArgv)) {
692         return false;
693       }
695       if (NS_WARN_IF(NS_FAILED(nsThreadManager::get().Init()))) {
696         return false;
697       }
699       if (NS_WARN_IF(!TakeInitialEndpoint().Bind(this))) {
700         return false;
701       }
703       // ... initializing components ...
705       if (NS_FAILED(NS_InitMinimalXPCOM())) {
706         return false;
707       }
709       return true;
710     }
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>`_
729 and `Module.h
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.
750 .. note::
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``.
762 .. code-block:: c++
764     /* static */
765     void DemoChild::Shutdown() {
766       if (gDemoChild) {
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);
778             });
779       }
780     }
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.
806 .. code-block:: c++
808     DemoChild::~DemoChild() {
809       // ...
810       XRE_ShutdownChildProcess();
811     }
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
815 in C++>` method.
817 .. code-block:: c++
819     void DemoParent::ActorDestroy(ActorDestroyReason aWhy) {
820       if (aWhy == AbnormalShutdown) {
821         GenerateCrashReport(OtherPid());
822       }
823       // ...
824     }
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.
844 .. code-block:: c++
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);
856       // ...
858       if (!mActor->SendCreateDemoHelplineChild(std::move(child))) {
859         NS_WARNING("Failed to SendCreateDemoHelplineChild");
860         return false;
861       }
863       resolveFail.release();
864       mResolver(Some(std::move(parent)));
865       return true;
866     }
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``
872 easily:
874 .. code-block:: c++
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");
881       }
882       return IPC_OK();
883     }
885 ``MakeProcessAndGetAssistance`` binds the same way:
887 .. code-block:: c++
889     RefPtr<DemoHelplineParent> demoHelplineParent = new DemoHelplineParent();
890     if (!endpoint.Bind(demoHelplineParent)) {
891       NS_WARNING("Unable to bind DemoHelplineParent");
892       return false;
893     }
894     MOZ_ASSERT(ok);
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
915 our promise:
917 .. code-block:: c++
919     /* static */
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();
929       } else {
930         MOZ_ASSERT(XRE_IsParentProcess());
931         auto promise = MakeRefPtr<LaunchDemoProcessPromise::Private>(__func__);
932         resolver = promise;
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__);
939                 })) {
940           NS_WARNING("Failed to launch Demo process");
941           resolver->Reject(NS_ERROR_FAILURE);
942           return false;
943         }
944       }
946       resolver->Then(
947           GetMainThreadSerialEventTarget(), __func__,
948           [aPromise](Maybe<Endpoint<PDemoHelplineParent>>&& maybeEndpoint) mutable {
949             if (!maybeEndpoint) {
950               aPromise->MaybeReject(NS_ERROR_FAILURE);
951               return;
952             }
954             RefPtr<DemoHelplineParent> demoHelplineParent = new DemoHelplineParent();
955             Endpoint<PDemoHelplineParent> endpoint = maybeEndpoint.extract();
956             if (!endpoint.Bind(demoHelplineParent)) {
957               NS_WARNING("Unable to bind DemoHelplineParent");
958               return false;
959             }
960             MOZ_ASSERT(ok);
962             // ... communicate with PDemoHelpline and write message to console ...
963           },
964           [aPromise](mozilla::ipc::ResponseRejectReason&& aReason) {
965             aPromise->MaybeReject(NS_ERROR_FAILURE);
966           });
968       return true;
969     }
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");
976       }
977       return IPC_OK();
978     }
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``:
993 .. code-block:: c++
995     partial interface Navigator {
996       [Throws]
997       Promise<DOMString> getAssistance();
998     };
1000     already_AddRefed<Promise> Navigator::GetAssistance(ErrorResult& aRv) {
1001       if (!mWindow || !mWindow->GetDocShell()) {
1002         aRv.Throw(NS_ERROR_UNEXPECTED);
1003         return nullptr;
1004       }
1006       RefPtr<Promise> echoPromise = Promise::Create(mWindow->AsGlobal(), aRv);
1007       if (NS_WARN_IF(aRv.Failed())) {
1008         return nullptr;
1009       }
1011       if (!DemoHelplineParent::MakeProcessAndGetAssistance(echoPromise)) {
1012         aRv.Throw(NS_ERROR_FAILURE);
1013         return nullptr;
1014       }
1016       return echoPromise.forget();
1017     }
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):
1023 .. code-block:: c++
1025     using DemoPromise = MozPromise<nsString, nsresult, true>;
1027     /* static */
1028     bool DemoHelplineParent::MakeProcessAndGetAssistance(
1029         RefPtr<mozilla::dom::Promise> aPromise) {
1031         // ... construct and connect demoHelplineParent ...
1033         RefPtr<DemoPromise> promise = demoHelplineParent->mPromise.Ensure(__func__);
1034         promise->Then(
1035             GetMainThreadSerialEventTarget(), __func__,
1036             [demoHelplineParent, aPromise](nsString aMessage) mutable {
1037               aPromise->MaybeResolve(aMessage);
1038             },
1039             [demoHelplineParent, aPromise](nsresult aErr) mutable {
1040               aPromise->MaybeReject(aErr);
1041             });
1043         if (!demoHelplineParent->SendRequestAssistance()) {
1044           NS_WARNING("DemoHelplineParent::SendRequestAssistance failed");
1045         }
1046     }
1048     mozilla::ipc::IPCResult DemoHelplineParent::RecvAssistance(
1049         nsString&& aMessage, const AssistanceResolver& aResolver) {
1050       mPromise.Resolve(aMessage, __func__);
1051       aResolver(true);
1052       return IPC_OK();
1053     }
1055     void DemoHelplineParent::ActorDestroy(ActorDestroyReason aWhy) {
1056       mPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
1057     }
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
1065 the New Process`_:
1067 .. code-block:: c++
1069     mozilla::ipc::IPCResult DemoHelplineChild::RecvRequestAssistance() {
1070       RefPtr<DemoHelplineChild> me = this;
1071       RefPtr<nsIRunnable> runnable =
1072           NS_NewRunnableFunction("DemoHelplineChild::Close", [me]() { me->Close(); });
1074       SendAssistance(
1075           nsString(HelpMessage()),
1076           [runnable](bool) { NS_DispatchToMainThread(runnable); },
1077           [runnable](mozilla::ipc::ResponseRejectReason) {
1078             NS_DispatchToMainThread(runnable);
1079           });
1081       return IPC_OK();
1082     }
1084     void DemoHelplineChild::ActorDestroy(ActorDestroyReason aWhy) {
1085       DemoChild::Shutdown();
1086     }
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.
1093 Running the Sample
1094 ~~~~~~~~~~~~~~~~~~
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
1101 content.
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
1111 painful.
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``:
1127 .. code-block:: c++
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);
1137         });
1139 with just an (asynchronous) call to ``Close``:
1141 .. code-block:: c++
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="PDemo,PDemoHelpline"
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.
1181     [...]
1182     ###!!! [Parent][RunMessage] Error: (msgtype=0x420001,name=PDemo::Msg_InitCrashReporter) Channel closing: too late to send/recv, messages will be lost
1183     [...]
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-