Use this when working on the VS Code integrated browser ("browserView") to understand its architecture and mental model. Covers the embedded Chromium browser, its editor tab, navigation, overlay/layout, sessions, and agent browser tools under `src/vs/platform/browserView` and `src/vs/workbench/contrib/browserView`.
86%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
The integrated browser ("browserView") embeds a real Chromium browser in VS Code, backed by an Electron WebContentsView. It renders live pages, presents each as an editor tab, and lets agents drive those pages through tools. It powers the in-product browser tab and the agent "browser" tools. It is not the old extensions/simple-browser (an iframe-in-a-webview), which now delegates to this on desktop.
It's a heavyweight, security-sensitive, multi-process primitive, and almost every design decision follows from that. This file describes the load-bearing ideas that rarely change. It deliberately does not enumerate current features/tools/commands/settings — those churn; the live features/ and tools/ folders are the source of truth. Build the mental model here, then go read the specific code you're changing.
A page is a native WebContentsView that only the main process may create, own, and position. Nothing else can touch it directly. It's owned by the main process and painted by the OS compositor on top of the workbench DOM — not inside it. Everything else works around this:
| Process | Location | What lives here |
|---|---|---|
| Main | platform/browserView/electron-main | The WebContentsView, sessions, trust, permissions, history, CDP, screenshots — authoritative page state. Only main can create native views. |
| Shared | platform/browserView/node | Playwright + remote/group automation services. Keeps a heavy dependency out of main (stability) and renderer (lifecycle). |
| Renderer | workbench/contrib/browserView + platform/browserView/electron-browser | Editor pane, UI feature contributions, agent tools, page preload script. Holds only lightweight proxies. |
Layer rule: platform must not import workbench; the agent host can't import workbench; shared types belong in platform/common. The renderer reaches main/shared through ProxyChannel IPC, never by importing implementations. Browser events share one eager, host-window-scoped catch-all subscription; the workbench applies its initial snapshot before routing live changes to model-local events. Buffers travel as tuple/array elements alongside event metadata, not nested inside JSON objects. Channels are registered in electron-main/app.ts and electron-utility/sharedProcess/sharedProcessMain.ts. Split: page ops/state → main; agent automation → shared; CDP plumbing is its own channel both renderer and shared use to reach main.
The renderer model is a read replica of state owned by the main-process view. Flow is one-directional:
renderer feature ──command──▶ model ──IPC──▶ main BrowserView ──▶ Chromium
▲ │
└──────────── event (state changed) ◀──────────┘To do something, call a method (round-trips to main); to react, listen to a model event. Never compute page state in the renderer — it has no access to the web contents.
BrowserView, emit a change event, then mirror the field + event on the renderer model and add it to the typed forwarding map in BrowserViewMainService. Forwarding must be installed before navigation; do not add per-browser IPC subscriptions. Each window subscription starts with its current snapshot, which defines workbench readiness without a separate initialization RPC. The workbench owns each browser's emitter map, passes it into the model, and dispatches directly by browser ID and event name; models only consume events.Wanting the renderer to "just read" something off the page is the signal you need new mirrored state + an event from main.
The single most error-prone area — the cause of nearly every "won't move / misaligned / shows through a menu / won't focus" bug. The page is painted by main in screen coordinates on top of the renderer, so the workbench fakes a normal DOM element. Treat the page's rectangle, visibility, focus, and imagery as things the workbench coordinates, never DOM facts:
A page is a normal editor — a read-only, serializable EditorInput + EditorPane on the standard registry, resolving lazily to a view-model (unloadable without closing the tab). This inherits tabs, splitting, persistence, focus, and keybinding scoping for free.
The pane is thin. Behavior is added via a local contribution model (separate from workbench contributions): small classes that attach to the editor, get DI, and hook a fixed lifecycle (model attach/detach, layout overrides, resize/visibility, focus, UI insertion). Each gets a lifetime scoped to the attached model, so per-page disposables clean up on navigate/close. Even native-view rendering is just one contribution.
To add behavior, write a new contribution modeled on a sibling — don't grow the editor or the main view. Contributions affecting the page rect compose through prioritized layout overrides (lower runs first; e.g. emulation sizes the viewport, pixel-snap runs last), so order matters — never hard-code pixels.
Keep these three distinct:
BrowserSession (cookies, cache, storage), and its id doubles as the CDP browser-context id. Scoped global, per-workspace, or ephemeral; multiple tabs can share one. Security is enforced here: file://, certificate trust, and permissions are all gated at the session (e.g. local files need workspace trust). New capabilities that expand a page's reach belong here, not on a feature.Cookies/login/storage → sessions. "Which pages can this client see" → groups. The protocol itself → the proxy.
WebContentsView the user sees, via CDP, one connection per chat session. Human and agent actions can collide; conflicts resolve in the user's favor (a human prompt/dialog can interrupt automation). The workbench (not Playwright) owns device emulation, so Playwright's auto-emulation is suppressed except during an agent action. Assume a human may interact with the same page concurrently.platform so the agent host, which can't depend on workbench, can reference them.)A browser context (its storage) is bound to a network: local, or remote by authority: the window's remote (remote workspace windows) or a remote agent host (Agents window sessions). Tabs save their requested session (storage scope, affinity and network); creation applies current trust and proxy restrictions without overwriting that request. Popups and automation contexts inherit their parent's network. Remote traffic uses an HTTPS proxy in the window's local extension host; proxy startup failure fails the view rather than falling back locally. A host known not to support proxying leaves new tabs on the local network with a visible warning.
The network belongs to the browser context, not its owner, audience, or policy. Global storage is always local. Workspace storage uses the window's network; other networks require a storage affinity (an agent host plus folder roots) selecting a partition within the workspace's storage folder, or use per-view in-memory storage without one. Clearing workspace storage includes these partitions.
Keep every test layer lean and scenario-focused. Protect major functionality whose failure would damage a core browser scenario, and use the lowest-cost layer that still exercises the actual risk. Do not duplicate behavior across layers or encode incidental implementation details.
extensions/vscode-api-tests/src/singlefolder-tests/browser*.test.ts are the preferred integration layer for browser APIs, CDP behavior, browser tools, extension-host wiring, and cross-process contracts exposed to extensions.WebContentsView. This includes core risks in preload keyboard routing, native focus/visibility/lifecycle, Electron permissions, popup editors, workbench UI over the native view, and live page-to-chat attachments. Keep each test to the happy-path spine, group related assertions into one coherent journey, and leave variants, edge cases, and visual details to lower layers.Good assertions express the user contract and remain stable through non-behavior-breaking changes and refactoring. Tests should protect behavior whose failure would materially break a major browser scenario and live at the cheapest layer that still exercises that failure mode. Smoke coverage should extend an existing journey when it stays coherent or use at most a small number of scenarios for the major journey.
electron-* / node and let the browser/ stubs throw "not available in web".microsoft/vscode
f357833
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.