Firefox exposes removal operations for IndexedDB databases, object
stores, and records from the Storage panel. Route those actor requests
to LibWeb so DevTools can delete the selected data and receive the
update or clear event needed to refresh the panel.
The Storage panel expects storesUpdate messages after watched storage
changes. Summarize committed IndexedDB mutation logs into DevTools paths
and forward them through WebContent so Firefox can refresh database,
object-store, and record rows without polling.
Firefox's behaviour is less than ideal here. A lot of things don't
update automatically even inspecting a page in Firefox. Some
things (like new databases) won't show up until you fully refresh the
page. So that makes it a bit hard to know that we're doing things
correctly. As far as I can tell, we are at least behaving as well as
Firefox requires.
We do have one workaround: Firefox doesn't display record updates
without a manual refresh, and in fact any change messages for them show
up as rows in the host's database table. So for now, we filter them out
to avoid visual weirdness in the inspector.
Firefox asks the storage watcher for an indexed-db resource before it
shows IndexedDB entries in the Storage panel. Add an IndexedDB actor and
serialize the live LibWeb database registry on demand, so WebContent can
return the host tree and table rows without duplicating database state.
Use the LibWeb inspection helpers to read IndexedDB internals, and keep
the Firefox protocol shape in LibDevTools. WebContent only forwards the
serialized response over the existing DevTools IPC path.
Storage actors all describe table columns with the same DevTools field
object shape. Extract a helper for constructing those objects so that
cookies, local storage, session storage, and soon IndexedDB, do not each
spell out the JSON fields by hand.
Firefox can split a DevTools protocol packet across multiple TCP
reads. The previous reader only checked that some data was available,
then tried to synchronously read the whole length-prefixed packet from
the readiness callback. Once the socket was nonblocking this could make
startup flaky when Firefox opened the inspector.
Buffer incoming bytes instead, and only dispatch messages once a full
length-prefixed JSON payload has arrived. Add a protocol test that sends
a request in two fragments through the real DevTools server.
Firefox sends the same storage actor mutation requests for Web Storage
that it uses for cookies. Handle addItem, editItem, removeItem, and
removeAll for localStorage and sessionStorage actors.
Apply the changes through the browser-process storage jar and emit the
matching store update packets so the Storage panel stays current after a
DevTools-initiated edit.
Firefox keeps the Storage panel current by sending store update packets
when localStorage or sessionStorage changes. Forward successful Web
Storage mutations to the storage actors and emit the matching update or
clear packet.
Use listener IDs for storage updates so the local and session storage
actors can subscribe independently.
Firefox exposes localStorage and sessionStorage through the same
storage actor protocol used by cookies. Add matching resources for the
current tab so the Storage panel can list key/value pairs.
Read the values through WebContent rather than directly from the
browser process. Session storage lives in LibWeb, and using the same
path for both stores keeps the actor independent of the backing store.
Firefox enables the Storage panel cookie toolbar from the storage traits
and calls addItem, removeItem, removeAll, or removeAllSessionCookies
for the selected host.
Advertise those operations and route them through the existing cookie
mutation delegate. Deletions reuse the visible storage host matching so
bulk actions stay scoped to the cookie tree item Firefox selected.
Firefox sends editItem when a cookie table cell is changed. Handle
that request by locating the original cookie, applying the edited
field, and forwarding the replacement through the delegate mutation
hook.
Return Firefox-style errorString values so invalid edits can be
rejected without changing the table contents.
DevTools needs to edit and delete cookies from the browser process. Add
a small mutation surface to CookieJar and expose it through the DevTools
delegate while preserving the existing cookie notification path.
Firefox keeps the Storage table current through storesUpdate messages
from the storage actor. Forward browser cookie change notifications to
the cookies actor and classify visible rows against a fresh cookie
snapshot.
The existing cookie-change notifications report cookies that are
relevant to a specific page, whereas DevTools wants all cookies
relevant for a host, so we end up having to provide two different sets
of cookies to `notify_cookies_changed()`.
Firefox asks the cookies actor for rows after selecting a Storage host.
Read the browser cookie jar through the DevTools delegate and serialize
matching cookies with the fields Firefox expects.
This keeps mutation support disabled, but makes existing cookies visible
in the Storage panel.
Firefox discovers Storage panel data through watcher resources. This
adds a read-only cookies actor with the fields and empty store response
expected by the Storage panel.
Mutation traits are reported as unsupported for now.
DevTools storage resources all have a "host" key derived from the URL,
which we produce in `storage_host_for_url()`.
Resolve applied-rule stylesheet identities through StyleSheetsActor and
include Firefox parentStyleSheet, line, and column fields on rule forms.
Firefox can then show matched rules as stylesheet rules and open the
corresponding CSS source location from the Rules panel.
Collect the style rules that apply to an inspected element and expose
them through the existing DOM node inspection path. This gives Firefox's
Rules panel real rule forms instead of the previous empty getApplied
response.
Extend the existing DOM node inspection request with an options payload
and carry it through the browser process, WebView, IPC, and WebContent.
This lets later DevTools requests forward Firefox's top-level flags
without changing any inspected-node behavior yet.
DevTools server callbacks can outlive the server when deferred work is
queued during connection shutdown. Capture weak pointers for those tasks
and clear socket callbacks while the server is being destroyed so later
actor cleanup cannot touch freed state.
Add protocol coverage that destroys a server with deferred actor cleanup
pending, before adding the style-rule actor path that depends on this.
Enable -Wexit-time-destructors for all in-tree library targets and
update process-lifetime library statics so they no longer register
exit-time destructors. Long-lived caches, lookup tables, singleton
registries, and generated constants now use NeverDestroyed or leaked
references where the data is intended to live until process exit.
Update LibWeb, LibLine, and the binding generators so regenerated
sources follow the same rule instead of reintroducing destructed
statics.
Firefox's remote toolbox exposes an editable URL field for tab
descriptors. Submitting that field sends navigateTo to the tab
descriptor rather than the frame target.
Handle the request in the tab actor, route it through the DevTools
delegate, and reuse WebView's URL sanitizing and load path. Add protocol
coverage so the toolbar navigation request stays wired up.
Firefox's inspector toolbar sends goBack and goForward requests to the
tab descriptor when the descriptor advertises navigation support. Thread
those requests through the DevTools delegate and reuse the existing
WebView session-history traversal path.
Keep the legacy frame-target requests working too, matching Firefox's
window-global target compatibility surface. Add protocol coverage for
both entry points so the advertised trait and deltas stay locked down.
Firefox reloads remote targets through the tab descriptor's
reloadDescriptor request. We advertised support for that request, but
left it unhandled, so Firefox could clear inspector state without
causing Ladybird to reload the page.
Route reloadDescriptor, and the legacy frame target reload request,
through the DevTools delegate to WebView's reload path. This lets the
existing navigation notifications publish the replacement inspector root
after the reload completes.
Firefox keeps using the same walker after navigation and expects it to
publish the new document root.
Normal tab debugging also relies on target switching. Re-inspect the tab
when navigation finishes, replace the walker DOM data, and notify the
watcher that the previous top-level frame target was destroyed before a
fresh target becomes available.
Send target availability as a watcher event, not as the watchTargets
response, and give each navigation a new inner window id so Firefox can
associate the new target with the new document.
Firefox drops the inspector tree when a target starts navigating. Keep
navigation subscribed on the frame actor and forward the event to the
inspector so the walker can announce root-destroyed.
Clear walker DOM caches, pending mutations, picker state, and selected
node state at the same time so stale actors do not remain usable while
the new document is loading.
Firefox starts element picking by sending pick to the walker actor. Add
matching protocol handling so Ladybird can enter picker mode, send
hover, preview, pick, and cancel events, and serialize picked nodes in
the shape Firefox expects.
Picker hit testing can return text nodes, but Firefox only selects
elements in this mode. Normalize picker events to the closest element
ancestor and include the parent chain so Firefox can materialize missing
tree nodes before selecting the target.
Firefox creates a new custom highlighter actor for each
getHighlighterByType request. Its frontend decides when to cache and
reuse those actors, while grid highlighting uses distinct actors so
multiple grids and subgrid parent overlays can remain visible at the
same time.
Match that allocation model by removing InspectorActor's server-side
highlighter cache. Also unregister highlighter actors after release or
finalize, and make each actor clear only the highlight state it owns.
Firefox's grid inspector builds its subgrid tree from two protocol
signals: isSubgrid on each GridActor form, and
Walker.getParentGridNode() for each subgrid container. Teach the walker
to answer the parent grid lookup so Firefox can attach subgrids below
their parent grid in the Layout panel.
Create typed highlighter actors for Firefox's highlighter requests.
The actor keeps the requested type so CssGridHighlighter can use grid
highlighting while the other highlighter types keep the existing box
model-compatible behavior.
Teach the walker to vend a layout inspector actor and serialize node
display types.
The layout inspector now returns Firefox-compatible grid actor forms
for getGrids and getCurrentGrid. It also supports Firefox's fallback
walker.getNodeFromActor(grid, ["containerEl"]) path, which the grid
panel uses when a grid form does not already contain a container node
actor ID.
Add IPC structures and requests for inspecting grid layout data from
WebContent.
WebView forwards the async replies to LibDevTools and exposes the grid
highlighter hooks used by the protocol layer.
Parse Firefox grid highlighter options in WebContent and store them with
the active grid highlight.
We have a common pattern of creating a `WeakPtr<T>` from a reference and
passing that into a lambda, to then take the strong ref when the lambda
is executed. Add `weak_callback(Weakable, lambda)` that returns a lambda
that only invokes the callback if a strong ref exists, and passes it as
the first argument.
LibJS+DevTools: Implement console.trace() with source locations
- Add Console::TraceFrame struct with source location data
- Implement Console::trace() to gather stack information
- Add WebView::StackFrame and ConsoleTrace for IPC
- Implement DevToolsConsoleClient::printer() for traces
- Update FrameActor to format traces for DevTools
- Update WorkerDebugConsoleClient trace handling
- Update ReplConsoleClient to format trace output
To avoid unnecessary IPC traffic, we now only send network response
bodies when a DevTools client is connected.
This requires tracking DevTools connection state in ViewImplementation
so we can propagate it to new WebContent processes created during
cross-site navigation.
Use blocking mode for socket writes to ensure large response bodies
(like 1MB+ HTML pages) are fully written without EAGAIN errors. The
socket is temporarily set to blocking mode during the write operation.
Also improve error handling by logging failed sends with the message
size and error details.
Additionally, when response content claims to be text but isn't valid
UTF-8, fall back to base64 encoding instead of returning empty content.
Propagate the request initiator type (e.g., "xmlhttprequest", "fetch",
"script", "stylesheet") from LibWeb through the IPC layer to DevTools.
This enables Firefox DevTools to correctly identify XHR/fetch requests
and display appropriate cause types in the Network panel's "Initiator"
column.
This adds support for viewing request payloads (POST data) and response
bodies in the Firefox DevTools network panel.
Request bodies are captured when network requests start and passed
through IPC to the NetworkEventActor, which returns them via the
getRequestPostData protocol method.
Response bodies are streamed via a new IPC message as data is received,
accumulated in NetworkEventActor (with a 10MB size limit to prevent
memory issues), and returned via getResponseContent. Text content is
returned as UTF-8, while binary content (images, etc.) is base64.
Previously, console messages were sent using an index-based system where
DevTools would be notified of new message indices and then request them
in batches. This created synchronization issues during page navigation
when the WebContent process resets while DevTools still has stale index
state.
This changes to a push-based model where console messages are sent
immediately as resources when they are logged, matching how Firefox
DevTools handles console messages. Each message is pushed through IPC
and forwarded to DevTools as a "console-message" or "error-message"
resource.
This eliminates the need for index tracking in FrameActor and simplifies
the entire console message pipeline from WebContent through to DevTools.
When a page navigates, send document-event resources with
"will-navigate" and tabNavigated messages so Firefox DevTools
can follow along and clear the Network panel appropriately.
Hook ResourceLoader to emit network request lifecycle events through
IPC to the UI process, where FrameActor creates NetworkEventActor
instances that serialize requests using Firefox's Remote Debug Protocol.
The Network panel now shows requests with method, URL, status, MIME
type, size, and timing information. Several features remain stubbed
(POST data, response content, cause detection) marked with FIXMEs.
This is enough for Firefox to display the Accessibility tab, containing
our accessibility tree which can be inspected. Most information is
blank for now.
There's quite a bit of duplication between AccessibilityWalkerActor and
WalkerActor - it might be worth trying to make a base class once the
details are figured out. Frustratingly, the two don't work quite the
same: for a lot of messages that would be sent to WalkerActor, the
accessibility equivalent is sent to the AccessibilityNodeActor instead.
Co-authored-by: Tim Flynn <trflynn89@pm.me>
Width and height are doubles, so get_string() will return nothing and
fail. We just want a JsonValue here without caring what type it is, so
let's just use get() instead.
We were ignoring the dom_element_unique_id field, which meant if we had
more than one inline style sheet on a page, we would not distinguish
between them. This had the effect of halting the style sheet loading
process in the dev tools, with none appearing.
Mark UA style sheets as "system", which makes them read-only in the
Firefox dev tools. Also give them an href with a fake resource:// URL so
that they don't appear as "<inline style sheet>", and don't set the href
for actual inline style sheets, for the opposite reason.
I've kept the title for UA style sheets, because that means we see
`Thing/Default.css` instead of just `Default.css` for all of them.