1Gecko Processes 2=============== 3 4Before Creating a New Process 5----------------------------- 6 7Firefox started out as a one process application. Then, one became two as 8NPAPI plugins like Flash were pushed into their own process (plugin processes) 9for security and stability reasons. Then, it split again so that the browser 10could also disentangle itself from web content (content processes). Then, 11implementations on some platforms developed processes for graphics ("GPU" 12processes). And for media codecs. And VR. And file URLs. And sockets. And 13even more content processes. And so on... 14 15Here is an incomplete list of *good* reasons we've created new processes: 16 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. 36 37Informed by this history, there is some of non-obvious preparation that you 38should do before starting down this path. This falls under the category of 39"First, do no harm": 40 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. 64 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. 69 70.. _launcher process: https://wiki.mozilla.org/Platform/Integration/InjectEject/Launcher_Process/ 71 72Firefox Process Hierarchy 73------------------------- 74 75This diagram shows the primary process types in Firefox. 76 77.. mermaid:: 78 79 graph TD 80 RDD -->|PRemoteDecoderManager| Content 81 RDD(Data Decoder) ==>|PRDD| Main 82 83 Launcher --> Main 84 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 90 91 Socket -->|PSocketProcessBridge| Content 92 93 GPU -->|PCompositorManager| Main 94 GPU -->|PCompositorManager| Content 95 96 Content -->|PGMPContent| GMP 97 98 VR -->|PVRGPU| GPU 99 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! 108 109The arrows point from the parent side to the child. Bolded arrows indicate the 110first top-level actors for the various process types. The other arrows show 111important actors that are usually the first connections establised between the 112two processes. These relationships difficult to discern from code. Processes 113should clearly document their top-level connections in their IPDL files. 114 115Some process types only exist on some platforms and some processes may only be 116created on demand. For example, Mac builds do not use a GPU process but 117instead fold the same actor connections into its main process (except ``PGPU``, 118which it does not use). These exceptions are also very hard to learn from code 119and should be clearly documented. 120 121``about:processes`` shows statistics for the processes in a currently running 122browser. It is also useful to see the distribution of web pages across content 123processes. 124 125.. _Adding a New Type of Process: 126 127Adding a New Type of Process 128---------------------------- 129 130Adding a new process type doesn't require any especially difficult steps but it 131does require a lot of steps that are not obvious. This section will focus on 132the steps as it builds an example. It will be light on the details of the 133classes and protocols involved. Some implementations may need to seek out a 134deeper understanding of the components set up here but most should instead 135strive for simplicity. 136 137In the spirit of creating a *responsible* process, the sample will connect 138several components that any deployed Gecko process is likely to need. These 139include configuring a sandbox, `registration with the CrashReporter service`_ 140and ("minimal") XPCOM initialization. Consult documentation for these 141components for more information on their integration. 142 143This example will be loosely based on the old (now defunct) IPDL **Extending a 144Protocol** example for adding a new actor. We will add a command to the 145browser's ``navigator`` JS object, ``navigator.getAssistance()``. When the 146user enters the new command in, say, the browser's console window, it will 147create 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. 149Once that is done, the new process will be cleanly destroyed. 150 151Code for the complete demo can be found `here 152<https://phabricator.services.mozilla.com/D119038>`_. 153 154.. _registration with the CrashReporter service: `Crash Reporter`_ 155 156Common Architecture 157~~~~~~~~~~~~~~~~~~~ 158 159Every type of process (besides the launcher and main processses) needs two 160classes and an actor pair to launch. This sample will be adding a process type 161we call **Demo**. 162 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``. 184 185A fifth class is optional but integration with common services requires 186something like it: 187 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``. 196 197Finally, it is highly probable and usually desirable for the new process to 198include another new top-level actor that represents the top-level operations 199and communications of the new process. This actor will use the new process as 200a child but may have any other process as the parent, unlike ``PDemo`` whose 201parent is always the main process. This new actor will be created by the main 202process, which creates a pair of ``Endpoint`` objects specifically for the 203desired process pairing, and then sends those ``Endpoint`` objects to their 204respective processes. The **Demo** example is interesting because the user can 205issue the command from a content process or the main one, by opening the 206console in a normal or a privileged page (e.g. ``about:sessionrestore``), 207respectively. Supporting both of these cases will involve very little 208additional effort. The sample will show this as part of implementing the 209second top-level actor pair ``PDemoHelpline`` in `Connecting With Other 210Processes`_, where the parent can be in either the main or a content process. 211 212The rest of the sections will explain how to compose these classes and 213integrate them with Gecko. 214 215Process Bookkeeping 216~~~~~~~~~~~~~~~~~~~ 217 218To begin with, look at the `geckoprocesstypes generator 219<https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/xpcom/geckoprocesstypes_generator/geckoprocesstypes/__init__.py>`_ 220which adds the bones for a new process (by defining enum values and so on). 221Some further manual intervention is still required, and you need to follow the 222following checklists depending on your needs. 223 224Basic requirements 225^^^^^^^^^^^^^^^^^^ 226 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>`_ 242 243Graphics 244######## 245 246If you need graphics-related interaction, hack into `gfxPlatform 247<https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/gfx/thebes/gfxPlatform.cpp>`_ 248 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>`_ 255 256Android 257####### 258 259You might want to talk with `#geckoview` maintainers to ensure if this is 260required or applicable to your new process type. 261 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>`_ 270 271Crash reporting 272############### 273 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>`_ 286 287Memory reporting 288################ 289 290Throughout the linked code, please consider those methods more as boilerplate code that will require some trivial modification to fit your exact usecase. 291 292- Add definition of memory reporter to your new :ref:`top-level actor <Top Level Actors>` 293 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>`_ 297 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 300 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>`_ 303 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 306 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>`_ 309 310If 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>`_ 311 312Process reporting 313################# 314 315Those elements will be used for exposing processes to users in some `about:` 316pages. You might want to ping `#fluent-reviewers` to ensure if you need your 317process there. 318 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/d4b9c457db637fde655592d9e2048939b7ab2854/toolkit/modules/ProcessType.jsm#14-20>`_ 324 325Profiler 326######## 327 328- Add definition of ``PProfiler`` to your new IPDL 329 330 + Type inclusion `protocol PProfiler <https://searchfox.org/mozilla-central/rev/fc4d4a8d01b0e50d20c238acbb1739ccab317ebc/ipc/glue/PUtilityProcess.ipdl#9>`_ 331 + Child-side `InitProfiler <https://searchfox.org/mozilla-central/rev/fc4d4a8d01b0e50d20c238acbb1739ccab317ebc/ipc/glue/PUtilityProcess.ipdl#42>`_ 332 333- 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. 334- Provide an implementation for `InitProfiler <https://searchfox.org/mozilla-central/rev/fc4d4a8d01b0e50d20c238acbb1739ccab317ebc/ipc/glue/UtilityProcessChild.cpp#147-151>`_ 335 336- 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. 337 338Static Components 339################# 340 341The amount of changes required here are significant, `Bug 1740485: Improve 342StaticComponents code generation 343<https://bugzilla.mozilla.org/show_bug.cgi?id=1740485>`_ tracks improving that. 344 345- Update allowance in those configuration files to match new process selector 346 that includes your new process. When exploring those components definitions, 347 keep in mind that you are looking at updating `processes` field in the 348 `Classes` object. The `ProcessSelector` value will come from what the reader 349 writes based on the instructions below. Some of these also contains several 350 services, so you might have to ensure you have all your bases covered. Some of 351 the components might not need to be updated as well. 352 353 + `libpref <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/modules/libpref/components.conf>`_ 354 + `telemetry <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/toolkit/components/telemetry/core/components.conf>`_ 355 + `android <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/widget/android/components.conf>`_ 356 + `gtk <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/widget/gtk/components.conf>`_ 357 + `windows <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/widget/windows/components.conf>`_ 358 + `base <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/xpcom/base/components.conf>`_ 359 + `components <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/xpcom/components/components.conf>`_ 360 + `ds <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/xpcom/ds/components.conf>`_ 361 + `threads <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/xpcom/threads/components.conf>`_ 362 + `cocoa kWidgetModule <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/widget/cocoa/nsWidgetFactory.mm#194-202>`_ 363 + `build <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/xpcom/build/components.conf>`_ 364 + `XPCOMinit kXPCOMModule <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/xpcom/build/XPCOMInit.cpp#172-180>`_ 365 366- Within `static components generator 367 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/xpcom/components/gen_static_components.py>`_ 368 369 + Add new definition in ``ProcessSelector`` for your new process 370 ``ALLOW_IN_x_PROCESS = 0x..`` 371 + Add new process selector masks including your new process definition 372 + Also add those into the ``PROCESSES`` structure 373 374- Within `module definition <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/xpcom/components/Module.h>`_ 375 376 + Add new definition in ``enum ProcessSelector`` 377 + Add new process selector mask including the new definition 378 + Update ``kMaxProcessSelector`` 379 380- Within `nsComponentManager <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/xpcom/components/nsComponentManager.cpp>`_ 381 382 + Add new selector match in ``ProcessSelectorMatches`` for your new process 383 (needed?) 384 + Add new process selector for ``gProcessMatchTable`` in 385 ``nsComponentManagerImpl::Init()`` 386 387Glean telemetry 388############### 389 390- Ensure your new IPDL includes on the child side 391 392 + `FlushFOGData 393 <https://searchfox.org/mozilla-central/rev/fc4d4a8d01b0e50d20c238acbb1739ccab317ebc/ipc/glue/PUtilityProcess.ipdl#55>`_ 394 + `TestTriggerMetrics 395 <https://searchfox.org/mozilla-central/rev/fc4d4a8d01b0e50d20c238acbb1739ccab317ebc/ipc/glue/PUtilityProcess.ipdl#60>`_ 396 397- Provide a parent-side implementation for `FOGData 398 <https://searchfox.org/mozilla-central/rev/fc4d4a8d01b0e50d20c238acbb1739ccab317ebc/ipc/glue/UtilityProcessParent.cpp#79-82>`_ 399- Provide a child-side implementation for `FlushFOGData 400 <https://searchfox.org/mozilla-central/rev/fc4d4a8d01b0e50d20c238acbb1739ccab317ebc/ipc/glue/UtilityProcessChild.cpp#179-183>`_ 401- Child-side should flush its FOG data at IPC `ActorDestroy 402 <https://searchfox.org/mozilla-central/rev/fc4d4a8d01b0e50d20c238acbb1739ccab317ebc/ipc/glue/UtilityProcessChild.cpp#199-201>`_ 403- Child-side `test metrics 404 <https://searchfox.org/mozilla-central/rev/fc4d4a8d01b0e50d20c238acbb1739ccab317ebc/ipc/glue/UtilityProcessChild.cpp#185-191>`_ 405- Within `FOGIPC 406 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/toolkit/components/glean/ipc/FOGIPC.cpp>`_ 407 408 + Add handling of your new process type within ``FlushAllChildData()`` `here 409 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/toolkit/components/glean/ipc/FOGIPC.cpp#106-121>`_ 410 and ``SendFOGData()`` `here 411 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/toolkit/components/glean/ipc/FOGIPC.cpp#165-182>`_ 412 + Add support for sending test metrics in ``TestTriggerMetrics()`` `here 413 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/toolkit/components/glean/ipc/FOGIPC.cpp#208-232>`_ 414 415- Handle process shutdown in ``register_process_shutdown()`` of `glean 416 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/toolkit/components/glean/api/src/ipc.rs>`_ 417 418Sandboxing 419########## 420 421Sandboxing changes related to a new process can be non-trivial, so it is 422strongly advised that you reach to the Sandboxing team in ``#hardening`` to 423discuss your needs prior to making changes. 424 425Linux Sandbox 426_____________ 427 428Linux sandboxing mostly works by allowing / blocking system calls for child 429process and redirecting (brokering) some from the child to the parent. Rules 430are written in a specific DSL: `BPF 431<https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/chromium/sandbox/linux/bpf_dsl/bpf_dsl.h#21-72>`_. 432 433- Add new ``SetXXXSandbox()`` function within `linux sandbox 434 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/linux/Sandbox.cpp#719-748>`_ 435- Within `sandbox filter 436 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/linux/SandboxFilter.cpp>`_ 437 438 + Add new helper ``GetXXXSandboxPolicy()`` `like this one 439 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/linux/SandboxFilter.cpp#2036-2040>`_ 440 called by ``SetXXXSandbox()`` 441 + Derive new class `similar to this 442 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/linux/SandboxFilter.cpp#2000-2034>`_ 443 inheriting ``SandboxPolicyCommon`` or ``SandboxPolicyBase`` and defining 444 the sandboxing policy 445 446- Add new ``SandboxBrokerPolicyFactory::GetXXXProcessPolicy()`` in `sandbox 447 broker 448 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp#881-932>`_ 449- Add new case handling in ``GetEffectiveSandboxLevel()`` in `sandbox launch 450 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/linux/launch/SandboxLaunch.cpp#243-271>`_ 451- Add new entry in ``enum class ProcType`` of `sandbox reporter header 452 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/linux/reporter/SandboxReporterCommon.h#32-39>`_ 453- Add new case handling in ``SubmitToTelemetry()`` in `sandbox reporter 454 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/linux/reporter/SandboxReporter.cpp#131-152>`_ 455- Add new case handling in ``SandboxReportWrapper::GetProcType()`` of `sandbox 456 reporter wrapper 457 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/linux/reporter/SandboxReporterWrappers.cpp#69-91>`_ 458 459MacOS Sandbox 460_____________ 461 462- Add new case handling in ``GeckoChildProcessHost::StartMacSandbox()`` of 463 `GeckoChildProcessHost <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/ipc/glue/GeckoChildProcessHost.cpp#1720-1743>`_ 464- Add new entry in ``enum MacSandboxType`` defined in `macOS sandbox header 465 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/mac/Sandbox.h#12-20>`_ 466- Within `macOS sandbox core 467 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/mac/Sandbox.mm>`_ 468 handle the new ``MacSandboxType`` in 469 470 + ``MacSandboxInfo::AppendAsParams()`` in the `switch statement 471 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/mac/Sandbox.mm#164-188>`_ 472 + ``StartMacSandbox()`` in the `serie of if/else statements 473 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/mac/Sandbox.mm#286-436>`_. 474 This code sets template values for the sandbox string rendering, and is 475 running on the side of the main process. 476 + ``StartMacSandboxIfEnabled()`` in this `switch statement 477 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/mac/Sandbox.mm#753-782>`_. 478 You might also need a ``GetXXXSandboxParamsFromArgs()`` that performs CLI 479 parsing on behalf of ``StartMacSandbox()``. 480 481- Create the new sandbox definition file 482 ``security/sandbox/mac/SandboxPolicy<XXX>.h`` for your new process ``<XXX>``, 483 and make it exposed in the ``EXPORTS.mozilla`` section of `moz.build 484 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/mac/moz.build#7-13>`_. 485 Those rules follows a specific Scheme-like language. You can learn more about 486 it in `Apple Sandbox Guide 487 <https://reverse.put.as/wp-content/uploads/2011/09/Apple-Sandbox-Guide-v1.0.pdf>`_ 488 as well as on your system within ``/System/Library/Sandbox/Profiles/``. 489 490Windows Sandbox 491_______________ 492 493- Introduce a new ``SandboxBroker::SetSecurityLevelForXXXProcess()`` that 494 defines the new sandbox in both 495 496 + the sandbox broker basing yourself on that `example 497 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp#1241-1344>`_ 498 + the remote sandbox broker getting `inspired by 499 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/win/src/remotesandboxbroker/remoteSandboxBroker.cpp#161-165>`_ 500 501- Add new case handling in ``WindowsProcessLauncher::DoSetup()`` calling 502 ``SandboxBroker::SetSecurityLevelForXXXProcess()`` in `GeckoChildProcessHost 503 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/ipc/glue/GeckoChildProcessHost.cpp#1391-1470>`_. 504 This will apply actual sandboxing rules to your process. 505 506Sandbox tests 507_____________ 508 509- New process' first top level actor needs to `include PSandboxTesting 510 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/common/test/PSandboxTesting.ipdl>`_ 511 and implement ``RecvInitSandboxTesting`` `like there 512 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/ipc/glue/UtilityProcessChild.cpp#165-174>`_. 513- Add your new process ``string_name`` in the ``processTypes`` list of `sandbox 514 tests <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/test/browser_sandbox_test.js#17>`_ 515- Add a new case in ``SandboxTest::StartTests()`` in `test core 516 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/common/test/SandboxTest.cpp#100-232>`_ 517 to handle your new process 518- Add a new if branch for your new process in ``SandboxTestingChild::Bind()`` 519 in `testing child 520 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/common/test/SandboxTestingChild.cpp#68-96>`_ 521- Add a new ``RunTestsXXX`` function for your new process (called by ``Bind()`` 522 above) `similar to that implementation 523 <https://searchfox.org/mozilla-central/rev/d4b9c457db637fde655592d9e2048939b7ab2854/security/sandbox/common/test/SandboxTestingChildTests.h#333-363>`_ 524 525Creating the New Process 526~~~~~~~~~~~~~~~~~~~~~~~~ 527 528The sample does this in ``DemoParent::LaunchDemoProcess``. The core 529behavior is fairly clear: 530 531.. code-block:: c++ 532 533 /* static */ 534 bool DemoParent::LaunchDemoProcess( 535 base::ProcessId aParentPid, LaunchDemoProcessResolver&& aResolver) { 536 UniqueHost host(new Host(aParentPid, std::move(aResolver))); 537 538 // Prepare "command line" startup args for new process 539 std::vector<std::string> extraArgs; 540 if (!host->BuildProcessArgs(&extraArgs)) { 541 return false; 542 } 543 544 // Async launch creates a promise that we use below. 545 if (!host->AsyncLaunch(extraArgs)) { 546 return false; 547 } 548 549 host->WhenProcessHandleReady()->Then( 550 GetCurrentSerialEventTarget(), __func__, 551 [host = std::move(host)]( 552 const ipc::ProcessHandlePromise::ResolveOrRejectValue& 553 aResult) mutable { 554 if (aResult.IsReject()) { 555 host->ResolveAsFailure(); 556 return; 557 } 558 559 new DemoParent(std::move(host)); 560 }); 561 } 562 563First, it creates an object of our ``GeckoChildProcessHost`` subclass (storing 564some stuff for later). ``GeckoChildProcessHost`` is a base class that 565abstracts the system-level operations involved in launching the new process. 566It is the most substantive part of the launch procedure. After its 567construction, the code prepares a bunch of strings to pass on the "command 568line", which is the only way to pass data to the new process before IPDL is 569established. All new processes will at least include ``-parentBuildId`` for 570validating that dynamic libraries are properly versioned, and shared memory for 571passing user preferences, which can affect early process behavior. Finally, it 572tells ``GeckoChildProcessHost`` to asynchronously launch the process and run 573the given lambda when it has a result. The lambda creates ``DemoParent`` with 574the new host, if successful. 575 576In this sample, the ``DemoParent`` is owned (in the reference-counting sense) 577by IPDL, which is why it doesn't get assigned to anything. This simplifies the 578design dramatically. IPDL takes ownership when the actor calls ``Open`` in its 579constructor: 580 581.. code-block:: c++ 582 583 DemoParent::DemoParent(UniqueHost&& aHost) 584 : mHost(std::move(aHost)) { 585 Open(mHost->TakeInitialPort(), 586 base::GetProcId(mHost->GetChildProcessHandle())); 587 // ... 588 mHost->MakeBridgeAndResolve(); 589 } 590 591After the ``Open`` call, the actor is live and communication with the new 592process can begin. The constructor concludes by initiating the process of 593connecting the ``PDemoHelpline`` actors; ``Host::MakeBridgeAndResolve`` will be 594covered in `Creating a New Top Level Actor`_. However, before we get into 595that, we should finish defining the lifecycle of the process. In the next 596section we look at launching the new process from the new process' perspective. 597 598.. warning:: 599 The code could have chosen to create a ``DemoChild`` instead of a 600 ``DemoParent`` and the choice may seem cosmetic but it has substantial 601 implications that could affect browser stability. The most 602 significant is that the prohibitibition on synchronous IPDL messages going 603 from parent to child can no longer guarantee freedom from multiprocess 604 deadlock. 605 606Initializing the New Process 607~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 608 609The new process first adopts the **Demo** process type in 610``XRE_InitChildProcess``, where it responds to the **Demo** values we added to 611some enums above. Specifically, we need to choose the type of MessageLoop our 612main thread will run (this is discussed later) and we need to create our 613``ProcessChild`` subclass. This is not an insignificant choice so pay close 614attention to the `MessageLoop` options: 615 616.. code-block:: c++ 617 618 MessageLoop::Type uiLoopType; 619 switch (XRE_GetProcessType()) { 620 case GeckoProcessType_Demo: 621 uiLoopType = MessageLoop::TYPE_MOZILLA_CHILD; break; 622 // ... 623 } 624 625 // ... 626 627 UniquePtr<ProcessChild> process; 628 switch (XRE_GetProcessType()) { 629 // ... 630 case GeckoProcessType_Demo: 631 process = MakeUnique<DemoChild::Process>(parentPID); 632 break; 633 } 634 635We then need to create our singleton ``DemoChild`` object, which can occur in 636the constructor or the ``Process::Init()`` call, which is common. We store a 637strong reference to the actor (as does IPDL) so that we are guaranteed that it 638exists as long as the ``ProcessChild`` does -- although the message channel may 639be closed. We will release the reference either when the process is properly 640shutting down or when an IPC error closes the channel. 641 642``Init`` is given the command line arguments constucted above so it will need 643to be overridden to parse them. It does this, binds our actor by 644calling ``Open`` as was done with the parent, then initializes a bunch of 645components that the process expects to use: 646 647.. code-block:: c++ 648 649 bool DemoChild::Init(int aArgc, char* aArgv[]) { 650 #if defined(MOZ_SANDBOX) && defined(OS_WIN) 651 mozilla::SandboxTarget::Instance()->StartSandbox(); 652 #elif defined(__OpenBSD__) && defined(MOZ_SANDBOX) 653 StartOpenBSDSandbox(GeckoProcessType_Demo); 654 #endif 655 656 if (!mozilla::ipc::ProcessChild::InitPrefs(aArgc, aArgv)) { 657 return false; 658 } 659 660 if (NS_WARN_IF(NS_FAILED(nsThreadManager::get().Init()))) { 661 return false; 662 } 663 664 if (NS_WARN_IF(!Open(ipc::IOThreadChild::TakeInitialPort(), mParentPid))) { 665 return false; 666 } 667 668 // ... initializing components ... 669 670 if (NS_FAILED(NS_InitMinimalXPCOM())) { 671 return false; 672 } 673 674 return true; 675 } 676 677This is a slimmed down version of the real ``Init`` method. We see that it 678establishes a sandbox (more on this later) and then reads the command line and 679preferences that we sent from the main process. It then initializes the thread 680manager, which is required by for the subsequent ``Open`` call. 681 682Among the list of components we initialize in the sample code, XPCOM is 683special. XPCOM includes a suite of components, including the component 684manager, and is usually required for serious Gecko development. It is also 685heavyweight and should be avoided if possible. We will leave the details of 686XPCOM development to that module but we mention XPCOM configuration that is 687special to new processes, namely ``ProcessSelector``. ``ProcessSelector`` 688is used to determine what process types have access to what XPCOM components. 689By default, a process has access to none. The code adds enums for selecting 690a subset of process types, like 691``ALLOW_IN_GPU_RDD_VR_SOCKET_UTILITY_AND_DEMO_PROCESS``, to the 692``ProcessSelector`` enum in `gen_static_components.py 693<https://searchfox.org/mozilla-central/source/xpcom/components/gen_static_components.py>`_ 694and `Module.h 695<https://searchfox.org/mozilla-central/source/xpcom/components/Module.h>`_. 696It then updates the selectors in various ``components.conf`` files and 697hardcoded spots like ``nsComponentManager.cpp`` to add the **Demo** processes 698to the list that can use them. Some modules are required to bootstrap XPCOM 699and will cause it to fail to initialize if they are not permitted. 700 701At this point, the new process is idle, waiting for messages from the main 702process that will start the ``PDemoHelpline`` actor. We discuss that in 703`Creating a New Top Level Actor`_ below but, first, let's look at how the main 704and **Demo** processes will handle clean destruction. 705 706Destroying the New Process 707~~~~~~~~~~~~~~~~~~~~~~~~~~ 708 709Gecko processes have a clean way for clients to request that they shutdown. 710Simply calling ``Close()`` on the top level actor at either endoint will begin 711the shutdown procedure (so, ``PDemoParent::Close`` or ``PDemoChild::Close``). 712The only other way for a child process to terminate is to crash. Each of these 713three options requires some special handling. 714 715.. note:: 716 There is no need to consider the case where the parent (main) process 717 crashed, because the **Demo** process would be quickly terminated by Gecko. 718 719In cases where ``Close()`` is called, the shutdown procedure is fairly 720straightforward. Once the call completes, the actor is no longer connected to 721a channel -- messages will not be sent or received, as is the case with any 722normal top-level actor (or any managed actor after calling 723``Send__delete__()``). In the sample code, we ``Close`` the ``DemoChild`` 724when some (as yet unwritten) **Demo** process code calls 725``DemoChild::Shutdown``. 726 727.. code-block:: c++ 728 729 /* static */ 730 void DemoChild::Shutdown() { 731 if (gDemoChild) { 732 // Wait for the other end to get everything we sent before shutting down. 733 // We never want to Close during a message (response) handler, so 734 // we dispatch a new runnable. 735 auto dc = gDemoChild; 736 RefPtr<nsIRunnable> runnable = NS_NewRunnableFunction( 737 "DemoChild::FinishShutdown", 738 [dc2 = std::move(gDemoChild)]() { dc2->Close(); }); 739 dc->SendEmptyMessageQueue( 740 [runnable](bool) { NS_DispatchToMainThread(runnable); }, 741 [runnable](mozilla::ipc::ResponseRejectReason) { 742 NS_DispatchToMainThread(runnable); 743 }); 744 } 745 } 746 747The comment in the code makes two important points: 748 749* ``Close`` should never be called from a message handler (e.g. in a 750 ``RecvFoo`` method). We schedule it to run later. 751* If the ``DemoParent`` hasn't finished handling messages the ``DemoChild`` 752 sent, or vice-versa, those messages will be lost. For that reason, we have a 753 trivial sentinel message ``EmptyMessageQueue`` that we simply send and wait 754 to respond before we ``Close``. This guarantees that the main process will 755 have handled all of the messages we sent before it. Because we know the 756 details of the ``PDemo`` protocol, we know that this means we won't lose any 757 important messages this way. Note that we say "important" messages because 758 we could still lose messages sent *from* the main process. For example, a 759 ``RequestMemoryReport`` message sent by the MemoryReporter could be lost. 760 The actor would need a more complex shutdown protocol to catch all of these 761 messages but in our case there would be no point. A process that is 762 terminating is probably not going to produce useful memory consumption data. 763 Those messages can safely be lost. 764 765`Debugging Process Startup`_ looks at what happens if we omit the 766``EmptyMessageQueue`` message. 767 768We can also see that, once the ``EmptyMessageQueue`` response is run, we are 769releasing ``gDemoChild``, which will result in the termination of the process. 770 771.. code-block:: c++ 772 773 DemoChild::~DemoChild() { 774 // ... 775 XRE_ShutdownChildProcess(); 776 } 777 778At this point, the ``DemoParent`` in the main process is alerted to the 779channel closure because IPDL will call its :ref:`ActorDestroy <Actor Lifetimes 780in C++>` method. 781 782.. code-block:: c++ 783 784 void DemoParent::ActorDestroy(ActorDestroyReason aWhy) { 785 if (aWhy == AbnormalShutdown) { 786 GenerateCrashReport(OtherPid()); 787 } 788 // ... 789 } 790 791IPDL then releases its (sole) reference to ``DemoParent`` and the destruction 792of the process apparatus is complete. 793 794The ``ActorDestroy`` code shows how we handle the one remaining shutdown case: 795a crash in the **Demo** process. In this case, IPDL will *detect* the dead 796process and free the ``DemoParent`` actor as above, only with an 797``AbnormalShutdown`` reason. We generate a crash report, which requires crash 798reporter integration, but no additional "special" steps need to be taken. 799 800Creating a New Top Level Actor 801~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 802 803We now have a framework that creates the new process and connects it to the 804main process. We now want to make another top-level actor but this one will be 805responsible for our intended behavior, not just bootstrapping the new process. 806Above, we saw that this is started by ``Host::MakeBridgeAndResolve`` after the 807``DemoParent`` connection is established. 808 809.. code-block:: c++ 810 811 bool DemoParent::Host::MakeBridgeAndResolve() { 812 ipc::Endpoint<PDemoHelplineParent> parent; 813 ipc::Endpoint<PDemoHelplineChild> child; 814 815 auto resolveFail = MakeScopeExit([&] { mResolver(Nothing()); }); 816 817 // Parent side is first PID (main/content), child is second (demo). 818 nsresult rv = PDempHelpline::CreateEndpoints( 819 mParentPid, base::GetProcId(GetChildProcessHandle()), &parent, &child); 820 821 // ... 822 823 if (!mActor->SendCreateDemoHelplineChild(std::move(child))) { 824 NS_WARNING("Failed to SendCreateDemoHelplineChild"); 825 return false; 826 } 827 828 resolveFail.release(); 829 mResolver(Some(std::move(parent))); 830 return true; 831 } 832 833Because the operation of launching a process is asynchronous, we have 834configured this so that it creates the two endpoints for the new top-level 835actors, then we send the child one to the new process and resolve a promise 836with the other. The **Demo** process creates its ``PDemoHelplineChild`` 837easily: 838 839.. code-block:: c++ 840 841 mozilla::ipc::IPCResult DemoChild::RecvCreateDemoHelplineChild( 842 Endpoint<PDemoHelplineChild>&& aEndpoint) { 843 mDemoHelplineChild = new DemoHelplineChild(); 844 if (!aEndpoint.Bind(mDemoHelplineChild)) { 845 return IPC_FAIL(this, "Unable to bind DemoHelplineChild"); 846 } 847 return IPC_OK(); 848 } 849 850``MakeProcessAndGetAssistance`` binds the same way: 851 852.. code-block:: c++ 853 854 RefPtr<DemoHelplineParent> demoHelplineParent = new DemoHelplineParent(); 855 if (!endpoint.Bind(demoHelplineParent)) { 856 NS_WARNING("Unable to bind DemoHelplineParent"); 857 return false; 858 } 859 MOZ_ASSERT(ok); 860 861However, the parent may be in the main process or in content. We handle both 862cases in the next section. 863 864.. _Connecting With Other Processes: 865 866Connecting With Other Processes 867~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 868 869``DemoHelplineParent::MakeProcessAndGetAssistance`` is the method that we run 870from either the main or the content process and that should kick off the 871procedure that will result in sending a string (that we get from a new **Demo** 872process) to a DOM promise. It starts by constructing a different promise -- 873one like the ``mResolver`` in ``Host::MakeBridgeAndResolve`` in the last 874section that produced a ``Maybe<Endpoint<PDemoHelplineParent>>``. In the main 875process, we just make the promise ourselves and call 876``DemoParent::LaunchDemoProcess`` to start the procedure that will result in 877it being resolved as already described. If we are calling from the content 878process, we simply write an async ``PContent`` message that calls 879``DemoParent::LaunchDemoProcess`` and use the message handler's promise as 880our promise: 881 882.. code-block:: c++ 883 884 /* static */ 885 bool DemoHelplineParent::MakeProcessAndGetAssistance( 886 RefPtr<mozilla::dom::Promise> aPromise) { 887 RefPtr<LaunchDemoProcessPromise> resolver; 888 889 if (XRE_IsContentProcess()) { 890 auto* contentChild = mozilla::dom::ContentChild::GetSingleton(); 891 MOZ_ASSERT(contentChild); 892 893 resolver = contentChild->SendLaunchDemoProcess(); 894 } else { 895 MOZ_ASSERT(XRE_IsParentProcess()); 896 auto promise = MakeRefPtr<LaunchDemoProcessPromise::Private>(__func__); 897 resolver = promise; 898 899 if (!DemoParent::LaunchDemoProcess( 900 base::GetCurrentProcId(), 901 [promise = std::move(promise)]( 902 Maybe<Endpoint<PDemoHelplineParent>>&& aMaybeEndpoint) mutable { 903 promise->Resolve(std::move(aMaybeEndpoint), __func__); 904 })) { 905 NS_WARNING("Failed to launch Demo process"); 906 resolver->Reject(NS_ERROR_FAILURE); 907 return false; 908 } 909 } 910 911 resolver->Then( 912 GetMainThreadSerialEventTarget(), __func__, 913 [aPromise](Maybe<Endpoint<PDemoHelplineParent>>&& maybeEndpoint) mutable { 914 if (!maybeEndpoint) { 915 aPromise->MaybeReject(NS_ERROR_FAILURE); 916 return; 917 } 918 919 RefPtr<DemoHelplineParent> demoHelplineParent = new DemoHelplineParent(); 920 Endpoint<PDemoHelplineParent> endpoint = maybeEndpoint.extract(); 921 if (!endpoint.Bind(demoHelplineParent)) { 922 NS_WARNING("Unable to bind DemoHelplineParent"); 923 return false; 924 } 925 MOZ_ASSERT(ok); 926 927 // ... communicate with PDemoHelpline and write message to console ... 928 }, 929 [aPromise](mozilla::ipc::ResponseRejectReason&& aReason) { 930 aPromise->MaybeReject(NS_ERROR_FAILURE); 931 }); 932 933 return true; 934 } 935 936 mozilla::ipc::IPCResult ContentParent::RecvLaunchDemoProcess( 937 LaunchDemoProcessResolver&& aResolver) { 938 if (!DemoParent::LaunchDemoProcess(OtherPid(), 939 std::move(aResolver))) { 940 NS_WARNING("Failed to launch Demo process"); 941 } 942 return IPC_OK(); 943 } 944 945To summarize, connecting processes always requires endpoints to be constructed 946by the main process, even when neither process being connected is the main 947process. It is the only process that creates ``Endpoint`` objects. From that 948point, connecting is just a matter of sending the endpoints to the right 949processes, constructing an actor for them, and then calling ``Endpoint::Bind``. 950 951Completing the Sample 952~~~~~~~~~~~~~~~~~~~~~ 953 954We have covered the main parts needed for the sample. Now we just need to wire 955it all up. First, we add the new JS command to ``Navigator.webidl`` and 956``Navigator.h``/``Navigator.cpp``: 957 958.. code-block:: c++ 959 960 partial interface Navigator { 961 [Throws] 962 Promise<DOMString> getAssistance(); 963 }; 964 965 already_AddRefed<Promise> Navigator::GetAssistance(ErrorResult& aRv) { 966 if (!mWindow || !mWindow->GetDocShell()) { 967 aRv.Throw(NS_ERROR_UNEXPECTED); 968 return nullptr; 969 } 970 971 RefPtr<Promise> echoPromise = Promise::Create(mWindow->AsGlobal(), aRv); 972 if (NS_WARN_IF(aRv.Failed())) { 973 return nullptr; 974 } 975 976 if (!DemoHelplineParent::MakeProcessAndGetAssistance(echoPromise)) { 977 aRv.Throw(NS_ERROR_FAILURE); 978 return nullptr; 979 } 980 981 return echoPromise.forget(); 982 } 983 984Then, we need to add the part that gets the string we use to resolve the 985promise in ``MakeProcessAndGetAssistance`` (or reject it if it hasn't been 986resolved by the time ``ActorDestroy`` is called): 987 988.. code-block:: c++ 989 990 using DemoPromise = MozPromise<nsString, nsresult, true>; 991 992 /* static */ 993 bool DemoHelplineParent::MakeProcessAndGetAssistance( 994 RefPtr<mozilla::dom::Promise> aPromise) { 995 996 // ... construct and connect demoHelplineParent ... 997 998 RefPtr<DemoPromise> promise = demoHelplineParent->mPromise.Ensure(__func__); 999 promise->Then( 1000 GetMainThreadSerialEventTarget(), __func__, 1001 [demoHelplineParent, aPromise](nsString aMessage) mutable { 1002 aPromise->MaybeResolve(aMessage); 1003 }, 1004 [demoHelplineParent, aPromise](nsresult aErr) mutable { 1005 aPromise->MaybeReject(aErr); 1006 }); 1007 1008 if (!demoHelplineParent->SendRequestAssistance()) { 1009 NS_WARNING("DemoHelplineParent::SendRequestAssistance failed"); 1010 } 1011 } 1012 1013 mozilla::ipc::IPCResult DemoHelplineParent::RecvAssistance( 1014 nsString&& aMessage, const AssistanceResolver& aResolver) { 1015 mPromise.Resolve(aMessage, __func__); 1016 aResolver(true); 1017 return IPC_OK(); 1018 } 1019 1020 void DemoHelplineParent::ActorDestroy(ActorDestroyReason aWhy) { 1021 mPromise.RejectIfExists(NS_ERROR_FAILURE, __func__); 1022 } 1023 1024The ``DemoHelplineChild`` has to respond to the ``RequestAssistance`` method, 1025which it does by returning a string and then calling ``Close`` on itself when 1026the string has been received (but we do not call ``Close`` in the ``Recv`` 1027method!). We use an async response to the ``GiveAssistance`` message to detect 1028that the string was received. During closing, the actor's ``ActorDestroy`` 1029method then calls the ``DemoChild::Shutdown`` method we defined in `Destroying 1030the New Process`_: 1031 1032.. code-block:: c++ 1033 1034 mozilla::ipc::IPCResult DemoHelplineChild::RecvRequestAssistance() { 1035 RefPtr<DemoHelplineChild> me = this; 1036 RefPtr<nsIRunnable> runnable = 1037 NS_NewRunnableFunction("DemoHelplineChild::Close", [me]() { me->Close(); }); 1038 1039 SendAssistance( 1040 nsString(HelpMessage()), 1041 [runnable](bool) { NS_DispatchToMainThread(runnable); }, 1042 [runnable](mozilla::ipc::ResponseRejectReason) { 1043 NS_DispatchToMainThread(runnable); 1044 }); 1045 1046 return IPC_OK(); 1047 } 1048 1049 void DemoHelplineChild::ActorDestroy(ActorDestroyReason aWhy) { 1050 DemoChild::Shutdown(); 1051 } 1052 1053During the **Demo** process lifetime, there are two references to the 1054``DemoHelplineChild``, one from IPDL and one from the ``DemoChild``. The call 1055to ``Close`` releases the one held by IPDL and the other isn't released until 1056the ``DemoChild`` is destroyed. 1057 1058Running the Sample 1059~~~~~~~~~~~~~~~~~~ 1060 1061To run the sample, build and run and open the console. The new command is 1062``navigator.getAssistance().then(console.log)``. The message sent by 1063``SendAssistance`` is then logged to the console. The sample code also 1064includes the name of the type of process that was used for the 1065``DemoHelplineParent`` so you can confirm that it works from main and from 1066content. 1067 1068Debugging Process Startup 1069------------------------- 1070 1071Debugging a child process at the start of its life is tricky. With most 1072platforms/toolchains, it is surprisingly difficult to connect a debugger before 1073the main routine begins execution. You may also find that console logging is 1074not yet established by the operating system, especially when working with 1075sandboxed child processes. Gecko has some facilities that make this less 1076painful. 1077 1078.. _Debugging with IPDL Logging: 1079 1080Debugging with IPDL Logging 1081~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1082 1083This is also best seen with an example. To start, we can create a bug in the 1084sample by removing the ``EmptyMessageQueue`` message sent to ``DemoParent``. 1085This message was intended to guarantee that the ``DemoParent`` had handled all 1086messages sent before it, so we could ``Close`` with the knowledge that we 1087didn't miss anything. This sort of bug can be very difficult to track down 1088because it is likely to be intermittent and may manifest more easily on some 1089platforms/architectures than others. To create this bug, replace the 1090``SendEmptyMessageQueue`` call in ``DemoChild::Shutdown``: 1091 1092.. code-block:: c++ 1093 1094 auto dc = gDemoChild; 1095 RefPtr<nsIRunnable> runnable = NS_NewRunnableFunction( 1096 "DemoChild::FinishShutdown", 1097 [dc2 = std::move(gDemoChild)]() { dc2->Close(); }); 1098 dc->SendEmptyMessageQueue( 1099 [runnable](bool) { NS_DispatchToMainThread(runnable); }, 1100 [runnable](mozilla::ipc::ResponseRejectReason) { 1101 NS_DispatchToMainThread(runnable); 1102 }); 1103 1104with just an (asynchronous) call to ``Close``: 1105 1106.. code-block:: c++ 1107 1108 NS_DispatchToMainThread(NS_NewRunnableFunction( 1109 "DemoChild::FinishShutdown", 1110 [dc = std::move(gDemoChild)]() { dc->Close(); })); 1111 1112When we run the sample now, everything seems to behave ok but we see messages 1113like these in the console: :: 1114 1115 ###!!! [Parent][RunMessage] Error: (msgtype=0x410001,name=PDemo::Msg_InitCrashReporter) Channel closing: too late to send/recv, messages will be lost 1116 1117 [Parent 16672, IPC I/O Parent] WARNING: file c:/mozilla-src/mozilla-unified/ipc/chromium/src/base/process_util_win.cc:167 1118 [Parent 16672, Main Thread] WARNING: Not resolving response because actor is dead.: file c:/mozilla-src/mozilla-unified/ipc/glue/ProtocolUtils.cpp:931 1119 [Parent 16672, Main Thread] WARNING: IPDL resolver dropped without being called!: file c:/mozilla-src/mozilla-unified/ipc/glue/ProtocolUtils.cpp:959 1120 1121We could probably figure out what is happening here from the messages but, 1122with more complex protocols, understanding what led to this may not be so easy. 1123To begin diagnosing, we can turn on IPC Logging, which was defined in the IPDL 1124section on :ref:`Message Logging`. We just need to set an environment variable 1125before starting the browser. Let's turn it on for all ``PDemo`` and 1126``PDemoHelpline`` actors: :: 1127 1128 MOZ_IPC_MESSAGE_LOG="PDemoParent,PDemoChild,PDemoHelplineParent,PDemoHelplineChild" 1129 1130To underscore what we said above, when logging is active, the change in timing 1131makes the error message go away and everything closes properly on a tested 1132Windows desktop. However, the issue remains on a Macbook Pro and the log 1133shows the issue rather clearly: :: 1134 1135 [time: 1627075553937959][63096->63085] [PDemoChild] Sending PDemo::Msg_InitCrashReporter 1136 [time: 1627075553949441][63085->63096] [PDemoParent] Sending PDemo::Msg_CreateDemoHelplineChild 1137 [time: 1627075553950293][63092->63096] [PDemoHelplineParent] Sending PDemoHelpline::Msg_RequestAssistance 1138 [time: 1627075553979151][63096<-63085] [PDemoChild] Received PDemo::Msg_CreateDemoHelplineChild 1139 [time: 1627075553979433][63096<-63092] [PDemoHelplineChild] Received PDemoHelpline::Msg_RequestAssistance 1140 [time: 1627075553979498][63096->63092] [PDemoHelplineChild] Sending PDemoHelpline::Msg_GiveAssistance 1141 [time: 1627075553980105][63092<-63096] [PDemoHelplineParent] Received PDemoHelpline::Msg_GiveAssistance 1142 [time: 1627075553980181][63092->63096] [PDemoHelplineParent] Sending reply PDemoHelpline::Reply_GiveAssistance 1143 [time: 1627075553980449][63096<-63092] [PDemoHelplineChild] Received PDemoHelpline::Reply_GiveAssistance 1144 [tab 63092] NOTE: parent actor received `Goodbye' message. Closing channel. 1145 [default 63085] NOTE: parent actor received `Goodbye' message. Closing channel. 1146 [...] 1147 ###!!! [Parent][RunMessage] Error: (msgtype=0x420001,name=PDemo::Msg_InitCrashReporter) Channel closing: too late to send/recv, messages will be lost 1148 [...] 1149 [default 63085] NOTE: parent actor received `Goodbye' message. Closing channel. 1150 1151The imbalance with ``Msg_InitCrashReporter`` is clear. The message was not 1152*Received* before the channel was closed. Note that the first ``Goodbye`` for 1153the main (default) process is for the ``PDemoHelpline`` actor -- in this case, 1154its child actor was in a content (tab) process. The second default process 1155``Goodbye`` is from the **Demo** process, sent when doing ``Close``. It might 1156seem that it should handle the ``Msg_InitCrashReporter`` if it can handle the 1157later ``Goodbye`` but this does not happen for safety reasons. 1158 1159Early Debugging For A New Process 1160~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1161 1162Let's assume now that we still don't understand the problem -- maybe we don't 1163know that the ``InitCrashReporter`` message is sent internally by the 1164``CrashReporterClient`` we initialized. Or maybe we're only looking at Windows 1165builds. We decide we'd like to be able to hook a debugger to the new process 1166so that we can break on the ``SendInitCrashReporter`` call. Attaching the 1167debugger has to happen fast -- process startup probably completes in under a 1168second. Debugging this is not always easy. 1169 1170Windows users have options that work with both the Visual Studio and WinDbg 1171debuggers. For Visual Studio users, there is an easy-to-use VS addon called 1172the `Child Process Debugging Tool`_ that allows you to connect to *all* 1173processes that are launched by a process you are debugging. So, if the VS 1174debugger is connected to the main process, it will automatically connect to the 1175new **Demo** process (and every other launched process) at the point that they 1176are spawned. This way, the new process never does anything outside of the 1177debugger. Breakpoints, etc work as expected. The addon mostly works like a 1178toggle and will remain on until it is disabled from the VS menu. 1179 1180WinDbg users can achieve essentially the same behavior with the `.childdbg`_ 1181command. See the docs for details but essentially all there is to know is that 1182``.childdbg 1`` enables it and ``.childdbg 0`` disables it. You might add it 1183to a startup config file (see the WinDbg ``-c`` command line option) 1184 1185Linux and mac users should reference gdb's ``detach-on-fork``. The command to 1186debug child processes is ``set detach-on-fork off``. Again, the behavior is 1187largely what you would expect -- that all spawned processes are added to the 1188current debug session. The command can be added to ``.gdbinit`` for ease. At 1189the time of this writing, lldb does not support automatically connecting to 1190newly spawned processes. 1191 1192Finally, Linux users can use ``rr`` for time-travel debugging. See `Debugging 1193Firefox with rr`_ for details. 1194 1195These solutions are not always desirable. For example, the fact that they hook 1196*all* spawned processes can mean that targeting breakpoints to one process 1197requires us to manually disconnect many other processes. In these cases, an 1198easier solution may be to use Gecko environment variables that will cause the 1199process to sleep for some number of seconds. During that time, you can find 1200the process ID (PID) for the process you want to debug and connect your 1201debugger to it. OS tools like ``ProcessMonitor`` can give you the PID but it 1202will also be clearly logged to the console just before the process waits. 1203 1204Set ``MOZ_DEBUG_CHILD_PROCESS=1`` to turn on process startup pausing. You can 1205also set ``MOZ_DEBUG_CHILD_PAUSE=N`` where N is the number of seconds to sleep. 1206The default is 10 seconds on Windows and 30 on other platforms. 1207 1208Pausing for the debugger is not a panacea. Since the environmental varaiables 1209are not specific to process type, you will be forced to wait for all of the 1210processes Gecko creates before you wait for it to get to yours. The pauses can 1211also end up exposing unknown concurrency bugs in the browser before it even 1212gets to your issue, which is good to discover but doesn't fix your bug. That 1213said, any of these strategies would be enough to facilitate easily breaking on 1214``SendInitCrashReporter`` and finding our sender. 1215 1216.. _Child Process Debugging Tool: https://marketplace.visualstudio.com/items?itemName=vsdbgplat.MicrosoftChildProcessDebuggingPowerTool 1217.. _.childdbg: https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/-childdbg--debug-child-processes- 1218