Skip to content

Bug 2065545 - [firefox-devtools-mcp] Add a tool to disable the network cache - #168

Open
shoemoney wants to merge 1 commit into
mozilla:mainfrom
shoemoney:bug-2065545-network-cache
Open

Bug 2065545 - [firefox-devtools-mcp] Add a tool to disable the network cache#168
shoemoney wants to merge 1 commit into
mozilla:mainfrom
shoemoney:bug-2065545-network-cache

Conversation

@shoemoney

Copy link
Copy Markdown
Contributor

Adds a set_network_cache tool so an agent can send every request to the network instead of the cache:

{ "behavior": "bypass" }                    // selected tab
{ "behavior": "bypass", "scope": "global" } // browser-wide
{ "behavior": "default" }                   // restore

Per your note on the bug, this is a tool rather than a config, and it is scoped to the selected tab by default with scope: "global" as the opt-in. It joins the existing network module, so it arrives with the developer preset alongside the profiler tools it is meant to be used with.

One correction to the bug, with evidence

The bug says:

WebDriver BiDi supports the emulation.setNetworkConditions command, which allows to bypass the cache.

That command does exist in Firefox, but it has no cache parameter — it is offline emulation. Probed against Firefox 154 through this repo's own BiDi transport:

ERR   emulation.setNetworkConditions {contexts, cacheDisabled:true}  -> invalid argument
ERR   emulation.setNetworkConditions {contexts, offline:false}       -> invalid argument
ERR   emulation.setNetworkConditions {contexts, latency:0}           -> invalid argument
ERR   emulation.setNetworkConditions networkConditions:"offline"     -> invalid argument
OK    emulation.setNetworkConditions networkConditions:{type:"offline"}
OK    emulation.setNetworkConditions networkConditions:null

The only accepted values are {type: "offline"} and null.

I checked that invalid argument really meant "exists, wrong params" rather than "no such command", because reading it the other way would have sent me down the wrong path:

ERR   bogus.noSuchCommand                    -> unknown command
ERR   emulation.noSuchThing                  -> unknown command
ERR   emulation.setLocaleOverride {}         -> invalid argument   (real command, bad params)

The command that actually bypasses the cache is network.setCacheBehavior, with cacheBehavior: "bypass" | "default" and an optional contexts — omit it for global, pass one for per-tab. That is what this PR wraps.

Measured, not assumed

A local server serving asset.js with Cache-Control: public, max-age=86400, counting real server hits across 3 page loads:

state 3 loads → server hits
baseline (default) 1 — cached
setCacheBehavior('bypass'), tab-scoped 3 — every load hit the network
setCacheBehavior('default') restore 0 — served from cache again
setCacheBehavior('bypass', {global:true}) 3

Both scopes work, and the effect is reversible.

Rejecting rather than defaulting

An unrecognised behavior or scope returns an error instead of falling back:

behavior must be one of default, bypass (got "disabled")
scope must be 'tab' or 'global' (got "everything")

Defaulting to default when someone asked for disabled would silently leave the cache on and quietly invalidate whatever they were measuring — the exact failure this tool exists to prevent. bypass also echoes back how to undo it, since the setting persists until reset:

cache bypass (selected tab) — set behavior='default' to restore caching

Tests

Six new unit tests in tests/tools/network.test.ts, all failing on unmodified main:

× exposes a behavior enum and an optional scope
× scopes to the selected tab by default
× applies browser-wide when scope is global
× tells the caller how to restore caching after a bypass
× rejects an unknown behavior instead of defaulting to cached
× rejects an unknown scope instead of silently using the tab

No integration test: the cache-bypass assertion needs a local HTTP server with a cacheable asset and a hit counter, which is a new pattern for tests/integration/ (every file there uses file:// fixtures, and file:// cannot exercise the HTTP cache). Happy to add one if you would like that pattern introduced — the measurements above came from exactly that harness, so it is written.

npm run lint            clean
npm run format:check    clean
npm run typecheck       clean
npm run typecheck:tests clean
npm run build           clean
npm run test:unit       43 files, 601 tests passed

Left alone deliberately

emulation.setNetworkConditions with {type: "offline"} is a genuinely useful thing to expose — offline-mode testing — but it is a different feature from this bug and would want its own tool and its own name. Happy to follow up if you want it.

…k cache

Adds set_network_cache, wrapping the BiDi network.setCacheBehavior command.
behavior='bypass' sends every request to the network; 'default' restores
normal caching. Scoped to the selected tab by default, browser-wide with
scope='global', as the bug asks for.

Joins the existing network module, so it comes with the developer preset
alongside the profiler tools it is meant to be used with.

Unknown behavior and scope values are rejected rather than defaulted:
silently caching when the caller asked to bypass would quietly invalidate
whatever they were measuring.

Note the bug names emulation.setNetworkConditions. That command exists in
Firefox but takes {type: "offline"} or null and has no cache parameter --
it is offline emulation, not cache control. network.setCacheBehavior is the
command that bypasses the cache; details and measurements in the PR.

@juliandescottes juliandescottes left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the patch, works nicely. Just one small comment to address.

Comment thread src/firefox/index.ts
Comment on lines +17 to +28
/**
* WebDriver BiDi network.CacheBehavior.
* - "default": normal HTTP cache behaviour
* - "bypass": skip the cache, so every request goes to the network
*/
export const CACHE_BEHAVIORS = ['default', 'bypass'] as const;

export type CacheBehavior = (typeof CACHE_BEHAVIORS)[number];

export function isCacheBehavior(value: unknown): value is CacheBehavior {
return CACHE_BEHAVIORS.includes(value as CacheBehavior);
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels a bit odd in index.ts. Could we move this to another module, eg create a src/firefox/cache.ts ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants