Skip to main content

dbgidchromium

Version: 1.1.9

Android-focused browser automation toolkit for DBG ID Browser (com.dbgid.browser).

Python talks to the Android browser over a local socket. Protocol values are base64-encoded (request and response), similar to Selenium-style WebDriver APIs with extra Playwright-style locators, network capture, header spoofing, and session resume helpers.

Package dbgidchromium
Version 1.1.9
Python >= 3.10
License MIT
Browser package com.dbgid.browser
Source API __init__.py
Alias Chrome = WebDriver, AsyncChrome = AsyncWebDriver
import dbgidchromium as chromium
print(chromium.__version__)  # 1.1.9

Requirements

  1. DBG ID Browser installed on the Android device/emulator.
  2. Shell access able to start activities/services (am / cmd activity), preferably via Shizuku rish on Termux.
  3. Python 3.10+ (stdlib only; no extra pip deps required for core).

Optional env:

Env Purpose
DBGIDCHROMIUM_RISH / DBGID_RISH Path to rish (default ~/bin/rish)
DBGIDCHROMIUM_ANDROID_SHELL / DBGID_ANDROID_SHELL auto (default), rish, direct
DBGIDCHROMIUM_GUI_FALLBACK / DBGID_GUI_FALLBACK Allow GUI fallback when headless launch fails

Install

# from source (this repo root)
pip install -e .

# or plain path import
export PYTHONPATH=/path/to/dbgidchromium/..
python -c "import dbgidchromium; print(dbgidchromium.__version__)"

Packaging maps the repo root as package dbgidchromium and ships ip2asn-v4-u32.tsv for IP/ASN spoof helpers.


Quick start

import dbgidchromium as chromium
from dbgidchromium import By, Keys, WebDriverWait, presence_of_element_located

driver = chromium.WebDriver(gui=False, debug=True)
try:
    driver.goto(
        "https://example.com",
        timeout=45,
        wait_until="javascript",
        clear_browser_state=True,
        log=True,
    )
    print(driver.title)
    print(driver.current_url)

    el = driver.find_element(By.CSS_SELECTOR, "h1")
    print(el.text)

    driver.locator("a").first.click()
finally:
    driver.close()

Context manager:

with chromium.WebDriver(gui=False) as driver:
    driver.goto("https://example.com", wait_until="complete")
    print(driver.page_source[:200])

Alias:

from dbgidchromium import Chrome  # same as WebDriver
driver = Chrome(gui=True)

Architecture (short)

Python (dbgidchromium)
    │  TCP socket + base64 DictMap payload
    ▼
DBG ID Browser (Android WebView / service)
    SplashActivity  → GUI mode
    MainService     → headless / gui=False
  • WebDriver subclasses RemoteConnection (a listening socket.socket).
  • Launch builds a webdriver://com.dbgid.browser/?data=<base64-json> intent.
  • Commands are string names from Command ("get", "find element", …).
  • detach() keeps the browser open; close() ends the WebDriver session.

Module constants

Name Value / meaning
__version__ "1.1.9"
ANDROID_PACKAGE "com.dbgid.browser"
ANDROID_ACTIVITY ".SplashActivity"
ANDROID_SERVICE ".MainService"
EXECUTE_SCRIPT_RESULT_ATTR DOM attr for script result side-channel
EXECUTE_SCRIPT_STATUS_ATTR DOM attr for script status
EXECUTE_SCRIPT_REQUEST_ATTR DOM attr for script request id
REQUEST_INTERCEPTOR_WINDOW_NAME_PREFIX Prefix used by request interceptor
REQUEST_INTERCEPTOR_DEFAULT_TYPES ("image", "audio", "video")
REQUEST_INTERCEPTOR_TYPE_ALIASES Maps aliases (cssstylesheet, xhrconnect, …)
IP2ASN_DB_FILE Path to bundled ip2asn-v4-u32.tsv
no_encode_again Global encode flag (default False)
Chrome Alias of WebDriver
AsyncChrome Alias of AsyncWebDriver

Exceptions

All extend WebDriverException except the base itself.

Class When
WebDriverException Generic driver/protocol error
NoSuchElementException Element not found
ApplicationClosed Browser/app closed while talking
CrossOriginFrameException Cross-origin iframe DOM access blocked
InvalidElementStateException Element state not valid for action
UnexpectedTagNameException Wrong tag (e.g. Select on non-<select>)
from dbgidchromium import NoSuchElementException, By

try:
    driver.find_element(By.CSS_SELECTOR, "#missing")
except NoSuchElementException as e:
    print("not found:", e)

By — locator strategies

class By:
    ID = "id"
    XPATH = "xpath"
    LINK_TEXT = "link text"
    PARTIAL_LINK_TEXT = "partial link text"
    NAME = "name"
    TAG_NAME = "tag name"
    CLASS_NAME = "class name"
    CSS_SELECTOR = "css selector"
driver.find_element(By.CSS_SELECTOR, "#login")
driver.find_elements(By.XPATH, "//button")
driver.find_element(By.NAME, "email")
driver.find_element(By.CLASS_NAME, "btn-primary")
driver.find_element(By.TAG_NAME, "h1")
driver.find_element(By.LINK_TEXT, "Sign in")
driver.find_element(By.PARTIAL_LINK_TEXT, "Sign")

Important: By.ID limitation

On some DBG ID Browser builds, By.ID may return empty because the browser side treats the value like a bare querySelector string instead of getElementById / #id.

Workaround — prefer CSS with #:

# may fail on affected browser builds
driver.find_element(By.ID, "text-input")

# reliable
driver.find_element(By.CSS_SELECTOR, "#text-input")
driver.query_selector("#text-input")

Keys

Android keycodes used with send_key / locator press:

class Keys:
    ENTER = 66
    TAB = 61
el.send_key(Keys.ENTER)
driver.locator("#q").press("Enter")  # locator path

Command — wire command names

Low-level protocol action strings. Usually you call WebDriver methods instead of building these by hand.

Attribute Wire value
CLOSE close
GET get
FIND_ELEMENT / FIND_ELEMENTS find element / find elements
CLICK_JAVA click java
EXECUTE_SCRIPT execute script
GET_ATTRIBUTE / SET_ATTRIBUTE / REMOVE_ATTRIBUTE attribute ops
SEND_KEY / SEND_TEXT input
PAGE_SOURCE / TITLE / CURRENT_URL page meta
GET_COOKIE(S) / SET_COOKIE / CLEAR_COOKIE(S) / DELETE_ALL_COOKIE cookies
GET/SET_LOCAL_STORAGE / GET/SET_SESSION_STORAGE / clears storage
GET/SET_USER_AGENT / GET/SET_HEADERS identity
SET_PROXY / SET_REQUEST_INTERCEPTOR network
SWIPE / SWIPE_UP / SWIPE_DOWN / SCROLL_TO gestures
WINDOW_HANDLES / CURRENT_WINDOW_HANDLE / SWITCH_TO_WINDOW tabs
SCREENSHOT screenshot
WAIT_UNTIL_ELEMENT / WAIT_UNTIL_NOT_ELEMENT waits
GET_RECAPTCHA_V3_TOKEN reCAPTCHA v3
OVERRIDE_JS_FUNCTION inject/override JS
INIT session bootstrap

DictMap / encode helpers

Protocol dict that stringifies values in the browser’s expected form.

from dbgidchromium import DictMap, b64encode, b64decode, decode_data

payload = DictMap({"command": "get", "url": "https://example.com"})
# WebDriver uses DictMap internally when sending commands

print(b64encode("hello"))
print(b64decode(b64encode("hello")))
Function Role
b64encode(value) Encode str/value → base64 text (with decode_data cleanup)
b64decode(value) Decode base64; coerces true/false/null/JSON-ish
decode_data(data) Unicode-escape / HTML-unescape cleanup

RemoteConnection

Base of WebDriver. Owns the listening socket server the Android app connects to.

# constructed via WebDriver(...); rarely used alone
# __init__(accept_time_out)
# init_socket_server()

WebDriver (core class)

WebDriver(
    gui=True,
    pip_mode=False,
    lang="en",
    debug=False,
    accept_time_out=60,
    recv_time_out=60 * 60,
    gui_fallback=None,
    resume=False,
)
Parameter Default Meaning
gui True True → Activity UI; False → headless service path
pip_mode False Picture-in-picture related launch flag
lang "en" Browser language hint
debug False Extra debug prints
accept_time_out 60 Seconds to accept Android socket connect
recv_time_out 3600 Socket recv timeout
gui_fallback env / auto If headless fails, may fall back to GUI
resume False Attach to existing live tab (see session resume)

Instance attributes (notable)

Attribute Meaning
gui / requested_gui Effective / requested GUI mode
debug Debug flag
switch_to SwitchTo helper (driver.switch_to.frame(...))
accept_time_out / recv_time_out Timeouts
_frame_stack Nested iframe context stack (internal)

Supports with (__enter__ / __exit__close()).


Lifecycle

Method Behavior
close() End WebDriver session (sends close; tears down connection)
detach() Drop Python socket without closing Android browser/tab
resume_browser_session(...) / resume_chrome() New Python driver attached to live browser tab
continue_browser_session() / continue_chrome() Continue helpers
detach_browser_session(driver) Module helper around detach
driver = chromium.WebDriver(gui=True)
driver.goto("https://example.com")
driver.detach()   # leave tab open

# later, new process/shell:
driver = chromium.resume_browser_session(gui=False, debug=True)
print(driver.current_url)
driver.close()

Static Android helpers:

WebDriver.force_stop_command(package)  # force-stop package string builder
WebDriver.home_command()               # home intent helper
WebDriver.wait(delay)                  # sleep wrapper

Module launch helpers:

from dbgidchromium import (
    start_browser_activity,
    start_browser_service,
    foreground_browser,
    screencap_png,
)

start_browser_activity(data=None, wait=True, ...)   # am start Activity
start_browser_service(data=None, ...)               # headless MainService
foreground_browser()                                # bring UI forward, keep tabs
png_bytes = screencap_png("/sdcard/shot.png")       # Android display PNG

Navigation

get(url, **kwargs)

Low-level navigate (no full wait pipeline of goto).

Optional kwargs:

  • use_cookie_from_requests — load cookies before GET
  • custom_headers / headers — apply headers before GET

goto(url, **kwargs) — preferred

High-level navigation + wait + optional spoof/headers/interceptor/clear.

Kwarg Default Meaning
wait_until "javascript" (GUI); headless defaults differ Readiness mode
timeout 30 Max wait seconds
poll_frequency ~0.06 headless / ~0.12 GUI Poll interval
settle_time ~0.15 headless / ~0.8 GUI Extra settle after ready
locator / wait_for None Also wait for element
clear_browser_state True clear_browser() before first nav
use_spoof None IP/ASN header spoof (True = random)
custom_headers / headers None Extra HTTP headers
interceptor / interceptor_mime_types / block_mime_types None Block resource types
fallback_wait_until None Softer wait if primary times out
fallback_after None When to try fallback
log True Log wait progress

wait_until modes:

Value Meaning
url_only URL changed / minimal
domcontentloaded DOMContentLoaded-ish readiness
realtime Early interactive/render signal
javascript JS-ready (default for GUI)
complete / load Full load style readiness

Headless (gui=False) without explicit wait_until defaults to url_only, or domcontentloaded if a locator is passed.

driver.goto(
    "https://www.google.com",
    wait_until="javascript",
    timeout=45,
    poll_frequency=0.10,
    settle_time=0.35,
    clear_browser_state=True,
    use_spoof=True,
    interceptor=["image", "font", "media"],
    custom_headers={"Accept-Language": "en-US"},
    log=True,
)

Related:

driver.go_back()
driver.go_forward()
driver.reload()
driver.set_content("<html><body>hi</body></html>", wait_until="javascript", timeout=30)
html = driver.content()          # alias-style page HTML helper
src = driver.page_source         # property
title = driver.title             # property
url = driver.current_url         # property

SPA wait

details = driver.wait_for_spa_ready(
    timeout=30,
    selector="#app",
    min_html_length=1200,
    poll_frequency=0.1,
    stable_time=0.35,
)
# aliases: wait_for_react_ready, wait_for_nextjs_ready (on async wrapper too)
driver.wait_for_navigation(
    locator=(By.CSS_SELECTOR, "h1"),
    timeout=30,
    poll_frequency=0.1,
    settle_time=0.75,
    wait_until="javascript",
)

Finding elements

Classic Selenium-style

el = driver.find_element(By.CSS_SELECTOR, "#email")
els = driver.find_elements(By.CSS_SELECTOR, "input")

driver.find_element_by_css_selector("#email")
driver.find_element_by_xpath("//input[@name='q']")
driver.find_element_by_name("q")
driver.find_element_by_class_name("btn")
driver.find_element_by_tag_name("button")
driver.find_element_by_link_text("Docs")
driver.find_element_by_partial_link_text("Doc")
driver.find_element_by_id("email")  # prefer CSS #email if By.ID flaky

driver.query_selector("#email")       # CSS
driver.querySelector("#email")        # camelCase alias
driver.query_selector_all(".item")
driver.querySelectorAll(".item")

Playwright-style locators

loc = driver.locator("css=button.submit")   # or plain CSS / xpath=... / text=...
loc = driver.locator("#email")
loc = driver.locator("xpath=//button")
loc = driver.locator("text=Sign in")
loc = driver.locator("//button")            # xpath if starts with // or (

loc.click()
loc.fill("user@example.com")
loc.type("slow text")
loc.press("Enter")
loc.dblclick()
loc.hover()
loc.check() / loc.uncheck() / loc.set_checked(True)
loc.is_checked() / loc.is_visible() / loc.is_enabled()
loc.text_content() / loc.inner_text() / loc.inner_html()
loc.input_value()
loc.get_attribute("href")
loc.evaluate("el => el.tagName")
loc.wait_for(state="visible", timeout=30)   # visible / attached-style states
loc.count()
loc.all()
loc.nth(0) / loc.first / loc.last
loc.filter(has_text="Save")
loc.locator(".child")                       # nested
loc.all_text_contents()
el = loc.element_handle()                   # -> WebElement

Semantic getters:

driver.get_by_text("Sign in", exact=True)
driver.get_by_role("button", name="Submit")
driver.get_by_label("Email")
driver.get_by_placeholder("Search")
driver.get_by_test_id("login-form")

ElementLocator scopes a locator under a parent WebElement.


WebElement

Returned by find_element* / locator element_handle().

Actions

el.click()
el.click_java()          # JS click path
el.clear()
el.focus()
el.send_text("hello")
el.send_key(Keys.ENTER)
el.set_attribute("data-x", "1", is_string=True)
el.remove_attribute("disabled")

Find under element

Same find_element* / query_selector* family as driver, scoped to the element.

child = el.find_element(By.CSS_SELECTOR, "span.label")
el.query_selector_all("li")

Properties / attributes

Member Type Notes
text property Visible / text content
value property + setter Form value
inner_html property + setter
outer_html property + setter
height / width property
position property Position info
disabled property + setter
is_displayed property
read_only property
get_attribute(name) method
print(el.text, el.value, el.is_displayed)
el.value = "new"
print(el.get_attribute("class"))

Resilient interaction helpers

Retry-oriented helpers (visibility/clickable waits, multiple attempts):

driver.typing_like_human("#email", "user@example.com", press_enter=False)
driver.ensure_typing("#email", "user@example.com", press_enter=True)
driver.ensure_click("button[type=submit]")
driver.ensure_option_select("select#country", "US")      # value / text / index-like
driver.ensure_checked_checkbox("input#terms")
token = driver.get_turnstile_token(
    selector="input[name='cf-turnstile-response']",
    timeout=10,
    poll_frequency=0.25,
)

ensure_* methods retry (~5 attempts) with short backoff; raise last error / TimeoutError on failure.


Waits

WebDriverWait

from dbgidchromium import (
    WebDriverWait,
    presence_of_element_located,
    visibility_of_element_located,
    invisibility_of_element_located,
    element_to_be_clickable,
    By,
)

wait = WebDriverWait(driver, timeout=10, poll_frequency=0.2)
el = wait.until(visibility_of_element_located((By.CSS_SELECTOR, "#ready")))
wait.until_not(presence_of_element_located((By.CSS_SELECTOR, ".spinner")))

Expected-condition factories take a locator tuple (By.*, value) and return predicates for until / until_not.

On timeout: TimeoutError("Time out to wait element").


Frames / iframes

frames = driver.detect_iframes(include_elements=False)
# [{id, name, src, same_origin, ...}, ...]

driver.switch_to_iframe(0)                 # index
driver.switch_to_iframe("frame-one")       # name/id
driver.switch_to_iframe(css_element)       # element
driver.switch_to_frame(...)                # alias of switch_to_iframe
driver.switch_to_parent_frame()
driver.switch_to_default_content()

# Selenium-style facade
driver.switch_to.frame(0)
driver.switch_to.iframe("frame-one")
driver.switch_to.parent_frame()
driver.switch_to.default_content()
driver.switch_to.window(handle)

FrameLocator (Playwright-style)

fl = driver.frame_locator("#frame-one")
print(fl.info())                 # tag/id/name/src/same_origin/...
print(fl.is_same_origin(), fl.is_cross_origin())

fl.locator("#inside").fill("hi")
fl.get_by_text("hello")
fl.get_by_role("button", name="Go")
nested = fl.frame_locator("#frame-two")

# cross-origin: DOM from parent is blocked; open src top-level if needed
if fl.is_cross_origin():
    fl.open()   # driver.get(iframe src)

NestedFrameLocator / FrameScopedLocator support nested frame trees. Cross-origin frames raise / mark CrossOriginFrameException paths — Chromium SOP still applies.


Tabs / pages

handles = driver.window_handles          # list; headless often single-tab
current = driver.current_window_handle
driver.switch_to_window(handles[0])

pages = driver.pages()                   # list[PageHandle]
page = driver.current_page               # property
page = driver.new_page("https://example.com")

page.bring_to_front()
print(page.url, page.title)
page.locator("h1").text_content()
page.frame_locator("iframe")
page.evaluate("() => document.title")
page.screenshot(path="tab.png")

Select<select> helper

from dbgidchromium import Select

select = Select(driver.find_element(By.CSS_SELECTOR, "select#country"))
print(select.options)
print(select.all_selected_options)
print(select.first_selected_option)

select.select_by_value("us")
select.select_by_index(2)
select.select_by_visible_text("United States")

select.deselect_all()          # multi-select
select.deselect_by_value("us")
select.deselect_by_index(2)
select.deselect_by_visible_text("United States")

Non-<select> elements → UnexpectedTagNameException.


JavaScript

title = driver.execute_script("return document.title")
# *args supported; elements can round-trip depending on marshal path
driver.execute_script("arguments[0].style.border='2px solid red'", el)

driver.override_js_function(long_bootstrap_script)

execute_script uses a marshaling wrapper + optional DOM attribute side-channel (EXECUTE_SCRIPT_*_ATTR) when direct eval is restricted (CSP). Kwargs include side_channel_polls, side_channel_sleep, prefer_dom_fallback.

Returning DOM nodes may yield WebElement instances.


Cookies, storage, headers, UA, proxy

driver.set_cookie("session", "abc", url="https://example.com")
print(driver.get_cookie("session", url="https://example.com"))
print(driver.get_cookies(url="https://example.com"))
driver.clear_cookie("session")
driver.clear_cookies()
driver.delete_all_cookie()

driver.set_local_storage("k", "v", is_string=True)
print(driver.get_local_storage())
driver.clear_local_storage()

driver.set_session_storage("k", "v")
print(driver.get_session_storage())
driver.clear_session_storage()

print(driver.user_agent)
driver.user_agent = "MyAgent/1.0"

print(driver.headers)                 # property
driver.headers = {"X-Debug": "1"}     # setter

driver.set_proxy("127.0.0.1", 8080)

driver.clear_browser(clear_cache=True)

Load cookies from a requests-like cookie jar / list:

from dbgidchromium import load_cookies_from_requests

load_cookies_from_requests(driver, cookies, url="https://example.com")
# or
driver.load_cookies_from_requests(cookies, url="https://example.com")

Module helpers: apply_custom_headers(driver, headers, merge=True), normalize_custom_headers(headers), clear_browser(driver, clear_cache=True), goto(driver, url, **kwargs).


IP / ASN spoof headers

Uses bundled ip2asn-v4-u32.tsv.

from dbgidchromium import generate_ip, use_spoof, AsnHit

info = generate_ip()
# {"ip": "...", "country": "US", "name": "...", "asn": 1234}

headers = driver.use_spoof(True)          # random public IP + related headers
headers = driver.use_spoof(info)          # explicit
headers = use_spoof(driver, spoof=info, merge=True)

# or via goto
driver.goto("https://httpbin.org/headers", use_spoof=True)

Related: load_ip2asn_u32_tsv, lookup_asn, random_public_ip_from_db, normalize_spoof, build_spoof_headers.

AsnHit dataclass fields: ip, asn, country, name.

Typical spoof headers include client IP / ASN-style forwarding headers derived from the hit.


Request interceptor (block resource types)

Pass to goto(..., interceptor=...) or set via protocol command.

Accepted types (after alias normalize): e.g. image, audio, video, media, font, stylesheet, script, document, connect, …

Aliases examples: imgimage, cssstylesheet, xhr/fetch/ajaxconnect, jsscript.

driver.goto(
    "https://example.com",
    interceptor=["image", "font", "media"],  # block these
)

Default type set constant: REQUEST_INTERCEPTOR_DEFAULT_TYPES = ("image", "audio", "video").


Network capture (fetch / XHR)

Injects JS hooks; stores records in-page.

driver.network_capture_start(clear=True, max_body_chars=200000)
driver.goto("https://example.com/app", wait_until="javascript")
# ... interact ...
records = driver.network_capture_get(include_document=False)
driver.network_capture_save("capture.json", include_document=False)
driver.network_capture_stop()

# aliases: start_network_capture / stop_network_capture

build_network_capture_script(clear=True, enabled=True, max_body_chars=200000) returns the raw JS if you need to inject manually.


Screenshots & gestures

png = driver.get_screenshot_as_png(use_rish=None, timeout=20)
driver.save_screenshot("/sdcard/page.png")
driver.get_screenshot_as_file("/sdcard/page.png")
driver.screenshot(path="/sdcard/page.png")       # or as_bytes=True

driver.scroll_to(0, 500)
driver.swipe(100, 800, 100, 200, speed=1)
driver.swipe_up()
driver.swipe_down()
driver.click_java(x=120, y=400)   # coordinate click via Java side

screencap_png(path=None) captures the full Android display (not only WebView) via screencap, with rish/file/base64 fallbacks.


reCAPTCHA / Turnstile helpers

token = driver.get_recaptcha_v3_token(action="login")
cf = driver.get_turnstile_token(
    selector="input[name='cf-turnstile-response']",
    timeout=10,
)

Async API

Thin asyncio wrappers around sync driver (run blocking calls in a worker).

import asyncio
from dbgidchromium import AsyncWebDriver, AsyncChrome

async def main():
    async with AsyncWebDriver(gui=False, debug=True) as driver:
        await driver.goto("https://example.com", wait_until="javascript")
        loc = driver.locator("h1")
        print(await loc.text_content())
        await driver.screenshot(path="/sdcard/async.png")

asyncio.run(main())

Classes:

Class Wraps
AsyncWebDriver WebDriver
AsyncWebElement WebElement
AsyncLocator Locator
AsyncFrameLocator FrameLocator
AsyncPageHandle PageHandle

AsyncWebDriver supports __aenter__ / __aexit__, goto, execute_script, locator helpers, network capture, screenshots, etc. Unknown attrs proxy to the underlying sync driver as async callables when callable.


Module-level function index

Function Summary
screencap_png(path=None, use_rish=None, timeout=20) Full-display PNG bytes
start_browser_activity(...) Start SplashActivity / intent
start_browser_service(...) Start MainService headless
foreground_browser(...) Foreground UI without wiping tabs
decode_data / b64encode / b64decode Protocol codecs
load_cookies_from_requests(driver, cookies, url="") Import cookies
clear_browser(driver, clear_cache=True) Clear via driver
generate_ip / lookup_asn / load_ip2asn_u32_tsv / … ASN DB
normalize_spoof / build_spoof_headers / use_spoof Spoof headers
normalize_custom_headers / apply_custom_headers Header merge/set
build_network_capture_script(...) JS capture bootstrap
goto(driver, url, **kwargs) Functional goto
find_element(driver, locator, command) Wait-condition helper
presence_of_element_located / visibility_of_element_located / invisibility_of_element_located / element_to_be_clickable Expected conditions
resume_browser_session / resume_chrome / continue_browser_session / continue_chrome / detach_browser_session Session continuity

End-to-end examples

1) Headless smoke + CSS locators

import dbgidchromium as chromium
from dbgidchromium import By

with chromium.WebDriver(gui=False, debug=True) as driver:
    driver.goto("https://example.com", wait_until="javascript", timeout=30)
    h1 = driver.find_element(By.CSS_SELECTOR, "h1")
    print(h1.text)
    driver.locator("a").first.click()
    print(driver.current_url)

2) Human-like login form

driver = chromium.WebDriver(gui=True, debug=True)
driver.goto("https://example.com/login", wait_until="javascript")
driver.ensure_typing("#email", "user@example.com")
driver.ensure_typing("#password", "secret", press_enter=False)
driver.ensure_checked_checkbox("#remember")
driver.ensure_click("button[type=submit]")
driver.wait_for_spa_ready(selector="#dashboard", timeout=20)
print(driver.title)
driver.close()

3) Iframe walk

driver.goto(local_fixture_url, wait_until="javascript")
frames = driver.detect_iframes()
driver.switch_to_iframe(0)
print(driver.find_element(By.CSS_SELECTOR, "#inside").text)
driver.switch_to_default_content()

# or
driver.frame_locator("#frame-one").locator("#inside").text_content()

4) Network capture + header spoof

with chromium.WebDriver(gui=False) as driver:
    driver.network_capture_start()
    driver.goto(
        "https://httpbin.org/headers",
        use_spoof=True,
        interceptor=["image", "font"],
        wait_until="javascript",
    )
    print(driver.locator("body").inner_text()[:500])
    driver.network_capture_save("out.json")

5) Wait for element

from dbgidchromium import WebDriverWait, visibility_of_element_located, By

driver.goto("https://example.com")
el = WebDriverWait(driver, 15).until(
    visibility_of_element_located((By.CSS_SELECTOR, "h1"))
)
print(el.text)

6) Async

import asyncio
from dbgidchromium import AsyncChrome

async def run():
    async with AsyncChrome(gui=False) as page:
        await page.goto("https://example.com", wait_until="complete")
        print(await page.locator("h1").text_content())

asyncio.run(run())

7) Run bundled example

python example.py
# prints: dbgidchromium version: 1.1.9
# runs local iframe DOM smoke + google wait_until modes

Design notes / limitations

  1. Android-only transport — needs DBG ID Browser + shell start permissions.
  2. By.ID may be unreliable — use By.CSS_SELECTOR with #id (browser-side issue on some builds).
  3. Headless vs GUIgui=False uses service path; default wait_until is more aggressive/minimal; gui_fallback may open UI if service handshake fails.
  4. Cross-origin iframes — detectable; DOM access from parent is not possible; use FrameLocator.open() to navigate top-level to src.
  5. Single-tab headlesswindow_handles often length 1 in service mode.
  6. Base64 protocol — all command values encoded; raw socket debugging will look opaque.
  7. Stdlib-first — core automation does not require Selenium/Playwright packages.

Versioning

Current package version is defined in __init__.py:

__version__ = "1.1.9"

setup.py reads the same field for distribution builds. See CHANGELOG.md for history (1.1.8 documented resilient helpers, release script, README refresh; this tree is 1.1.9).

python -c "import dbgidchromium as c; print(c.__version__)"

Links


API map (classes)

dbgidchromium
├── __version__ = "1.1.9"
├── By, Keys, Command, DictMap, AsnHit
├── Exceptions: WebDriverException, NoSuchElementException, ApplicationClosed,
│               CrossOriginFrameException, InvalidElementStateException,
│               UnexpectedTagNameException
├── RemoteConnection
│   └── WebDriver (= Chrome)
│       ├── WebElement
│       ├── Select
│       ├── WebDriverWait + expected conditions
│       ├── SwitchTo
│       ├── Locator / ElementLocator
│       ├── FrameLocator / NestedFrameLocator / FrameScopedLocator
│       └── PageHandle
└── AsyncWebDriver (= AsyncChrome)
    ├── AsyncWebElement
    ├── AsyncLocator
    ├── AsyncFrameLocator
    └── AsyncPageHandle

For behavioral smoke tests mirroring navigation + iframe DOM, see example.py.

Release files for dbgidchromium 1.1.10

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for dbgidchromium 1.1.10
File Size Uploaded
dbgidchromium-1.1.10.tar.gz 7.5 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for dbgidchromium 1.1.10
File Interpreter ABI Platform
dbgidchromium-1.1.10-py3-none-any.whl Python 3 none any Details

Total release size: 14.9 MB

Release files / dbgidchromium-1.1.10.tar.gz

Download URL dbgidchromium-1.1.10.tar.gz
Size 7.5 MB
Tags Source
SHA-256 checksum
How to use checksums
210dc5a795191fc000ab74f2548c76807b7aa094b39486db0a428d88cd5baf9e
BLAKE2b-256 checksum
How to use checksums
d74b1df55569654f6b2bae06f2f8d95d5e9cbe1224590d44ad454138c72f39e6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.13

Release files / dbgidchromium-1.1.10-py3-none-any.whl

Download URL dbgidchromium-1.1.10-py3-none-any.whl
Size 7.4 MB
Tags Python 3
SHA-256 checksum
How to use checksums
2827461be8629e3e965abe0ed81ce82fe88a2a5f0ee8f6c9d91453f324889241
BLAKE2b-256 checksum
How to use checksums
2e7c92cbfb952053fd847dec9e70d69b110f6bdb8fcdfecf0672588f60baa786
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.13

Release history Release notifications | RSS feed

This release

1.1.10 This release

2 release files

1.1.9

2 release files

1.1.8

2 release files

1.1.7

2 release files

1.1.6

2 release files

1.1.5

2 release files

1.1.4

2 release files

1.1.3

2 release files

1.1.2

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page