Chapter 03Lesson 01~125 minutes

WebDriver Architecture, W3C Protocol, Sessions, and Capabilities: Core Concepts and Mental Model

Open the one-line WebDriver constructor and see the system it hides: a language binding acts as the local end, a driver or Grid endpoint acts as the remote end, a new-session negotiation establishes identity and capabilities, and every later command is scoped to that session.

W3C WebDriverSessionCapabilitiesRemote endElement reference

Learning objectives

  • Explain the W3C local-end, remote-end, endpoint-node, and intermediary-node model in beginner terms.
  • Describe the new-session handshake and the difference between requested and returned capabilities.
  • Explain alwaysMatch, firstMatch, standard capability names, and vendor-prefixed extension capabilities.
  • Treat the session ID as a protocol identity rather than a decorative log value.
  • Explain how DOM elements cross the protocol boundary as session-scoped element references.
  • Distinguish classic WebDriver request/response commands from WebDriver BiDi event streams without conflating them.

1. Why protocol literacy matters

In Chapter 02, webdriver.Chrome() looked like a local Python object constructor. Operationally, it crosses process boundaries. The Selenium binding creates a WebDriver new-session request. A browser driver—or Grid Router acting as an intermediary—receives that request, negotiates a browser session, and returns a session ID plus effective capabilities. Every navigation, element lookup, click, screenshot, and quit is then a command associated with that session.

When engineers skip this model, remote execution feels “different” even though the same WebDriver semantics are being transported to a different remote end. Grid scheduling, browser-cloud execution, and CI incidents become easier to reason about once you can point to the boundary where a request is created, routed, matched, executed, and returned.

2. Local end, remote end, and transport

Classic WebDriver request/response path

The following diagram visualizes the relationships described in Local end, remote end, and transport. Read the nodes in sequence and use the arrows to connect the conceptual state changes to the explanation around the diagram.

flowchart TD
  T["Test intent"] --> B["Selenium Python binding / local end"]
  B -->|"HTTP WebDriver command"| R["Driver or Grid Router / remote end"]
  R --> D["Browser automation endpoint"]
  D --> A["Application under test"]
  A --> D
  D --> R
  R -->|"WebDriver response"| B
  B --> E["Assertion + evidence"]

The local end is the client-side implementation that turns binding calls into protocol commands. The remote end receives WebDriver commands. A direct browser driver can be an endpoint node; Grid can act as an intermediary that routes the new-session request to a suitable endpoint. The browser itself is not your Python process, and Grid is not your test runner.

3. New Session is a negotiation, not a launch command with decorations

The W3C New Session command is POST /session. Its capability request has two important buckets. alwaysMatch contains requirements that apply to every candidate. firstMatch is an ordered list of alternative candidate sets. The remote end validates and merges them, then tries to find a matching session configuration.

{
  "capabilities": {
    "alwaysMatch": {
      "browserName": "chrome",
      "pageLoadStrategy": "normal"
    },
    "firstMatch": [
      {}
    ]
  }
}

This JSON illustrates the wire-level model; normal Selenium code should use browser Options classes instead of hand-building the request. Selenium wraps the requested capabilities into the protocol shape and parses the response back into driver.capabilities.

4. Requested capabilities versus effective capabilities

A capability is a named property used during session negotiation or returned to describe the established session. Standard examples include browserName, browserVersion, platformName, pageLoadStrategy, proxy, timeouts, and unhandledPromptBehavior. The request expresses intent; the response is evidence of what the remote end actually established.

Browser-specific capabilities live in a vendor namespace. The WebDriver specification requires extension-capability keys to contain a colon, such as goog:chromeOptions or moz:firefoxOptions. A made-up unprefixed key is not portable browser configuration; at an endpoint node it is an invalid argument.

Question Request side Response side
Which browser do I need? browserName actual browserName
Which version actually ran? optional version constraint browserVersion
Which operating system executed? optional platformName requirement effective platformName
Which Chrome arguments were requested? goog:chromeOptions browser/driver-specific returned fields as supported

5. Session ID is the unit of browser-control ownership

The specification defines a session ID as a UUID-like string that uniquely identifies an active WebDriver session. Commands such as navigation or element lookup are routed to a session-specific URI. When quit() deletes the session, that identity is no longer active. A later command with the old ID should fail rather than silently attach to some other browser.

session A: 9f1...  -> browser process/context A -> active
DELETE /session/9f1...
session A: 9f1...  -> no longer active
GET /session/9f1.../title -> invalid session id

This is why session IDs belong in first-failure evidence: they let you correlate client logs, Grid events, browser evidence, and teardown to the same execution.

6. A WebElement is a protocol handle, not a copied DOM node

When a remote end finds an element, it does not serialize the entire DOM node into Python. The WebDriver protocol returns an element-reference object. The W3C identifier key is element-6066-11e4-a52e-4f735466cecf; its value is a remote handle known within the session and current browsing-context state. Selenium wraps that handle in a binding-level WebElement.

{
  "value": {
    "element-6066-11e4-a52e-4f735466cecf": "remote-node-id"
  }
}

This becomes important later when a DOM rerender invalidates a handle and Selenium reports a stale element reference. Chapter 04 focuses on finding; Chapter 05 on state/interactions; Chapter 28 returns to stale-handle diagnosis.

7. Classic WebDriver versus WebDriver BiDi

Classic WebDriver is primarily command/response: the local end asks for an action or value, and the remote end answers. WebDriver BiDi adds a bidirectional connection where the browser can emit subscribed events such as log or network activity without the client polling each one. BiDi does not erase the WebDriver session model, and CDP is not a cross-browser synonym for BiDi.

Chapter 03 needs only this boundary: do not interpret an HTTP WebDriver response, a BiDi event, and a browser-vendor CDP message as the same transport. Chapter 18 teaches the version-sensitive BiDi APIs.

8. Read-only evidence before mutation

Use the returned session state before adding complex behavior:

from selenium import __version__ as selenium_version
from selenium import webdriver

driver = None
try:
    options = webdriver.ChromeOptions()
    print("requested=", options.to_capabilities())
    driver = webdriver.Chrome(options=options)
    print("selenium=", selenium_version)
    print("session_id=", driver.session_id)
    print("returned=", driver.capabilities)
    print("url=", driver.current_url)
    print("title=", driver.title)
finally:
    if driver is not None:
        driver.quit()

The requested dictionary and returned capabilities are not expected to be identical. The response may include defaults and implementation-specific details. Treat the response as negotiated evidence, not as a mirror of your source code.

9. Why this matters in DevOps

A CI failure that says “session not created” is an environment/negotiation incident, not a locator incident. A Grid request waiting for a matching slot is a capability/capacity issue, not an AUT readiness issue. A command sent after teardown is a lifecycle defect, not a browser rendering defect. Protocol vocabulary lets teams classify incidents before changing the wrong layer.

Knowledge check

What does a returned session ID prove?

Why are requested and returned capabilities different concepts?

What is the purpose of firstMatch?

Why must vendor extension capabilities contain a colon?

Is a Python WebElement the DOM node itself?

How does classic WebDriver differ from BiDi at a high level?

Next lesson

Observe the protocol through local and remote sessions

Lesson 2 uses the same loopback fixture through a direct local driver and a local Grid Standalone endpoint, then compares capabilities, session identity, element handles, and teardown behavior.

Official references and version notes

Version and compatibility note

Version-sensitive behavior was rechecked against current Selenium and W3C primary documentation on 2026-08-27. The mandatory path pins Selenium Python 4.47.0, uses Python 3.10+, one installed supported Chromium-family browser, Java 11+ for the local Selenium Server/Grid Standalone exercise, Selenium Server 4.47.0, and loopback-only endpoints at 127.0.0.1. The Grid lab starts Standalone with --selenium-manager true and --reject-unsupported-caps true so driver discovery can remain automatic and an intentionally unavailable capability is rejected deterministically. WebDriver BiDi is explained only as a distinct bidirectional protocol boundary here; Chapter 18 teaches its evolving APIs in depth.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.