Firefox IPC: How Processes Communicate via IPDL

This article provides a quick overview of Inter-Process Communication (IPC) in the Firefox web browser. It covers the fundamentals of Firefox's multi-process architecture, defines the IPC Protocol Definition Language (IPDL), and explains the exact mechanisms child processes use to securely and efficiently communicate with the parent process.

The Firefox Multi-Process Architecture

Modern Firefox uses a multi-process architecture to separate the core browser interface from untrusted web content. This separation ensures stability and security.

The architecture relies on two main types of processes:

  • The Parent Process: Also known as the main or broker process. It runs with high privileges, manages the browser’s user interface, and has direct access to operating system resources like the file system and network.
  • Child Processes: These run in restricted environments called sandboxes. Different child processes handle specific tasks, such as rendering web content, decoding media, or managing GPU operations.

Because child processes are locked inside a security sandbox, they cannot directly access the hard drive, display graphics, or fetch network data. Instead, they must use Inter-Process Communication (IPC) to ask the parent process to perform these privileged actions on their behalf.

What is IPDL?

To manage IPC safely and efficiently, Mozilla created IPDL (IPC Protocol Definition Language). IPDL is a domain-specific language used strictly in Firefox to declare the exact messages and data structures that processes are allowed to send to one another.

During the Firefox build process, an IPDL compiler translates .ipdl files into standard C++ code. Using IPDL instead of writing raw C++ communication code provides two major benefits:

  1. Boilerplate Reduction: It automatically generates the complex code required to pack and unpack data for transport across process boundaries.
  2. Security: It enforces strict type checking. If a compromised child process sends corrupted or unexpected data, the generated IPDL code detects the anomaly and immediately terminates the child process before it can exploit the parent process.

The Actor Model

Communication in IPDL is structured around the "Actor" model. Every IPDL protocol defines a pair of communicating endpoints: a Parent Actor and a Child Actor.

  • The Child Actor lives in the child process.
  • The Parent Actor lives in the parent process.

For every protocol defined in an .ipdl file (for example, PContent), the IPDL compiler generates two C++ classes: ContentParent and ContentChild. These two actors form a dedicated communication bridge. A child process talks to the parent process by calling methods on its Child actor, which are seamlessly routed to the corresponding Parent actor.

How Child Processes Talk to the Parent Process

The actual communication between a child process and the parent process happens through a structured, step-by-step lifecycle:

1. Protocol Declaration Developers define the rules of communication in an .ipdl file. They specify the message names, the data types being passed (parameters), and the direction of the message (from child to parent, parent to child, or both).

2. Calling the Send Method When a child process needs to talk to the parent—for example, to request cookie data—the C++ code in the child process calls a generated Send method on its Child Actor (e.g., SendGetCookieString()).

3. Serialization The IPDL-generated C++ code takes the arguments passed into the Send method and serializes them. Serialization is the process of converting complex C++ data structures (like strings, arrays, or structs) into a flat stream of raw bytes.

4. OS-Level Transport Once the message is converted into bytes, it must physically cross the process boundary. Firefox uses the Chromium IPC library under the hood to handle the low-level transport. The byte stream is pushed through an operating-system-specific data pipe (Named Pipes on Windows, socketpair on Linux, and Mach ports on macOS).

5. Deserialization and Validation The parent process constantly listens to its end of the OS pipe. When the byte stream arrives, the IPDL-generated code in the parent process intercepts it and deserializes the bytes back into standard C++ data structures. If the bytes do not match the exact format declared in the .ipdl file, the message is rejected, and the child process is crashed for security reasons.

6. Executing the Receive Method Once the data is validated, the Parent Actor triggers a corresponding Recv method in the parent process (e.g., RecvGetCookieString()). The parent process executes the requested privileged operation and, if necessary, sends a response back down the pipe using the exact same serialization process.

Asynchronous vs. Synchronous Messages

IPDL supports two primary modes of communication for child-to-parent messages:

  • Asynchronous (async): This is the default and preferred method. The child process sends a message to the parent and immediately continues doing other work without waiting for an answer. When the parent finishes processing the request, it sends an asynchronous reply back, which the child handles via a callback or a Promise. This prevents processes from freezing.
  • Synchronous (sync): The child process sends a message and completely halts its own execution until the parent process replies. Because this blocks the child process from rendering web pages or responding to user input, synchronous messages are heavily discouraged and used only when absolutely necessary.

To keep the browser responsive, IPDL enforces a strict directional rule for synchronous messages: a child can block while waiting for the parent, but the parent process is almost never allowed to block while waiting for a child. This ensures that even if a web page freezes in a child process, the main Firefox user interface remains fast and responsive.