How the Playwright Browser Probe Works Across Chrome, Edge, and Chromium Without Node.js

The patent-disclosure-skill repository provides a pure-Python Playwright probe that auto-detects and launches system Chrome, system Edge, or bundled Chromium using only playwright.sync_api—no Node.js runtime required.

This article examines the implementation in handsomestWei/patent-disclosure-skill, specifically how tools/shared/browser.py implements a self-contained browser discovery mechanism. The probe eliminates the traditional Node.js dependency by leveraging Playwright's Python bindings and embedded driver binaries, making it ideal for lightweight Python environments.

Environment-Driven Configuration

The probe respects two environment variables that control its behavior without code changes.

PLAYWRIGHT_HEADED — Display Mode Control

The headed() function parses this flag to determine whether browsers launch in headed or headless mode.

import os

def headed() -> bool:
    return os.environ.get("PLAYWRIGHT_HEADED", "").lower() in ("1", "true", "yes", "on")

Source: tools/shared/browser.py, lines 35-39

PATENT_BROWSER_CHANNEL — Force a Specific Browser

The _forced_channel() function reads this optional override, allowing users to bypass auto-detection entirely.

def _forced_channel() -> Optional[str]:
    ch = os.environ.get("PATENT_BROWSER_CHANNEL", "").strip().lower()
    if ch in ("chrome", "msedge", "chromium"):
        return ch
    return None

Source: tools/shared/browser.py, lines 61-64

Auto-Detection Priority Order

When no channel is forced, the probe follows a fixed fallback sequence defined in _AUTO_CHANNELS:

_AUTO_CHANNELS: Tuple[str, ...] = ("chrome", "msedge")  # None = bundled Chromium

This tuple drives the loop in launch_chromium(), which attempts each candidate in order:

  1. "chrome" — System-installed Google Chrome
  2. "msedge" — System-installed Microsoft Edge
  3. None — Playwright's bundled Chromium (downloaded via playwright install chromium)

Source: tools/shared/browser.py, lines 31-34

Installation Verification Before Launch

The probe validates that Playwright's Python package is importable before attempting any browser launches. The playwright_installed() function guards against missing dependencies with a clean boolean check.

def playwright_installed() -> bool:
    try:
        import playwright
        return True
    except ImportError:
        return False

Source: tools/shared/browser.py, lines 43-49

Launch Mechanics: How Node.js Is Bypassed

The core launch logic resides in launch_chromium(). For each candidate channel, the function constructs arguments from LAUNCH_ARGS and calls playwright.chromium.launch() with the channel parameter when applicable.

Launch Arguments Configuration

LAUNCH_ARGS = [
    "--disable-blink-features=AutomationControlled",
    "--no-sandbox",           # Required for Docker/root environments

    "--disable-setuid-sandbox",
]

These flags disable automation detection (reducing bot-blocking) and relax sandboxing for portability.

The Launch Loop

def launch_chromium(channel: Optional[str] = None):
    from playwright.sync_api import sync_playwright
    
    with sync_playwright() as p:
        browser = p.chromium.launch(
            headless=not headed(),
            args=LAUNCH_ARGS,
            channel=channel,  # None → bundled Chromium; "chrome"/"msedge" → system install

        )
        # ... success handling

The channel parameter is the critical mechanism: when set to "chrome" or "msedge", Playwright locates and uses the system browser executable; when None, it falls back to its own Chromium binary.

Source: tools/shared/browser.py, lines 94-106

Success and Failure Handling

Success Path

On first successful launch, the browser closes immediately and returns:

browser.close()
return browser, label  # label ∈ {"chrome", "msedge", "chromium"}

Source: tools/shared/browser.py, lines 99-104

Failure Path with Installation Hints

If all candidates fail, install_chromium_hint() generates platform-specific guidance:

def install_chromium_hint() -> str:
    return "Playwright Chromium not found. Run: playwright install chromium"

The probe raises RuntimeError with this hint, directing users to resolve the issue without manual troubleshooting.

Source: tools/shared/browser.py, lines 108-110 and install_chromium_hint() implementation

JSON Report Structure

The probe_launch() wrapper returns a machine-parseable dictionary suitable for CI/CD pipelines and health checks:

Key Type Description
playwright bool Whether the Python package is importable
channel str | None Which browser channel was detected/specified
ok bool Whether launch succeeded
error str | None Exception message if failed
hint str | None Installation or remediation guidance

Source: tools/shared/browser.py, lines 124-140

Command-Line and Programmatic Usage

CLI Probe Mode


# Quick system check — exits 0 on success

python -m tools.shared.browser --probe

stderr output (human-readable):


PROBE: ok=true channel=chrome error=

stdout output (JSON):

{
  "playwright": true,
  "channel": "chrome",
  "ok": true,
  "error": null,
  "hint": null
}

Source: tools/shared/browser.py, lines 168-176 (main() function)

Programmatic Integration

from tools.shared.browser import probe_launch

result = probe_launch()
if result["ok"]:
    print(f"Ready: {result['channel']}")
else:
    print(f"Failed: {result['error']}")
    print(f"Fix: {result['hint']}")

Force Specific Channel

export PATENT_BROWSER_CHANNEL=msedge
python -m tools.shared.browser --probe

Valid values: chrome, msedge, chromium

Why No Node.js Is Required

Traditional Playwright setups require Node.js for the driver process. This probe avoids that dependency through three design choices:

  1. Python-native bindings — Uses playwright.sync_api exclusively
  2. Embedded driver binaries — The playwright PyPI package includes pre-compiled browser drivers
  3. Direct executable discovery — System Chrome/Edge paths are resolved via Playwright's built-in locators, not Node.js path resolution

The bundled Chromium fallback works because playwright install chromium downloads a standalone browser build that communicates via the Python binding's WebSocket protocol—no separate Node.js runtime orchestrates the process.

Summary

  • The probe in tools/shared/browser.py implements pure-Python browser discovery using Playwright's sync API
  • Auto-detection order: system Chrome → system Edge → bundled Chromium, controlled by _AUTO_CHANNELS
  • Environment variables PLAYWRIGHT_HEADED and PATENT_BROWSER_CHANNEL configure behavior without code changes
  • Launch arguments in LAUNCH_ARGS disable automation flags and sandboxing for reliability
  • Node.js elimination relies on Python bindings plus embedded driver binaries, not external runtime
  • JSON output from probe_launch() enables programmatic health checks and CI integration

Frequently Asked Questions

Does this probe work in Docker containers without a display?

Yes. By default, headed() returns False unless PLAYWRIGHT_HEADED is explicitly set, so browsers launch in headless mode. The --no-sandbox flag in LAUNCH_ARGS handles root-privileged containers where standard Chromium sandboxing fails.

What happens if I have both Chrome and Edge installed?

The probe selects Chrome first based on _AUTO_CHANNELS = ("chrome", "msedge"). To override, set PATENT_BROWSER_CHANNEL=msedge before running.

How do I install the bundled Chromium if no system browser exists?

Run playwright install chromium in your environment. The probe's install_chromium_hint() generates this exact command in its error output when detection fails.

Can I extend this to support Firefox or WebKit?

The current implementation is Chromium-specific. Playwright's Python bindings do support Firefox and WebKit via playwright.firefox and playwright.webkit, but browser.py would require modifications to _AUTO_CHANNELS and launch_chromium() to iterate over additional browser types.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →