|
| 1 | +# Developing DevTools |
| 2 | + |
| 3 | +## Concepts |
| 4 | + |
| 5 | +Read the complete [protocol description](https://firefox-source-docs.mozilla.org/devtools/backend/protocol.html) for an in-depth look at all of the important concepts. |
| 6 | + |
| 7 | +- **Client:** Frontend that contains different tooling panels (Inspector, Debugger, Console, ...) and sends requests to the server. |
| 8 | + At the moment this is the `about:debugging` page in Firefox. |
| 9 | +- **Server:** Browser that is being inspected by the client. Receives messages and delivers them to the appropiate actor so it can reply. |
| 10 | +- **Actor:** Code on the server that can exchange messages with the client. |
| 11 | +- **Message:** JSON packet that is exchanged between the server and the client. |
| 12 | + - Messages from the client must include a `to` field with the name of the actor they are directed to, and a `type` field specifying what sort of packet it is. |
| 13 | + - Messages from the server must include a `from` field with the name of the actor that sends them. |
| 14 | + |
| 15 | +```mermaid |
| 16 | +sequenceDiagram |
| 17 | + participant Client |
| 18 | + participant Server |
| 19 | + actor Actor1 |
| 20 | + Client->>Server: {"to": "Actor1", "type": "SayHi"} |
| 21 | + Server-->>Actor1: {"to": "Actor1", "type": "SayHi"} |
| 22 | + Actor1-->>Server: {"from": "Actor1", "content": "hi!"} |
| 23 | + Server->>Client: {"from": "Actor1", "content": "hi!"} |
| 24 | +``` |
| 25 | + |
| 26 | +## Displaying protocol traffic |
| 27 | + |
| 28 | +<div class="warning _note"> |
| 29 | + |
| 30 | +Jump to the [Capturing and processing protocol traffic](#capturing-and-processing-protocol-traffic) section for a more useful tool for log analysis. |
| 31 | +</div> |
| 32 | + |
| 33 | +### Servo ↔ Firefox |
| 34 | + |
| 35 | +Servo can show the messages sent and received from the DevTools server. |
| 36 | +Enable the correct loging level for `devtools` and you are set: |
| 37 | + |
| 38 | +```sh |
| 39 | +RUST_LOG="error,devtools=debug" ./mach run --devtools=6080 |
| 40 | +``` |
| 41 | + |
| 42 | +The output has messages sent (prefixed by `<-`) and messages received (no prefix). |
| 43 | +Here we can see how Servo sends the initial connection information and Firefox replies with a request to connect and its version number. |
| 44 | + |
| 45 | +``` |
| 46 | +[2025-11-07T11:37:35Z INFO devtools] Connection established to 127.0.0.1:47496 |
| 47 | +[2025-11-07T11:37:35Z DEBUG devtools::protocol] <- {"from":"root","applicationType":"browser","traits":{"sources":false,"highlightable":true,"customHighlighters":true,"networkMonitor":true}} |
| 48 | +[2025-11-07T11:37:35Z DEBUG devtools::protocol] {"type":"connect","frontendVersion":"144.0.2","to":"root"} |
| 49 | +[2025-11-07T11:37:35Z DEBUG devtools::protocol] <- {"from":"root"} |
| 50 | +``` |
| 51 | + |
| 52 | +### Firefox ↔ Firefox |
| 53 | + |
| 54 | +A lot of work to improve developer tool support in Servo requires **reverse-engineering** the working implementation in Firefox. |
| 55 | +One of the most efficient ways to do this is to observe a successful session in Firefox and record the bidirectional protocol traffic between the server and the client. |
| 56 | + |
| 57 | +<div class="warning _note"> |
| 58 | + |
| 59 | +#### On the first run |
| 60 | + |
| 61 | +1. Create a new Firefox profile using `firefox --createprofile devtools-testing`. |
| 62 | +1. Launch Firefox with `firefox --new-instance -P devtools-testing`. |
| 63 | +1. Open about:config and click on "Accept the Risk and Continue". |
| 64 | +1. Change the following configuration: |
| 65 | + |
| 66 | +```sh |
| 67 | +# To see logs in the terminal window |
| 68 | +browser.dom.window.dump.enabled = true |
| 69 | +devtools.debugger.log = true |
| 70 | +devtools.debugger.log.verbose = true |
| 71 | +# To enable debugging |
| 72 | +devtools.chrome.enabled = true |
| 73 | +devtools.debugger.remote-enabled = true |
| 74 | +# Optional, avoids having to confirm every time there is a connection |
| 75 | +devtools.debugger.prompt-connection = false |
| 76 | +``` |
| 77 | +</div> |
| 78 | + |
| 79 | +After Firefox is configured, it can be launched from the terminal starting the DevTools server: |
| 80 | + |
| 81 | +```sh |
| 82 | +firefox --new-instance --start-debugger-server 6080 -P devtools-testing |
| 83 | +# (on macOS you may need `/Applications/Firefox.app/Contents/MacOS/firefox`) |
| 84 | +``` |
| 85 | + |
| 86 | +In this case it is possible to use the same Firefox instance as a client and a server. However, it is not recommended to use "This Firefox", as that doesn't give you access to tabs and messages could be different. |
| 87 | +Instead, whether you are using the same or a different instance, follow the steps outlined in the [Connecting to Servo](https://book.servo.org/hacking/using-devtools.html#connecting-to-servo) section, skipping the first one. |
| 88 | + |
| 89 | +The terminal window now contains full debug server logs; copy them to somewhere for further analysis. |
| 90 | + |
| 91 | +## Capturing and processing protocol traffic |
| 92 | + |
| 93 | +We have seen a simple way of obtaining message logs from Servo and Firefox. |
| 94 | +However, this soon turns complex when wanting to compare logs between the two due to the different formats or performing queries on them. |
| 95 | +There is a small script to make this process easier: [`etc/devtools_parser.py`](https://github.com/servo/servo/blob/main/etc/devtools_parser.py). |
| 96 | + |
| 97 | +It is based on [Wireshark](https://www.wireshark.org/), a powerful network packet analyzer; more specifically its cli, `tshark`. |
| 98 | +It is configured to log packets sent on your local network on the port that the DevTools server is running. |
| 99 | +It can read the payloads from these packets, which are small bits of the JSON DevTools protocol. |
| 100 | + |
| 101 | +**`tshark` needs to be installed** for the script to work. |
| 102 | +Install it with your package manager or get the full Wireshark release from [the official website](https://www.wireshark.org/download.html). |
| 103 | + |
| 104 | +```sh |
| 105 | +# Linux (Debian based) |
| 106 | +sudo apt install tshark |
| 107 | +# Linux (Arch based) |
| 108 | +sudo pacman -S wireshark-cli |
| 109 | +# Linux (Fedora) |
| 110 | +sudo dnf install wireshark-cli |
| 111 | +# MacOS (With homebrew): |
| 112 | +brew install --cask wireshark |
| 113 | +# Windows (With chocolatey): |
| 114 | +choco install wireshark |
| 115 | +``` |
| 116 | + |
| 117 | +You may need to add your user to the wireshark group to allow for rootless captures. Use `usermod -a -G wireshark $USER`. |
| 118 | + |
| 119 | +Finally, make sure to [set up a Firefox profile for debugging](#on-the-first-run). |
| 120 | + |
| 121 | +### Capture a session |
| 122 | + |
| 123 | +1. Run either Servo or Firefox with the DevTools server enabled: |
| 124 | + |
| 125 | +```sh |
| 126 | +./mach run --devtools 6080 |
| 127 | +firefox --new-instance --start-debugger-server 6080 -P devtools-testing |
| 128 | +``` |
| 129 | + |
| 130 | +2. In another terminal, start the script in capture mode (`-w`), specifying the same port as before: |
| 131 | + |
| 132 | +``` |
| 133 | +./etc/devtools_parser.py -p 6080 -w capture.pcap |
| 134 | +``` |
| 135 | + |
| 136 | +3. Connect from `about:debugging` following [the same steps](using-devtools.md#connecting-to-servo). |
| 137 | +4. Perform any actions you want to record. |
| 138 | +5. Press `Ctrl-C` in the terminal running the parser to stop the recording. This will do two things: |
| 139 | + - Save the results to a `.pcap` file specified by the `-w` flag. |
| 140 | + This is a binary file format for Wireshark, but we can read it later with the same tool. |
| 141 | + - Print the message log. |
| 142 | + There are two modes: the regular one, where it prints the messages in a friendly way, and `--json`, which emits newline-separated JSON with each message. |
| 143 | +6. You can now close Servo or Firefox. |
| 144 | + |
| 145 | +### Read a capture |
| 146 | + |
| 147 | +It is useful to save multiple captures and compare them later. |
| 148 | +While `tshark` saves them by default in the `.pcap` format, we can use the same script to get better output from them. |
| 149 | +Here the `--json` option is very useful, as it makes it possible to use tools like [`jq`](https://jqlang.org/) or [`nushell`](https://www.nushell.sh/) to query and manipulate data from them. |
| 150 | + |
| 151 | +```sh |
| 152 | +# Pretty print the messages |
| 153 | +./etc/devtools_parser.py -r capture.pcap |
| 154 | +# Save the capture in an NDJSON format |
| 155 | +./etc/devtools_parser.py -r capture.pcap --json > capture.json |
| 156 | +# Example of a query with jq to get unique message types |
| 157 | +./etc/devtools_parser.py -r capture.pcap --json | jq -cs 'map({actor: (.from//.to) | gsub("[0-9]";""), type: .type} | select(.type != null)) | .[]' | sort -u |
| 158 | +``` |
| 159 | + |
| 160 | +<div class="warning _note"> |
| 161 | + |
| 162 | +It is possible to save a JSON capture from the beginning using `./etc/devtools_parser.py -w capture.pcap --json > capture.json`. |
| 163 | +</div> |
| 164 | + |
| 165 | +Here is an excerpt from the output of a capture: |
| 166 | + |
| 167 | +```json |
| 168 | +{"to": "root", "type": "getRoot"} |
| 169 | +{"from": "root", "deviceActor": "device1", "performanceActor": "performance0", "preferenceActor": "preference2", "selected": 0} |
| 170 | +{"to": "device1", "type": "getDescription"} |
| 171 | +{"from": "device1", "value": {"apptype": "servo", "version": "0.0.1", "appbuildid": "20251106175140", "platformversion": "133.0", "brandName": "Servo"}} |
| 172 | +``` |
0 commit comments