diff --git a/README.md b/README.md index 5af6667..64d675c 100644 --- a/README.md +++ b/README.md @@ -800,7 +800,7 @@ Windows are not measured. `python scripts/verify_npx_e2e.py` reproduces it. See | `LYRA_BROWSER_CHANNEL` | — | Force one channel (`chrome`/`msedge`). Unset = try chrome→msedge→bundled. | | `LYRA_BROWSER_ALLOW_BUNDLED` | `true` | Allow bundled-Chromium fallback (only exists after `playwright install`). | | `LYRA_BROWSER_AUDIT_STDERR` | `false` | Also write every audit record to stderr as one JSON line. For a host whose disk is ephemeral but whose stderr is collected (the Cloudflare image sets it). | -| `LYRA_BROWSER_DRIVER` | `auto` | Which driver launches the browser: `auto` takes `patchright` when installed and falls back to `playwright`; either name pins it. `native` (experimental, opt-in) drives an installed Chrome over raw DevTools with no Playwright at all, always with the `cdp` guard, the sandbox on and no `Runtime.enable`. Selectors cover css (open shadow roots pierced), `text=`, `role=…[name=…]`, `xpath=`, `nth=`, `:visible` and `:has-text()`; the tree view (`read_page(mode="tree")` and its refs) is not in it yet. `open_browser` reports the one in use as `driver`. | +| `LYRA_BROWSER_DRIVER` | `auto` | Which driver launches the browser: `auto` takes `patchright` when installed and falls back to `playwright`; either name pins it. `native` (experimental, opt-in) drives an installed Chrome over raw DevTools with no Playwright at all, always with the `cdp` guard, the sandbox on and no `Runtime.enable`. Selectors cover css (open shadow roots pierced), `text=`, `role=…[name=…]`, `xpath=`, `nth=`, `:visible` and `:has-text()`; `read_page(mode="tree")` comes from Chrome's accessibility tree in Playwright's format, and its refs reach into same-site and cross-site iframes. `open_browser` reports the one in use as `driver`. | | `LYRA_BROWSER_SANDBOX` | on | `native` driver only: Chrome's renderer sandbox. Playwright always launches with `--no-sandbox`; the native driver keeps the sandbox unless this is off (a container without user namespaces). | | `LYRA_BROWSER_GUARD` | `route` | Where the navigation guard stands: `route` (Playwright's route) or `cdp` (a second DevTools connection: every redirect hop judged, cache on, DataDome lets the browser in, a loopback debugging port, `guard_lost` if the guard fails). See Guard backends. | | `LYRA_UAT_CLAUDE_BIN` | `claude` on `PATH` | The Claude Code executable the `claude-code` UAT brain runs. See UAT mode. | diff --git a/src/lyra_browser/native/frames.py b/src/lyra_browser/native/frames.py new file mode 100644 index 0000000..ee8d433 --- /dev/null +++ b/src/lyra_browser/native/frames.py @@ -0,0 +1,179 @@ +"""Which DevTools session each frame of a page lives in, out-of-process iframes included. + +A same-site iframe renders in the page's own process and is reached through the page's +session with its ``frameId``. A cross-site one (an OOPIF) runs in another process: Chrome +gives it a target of its own, and every command about its document has to go to that +target's session. ``FrameTracker`` auto-attaches to those (without +``waitForDebuggerOnStart``, as the context does for tabs: ``cdp_guard`` holds them) and +keeps the map, so a locator or a snapshot asks one question, ``frame(frame_id)``, and gets +the session to use. + +Coordinates differ the same way. Inside one session, ``DOM.getContentQuads`` answers in +the viewport of that session's top frame; an OOPIF's answers are in its own viewport. +``viewport_offset`` adds up the owner ``