# How ego-browser Resolves Elements Inside IFrames: CDP Session Isolation Explained

> Discover how ego-browser's CDP session isolation resolves iframe elements. Learn about its frame-aware resolver for seamless nested element interaction.

- Repository: [CitroLabs/ego-lite](https://github.com/citrolabs/ego-lite)
- Tags: deep-dive
- Published: 2026-08-04

---

**ego-browser isolates each iframe in its own Chrome DevTools Protocol (CDP) session and uses a frame-aware resolver to route commands to the correct browsing context, enabling seamless interaction with nested elements.**

Working with nested iframes in browser automation requires careful context management to prevent DOM queries from leaking into the wrong document. The `citrolabs/ego-lite` repository solves this through **ego-browser**, which implements isolated CDP sessions for each iframe. This architecture ensures that element resolution commands—whether triggered by CSS selectors, role-based locators, or `@ref` identifiers—always target the correct DOM context without manual session juggling.

## The Architecture of IFrame Element Resolution

### Per-IFrame Session Isolation

Unlike single-context automation tools, ego-browser creates a dedicated CDP session for every iframe. When the resolver encounters a selector or `@ref` belonging to an iframe, it routes all subsequent CDP commands—such as `DOM.getBoxModel`, `DOM.resolveNode`, or `Accessibility.getFullAXTree`—through the iframe's specific session rather than the top-level page. This isolation prevents selector collisions and ensures accurate coordinate calculations for mouse events inside nested frames.

### Frame-Aware Reference Storage

The system maintains a **`refMap`** that associates each captured element reference (`@ref`) with its containing **`frameId`**. This mapping enables the resolver to immediately identify which browsing context owns the element before executing any DOM queries, as implemented in the resolution pipeline.

## The Element Resolution Pipeline

When `browser.click`, `browser.hover`, or low-level resolution functions receive an iframe element, they execute this workflow:

1. **Identify the iframe context** – The resolver queries `refMap.get(refId)` to retrieve the stored `frameId` associated with the element reference.
2. **Resolve the effective session** – The `resolveFrameSession(frameId, sessionId, iframeSessions)` function looks up the iframe's CDP session in the `iframeSessions` Map, falling back to the parent page's session if no specific mapping exists.
3. **Execute CDP commands in context** – All subsequent calls use the *effective* session ID returned by step 2, ensuring commands evaluate against the iframe's DOM rather than the top-level document.
4. **Handle stale references** – If a cached `backendNodeId` becomes invalid, the system falls back to role-based lookup via `findBackendNodeIdByRoleName` or `findBackendNodeIdsByRoleName`, passing the `frameId` and `iframeSessions` to maintain context during the accessibility tree query.

## Core Resolution Functions in element-resolver.ts

The implementation resides primarily in [`src/element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/element-resolver.ts).

**`resolveFrameSession`** (lines 240-247)
This helper selects the appropriate CDP session from the `iframeSessions` Map. It accepts a `frameId`, the parent `sessionId`, and the session mapping, returning the effective session ID for command routing.

**`resolveElementCenter`** (lines 70-85, 99-119)
Before calling `DOM.getBoxModel` to calculate click coordinates, this function extracts the correct iframe session via `resolveFrameSession`. It returns the center coordinates (`x`, `y`) along with the `sessionId` used for the operation.

**`resolveElementObjectId`** (lines 56-71, 78-95)
Similarly resolves the proper session before invoking `DOM.resolveNode` to obtain a JavaScript object reference for the element, ensuring the node resolution occurs within the correct browsing context.

**`resolveAxSession`** (lines 491-503)
Dedicated to Accessibility tree queries, this function ensures that `Accessibility.getFullAXTree` and related commands execute within the iframe's session when resolving elements by role or accessibility properties.

## Locating and Managing IFrame Sessions

To facilitate manual session management, ego-browser exposes `browser.iframeTarget(frameSelector)` in [`src/driver/nav.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/driver/nav.ts) (lines 256-267) and [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts) (lines 781-782). This utility returns the target ID of an iframe whose URL matches a provided substring, allowing callers to create or reuse sessions for specific nested contexts.

## Code Examples

### High-Level API Usage

```typescript
// Click a button inside an iframe whose URL contains "login"
const iframeId = await browser.iframeTarget('login'); // Returns session ID for the iframe
const sessions = new Map();                          // Map frameId → sessionId
sessions.set(iframeId, iframeId);                   // Store the iframe session

// Use a ref captured inside the iframe
await browser.click('@42', { iframeSessions: sessions });

// Or use a locator directly; resolver switches sessions automatically
await browser.click('css:button.submit', { iframeSessions: sessions });

```

### Low-Level Resolver Access

```typescript
import { resolveElementCenter } from './element-resolver.js';

const { x, y, sessionId } = await resolveElementCenter(
  cdp,                     // CDP client instance
  mainSessionId,           // Top-level page session
  refMap,                  // Map of @refs → element data (includes frameId)
  '@15',                   // Reference ID inside an iframe
  iframeSessions           // Map<frameId, iframeSessionId>
);

await cdp.sendRaw('Input.dispatchMouseEvent', {
  type: 'mousePressed',
  x, y,
  button: 'left',
}, sessionId);

```

## Summary

- **ego-browser** isolates each iframe in a dedicated CDP session to prevent context leakage between nested browsing contexts.
- The **`resolveFrameSession`** function in [`src/element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/element-resolver.ts) maps `frameId` values to their corresponding session IDs, ensuring commands route correctly.
- Element references stored in **`refMap`** include `frameId` metadata, enabling automatic context detection during resolution.
- Fallback mechanisms like **`findBackendNodeIdByRoleName`** respect iframe boundaries by accepting `frameId` and `iframeSessions` parameters.
- The **`browser.iframeTarget`** utility provides explicit session acquisition for iframes matching URL patterns.

## Frequently Asked Questions

### How does ego-browser track which iframe contains an element?

Each captured element reference in the `refMap` stores a `frameId` field indicating its containing iframe. When resolving an element by `@ref`, the system retrieves this metadata via `refMap.get(refId)` to determine the correct CDP session.

### What happens if a backendNodeId becomes stale inside an iframe?

If the cached `backendNodeId` is invalid, the resolver falls back to role-based lookup using `findBackendNodeIdByRoleName` or `findBackendNodeIdsByRoleName`. These functions accept `frameId` and `iframeSessions` parameters to ensure the accessibility tree query runs within the correct iframe session.

### Can I manually specify an iframe session for automation commands?

Yes. Use `browser.iframeTarget(frameSelector)` (implemented in [`src/driver/nav.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/driver/nav.ts)) to obtain a session ID for an iframe matching a URL substring. Pass this via the `iframeSessions` Map option to `browser.click`, `browser.hover`, or other helpers to override automatic detection.

### Where is the iframe session resolution logic implemented?

The core logic resides in [`src/element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/element-resolver.ts), specifically within `resolveFrameSession` (lines 240-247) for session selection, and `resolveElementCenter` (lines 70-119) and `resolveElementObjectId` (lines 56-95) for element-specific resolution. Accessibility-specific session handling is implemented in `resolveAxSession` (lines 491-503).