codebytere

#53204: perf: keep requests off the main thread for type-filtered and observer-only webRequest listeners

Merged
Created: Aug 26, 2026, 8:55:32 AM
Merged: Aug 27, 2026, 4:21:16 PM
6 comments
Target: main

Description of Change

Renderer requests no longer wait for the main process when the only webRequest listeners that could match them are observers (onCompleted, onErrorOccurred, onSendHeaders, onBeforeRedirect, onResponseStarted) or are filtered to other resource types. Follow-up to #53115, whose IO-thread gate sent every request through the UI-thread ProxyingURLLoaderFactory as soon as a session had any listener, because a listener may modify the request and re-runs on each redirect leg, so its URL filter cannot be applied up front. Two things can be decided up front:

  • A request's resource type never changes across redirects, so the listeners' existing types filter is honored before the request reaches the UI thread, in the gate and in the proxy's pass-through path. A blocking listener registered with types: ['mainFrame', 'subFrame'] no longer pulls images, scripts and fetch() calls through the main process.
  • Observer listeners cannot change anything. When only observers match a request's type, it goes straight to the network service and a pass-through URLLoaderClient on the IO thread reports the redirect, response and completion to api::WebRequest afterwards. Listeners receive the same details (including one onSendHeaders per redirect leg); the renderer never waits for them.

InterceptState keeps two resource-type masks (blocking / observing) instead of a boolean, recomputed whenever a listener is added or removed.

Linux x64 release, page with 150 small subresources from a local server in a separate process, median of 3 warm loads; "busy" = the main process runs 100 ms of JavaScript right after the navigation commits. Before -> after:

listeners on the session page, idle page, busy fetch() round trip main-process CPU per request
none 53 -> 50 ms 152 -> 54 ms 0.46 -> 0.40 ms 146 -> 129 µs
onCompleted + onErrorOccurred + onSendHeaders 56 -> 46 ms 160 -> 55 ms 0.63 -> 0.46 ms 342 -> 244 µs
onBeforeRequest + onHeadersReceived, types: ['mainFrame', 'subFrame'] 57 -> 49 ms 161 -> 108 ms 0.60 -> 0.40 ms 309 -> 129 µs
both of the above 57 -> 48 ms 155 -> 108 ms 0.61 -> 0.47 ms 325 -> 236 µs
onBeforeSendHeaders, no filter 58 -> 54 ms 159 -> 163 ms 0.61 -> 0.65 ms 320 -> 342 µs

In the 108 ms rows the subresources are on the direct path but the document request itself matches the frame-typed blocking listeners, so its completion still goes through the main process; an unfiltered blocking listener is unchanged by design.

Checklist

Release Notes

Notes: Renderer network requests no longer waited on the main process when the session's webRequest listeners only observe requests (onCompleted, onErrorOccurred, onSendHeaders, onBeforeRedirect, onResponseStarted) or are filtered to other resource types.

Backports

42-x-y
Pending
Waiting for a manual backport
43-x-y
Pending
Waiting for a manual backport
44-x-y
Merged
PR Number
#53240
Merged At
Aug 27, 2026, 9:16:08 PM
Released In
Not yet
Release Date
Not yet

Semver Impact

Major
Breaking changes
Minor
New features
Patch
Bug fixes
None
Docs, tests, etc.

Semantic Versioning helps users understand the impact of updates:

  • Major (X.y.z): Breaking changes that may require code modifications
  • Minor (x.Y.z): New features that maintain backward compatibility
  • Patch (x.y.Z): Bug fixes that don't change the API
  • None: Changes that don't affect using facing parts of Electron