Skip to content

Conversation

@navin772
Copy link
Member

@navin772 navin772 commented Nov 14, 2025

User description

🔗 Related Issues

💥 What does this PR do?

Adds support for the set_user_agent_override command in the emulation module - https://w3c.github.io/webdriver-bidi/#command-emulation-setUserAgentOverride

🔧 Implementation Notes

Usage:

  1. Overriding a context
    driver.emulation.set_user_agent_override(user_agent="Mozilla/5.0 (Custom Test Agent)", contexts=[driver.current_window_handle])
  2. Overriding a user context
    driver.emulation.set_user_agent_override(user_agent="Mozilla/5.0 (Custom User Context Agent)", user_contexts=[user_context])

💡 Additional Considerations

🔄 Types of changes

  • New feature (non-breaking change which adds functionality and tests!)

PR Type

Enhancement


Description

  • Adds set_user_agent_override command to emulation module

  • Supports overriding user agent for contexts or user contexts

  • Includes validation to prevent conflicting parameter combinations

  • Adds comprehensive test coverage for both context types


Diagram Walkthrough

flowchart LR
  A["Emulation Module"] -->|"adds method"| B["set_user_agent_override"]
  B -->|"accepts"| C["user_agent string"]
  B -->|"accepts"| D["contexts or user_contexts"]
  B -->|"validates"| E["parameter combinations"]
  B -->|"executes"| F["BiDi command"]
  G["Test Suite"] -->|"validates"| H["context override"]
  G -->|"validates"| I["user context override"]
Loading

File Walkthrough

Relevant files
Enhancement
emulation.py
Add set_user_agent_override emulation command                       

py/selenium/webdriver/common/bidi/emulation.py

  • Implements set_user_agent_override method following W3C WebDriver BiDi
    specification
  • Accepts optional user_agent parameter to set or clear override
  • Accepts either contexts or user_contexts parameter (mutually
    exclusive)
  • Includes validation to ensure exactly one context type is provided
  • Executes BiDi command with properly formatted parameters
+32/-0   
Tests
bidi_emulation_tests.py
Add user agent override tests for contexts and user contexts

py/test/selenium/webdriver/common/bidi_emulation_tests.py

  • Adds helper function get_browser_user_agent to retrieve current user
    agent via script evaluation
  • Adds test test_set_user_agent_override_with_contexts to verify
    override with browsing contexts
  • Adds test test_set_user_agent_override_with_user_contexts to verify
    override with user contexts
  • Tests both setting custom user agent and clearing override (resetting
    to initial value)
+47/-0   

@selenium-ci selenium-ci added C-py Python Bindings B-devtools Includes everything BiDi or Chrome DevTools related labels Nov 14, 2025
@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Nov 14, 2025

PR Compliance Guide 🔍

(Compliance updated until commit a9ae8a1)

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🟡
🎫 #1234
🔴 Provide a fix or mechanism so that JavaScript in link href executes on click in affected
Firefox versions.
Investigate and ensure that clicking links with JavaScript in the href triggers as
expected in Firefox (not regressing from 2.47.1 behavior).
Verify behavior via tests demonstrating the alert or JS execution occurs.
🟡
🎫 #5678
🔴 Diagnose and resolve "Error: ConnectFailure (Connection refused)" when instantiating
multiple ChromeDriver instances on Ubuntu 16.04.4 with Chrome 65 and ChromeDriver 2.35
using Selenium 3.9.0.
Ensure subsequent ChromeDriver instantiations do not fail with connection refused errors.
Provide guidance, fixes, or configuration changes to prevent recurring connection failures
across instances.
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
Missing Auditing: The new command execution to override user agents is not accompanied by any audit logging
of who performed the action, when, or the outcome, which may be required for critical
actions.

Referred Code
def set_user_agent_override(
    self,
    user_agent: str | None = None,
    contexts: list[str] | None = None,
    user_contexts: list[str] | None = None,
) -> None:
    """Set user agent override for the given contexts or user contexts.

    Args:
        user_agent: User agent string to emulate, or None to clear the override.
        contexts: List of browsing context IDs to apply the override to.
        user_contexts: List of user context IDs to apply the override to.

    Raises:
        ValueError: If both contexts and user_contexts are provided, or if neither
            contexts nor user_contexts are provided.
    """
    if contexts is not None and user_contexts is not None:
        raise ValueError("Cannot specify both contexts and user_contexts")

    if contexts is None and user_contexts is None:


 ... (clipped 10 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Input Validation: The method accepts external parameters (lists of IDs and a user agent string) without
explicit validation or sanitization in this layer, which may rely on upstream validation
not visible in the diff.

Referred Code
def set_user_agent_override(
    self,
    user_agent: str | None = None,
    contexts: list[str] | None = None,
    user_contexts: list[str] | None = None,
) -> None:
    """Set user agent override for the given contexts or user contexts.

    Args:
        user_agent: User agent string to emulate, or None to clear the override.
        contexts: List of browsing context IDs to apply the override to.
        user_contexts: List of user context IDs to apply the override to.

    Raises:
        ValueError: If both contexts and user_contexts are provided, or if neither
            contexts nor user_contexts are provided.
    """
    if contexts is not None and user_contexts is not None:
        raise ValueError("Cannot specify both contexts and user_contexts")

    if contexts is None and user_contexts is None:


 ... (clipped 10 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

Previous compliance checks

Compliance check up to commit fcc1b83
Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🟡
🎫 #1234
🔴 In Firefox, clicking a link with JavaScript in its href should trigger the JavaScript as
it did in Selenium 2.47.1 but not in 2.48.x.
Provide a fix or behavior change so that JavaScript in link href executes on click() with
Firefox 42.
🟡
🎫 #5678
🔴 Address repeated "Error: ConnectFailure (Connection refused)" when instantiating multiple
ChromeDriver instances on Ubuntu 16.04.4 with Chrome 65 and ChromeDriver 2.35 using
Selenium 3.9.0.
Ensure subsequent ChromeDriver instantiations after the first do not fail with connection
refused.
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
No action logs: The new method executes a potentially critical emulation change without any auditing or
logging to record who triggered it, when, and with what parameters.

Referred Code
def set_user_agent_override(
    self,
    user_agent: Optional[str] = None,
    contexts: Optional[list[str]] = None,
    user_contexts: Optional[list[str]] = None,
) -> None:
    """Set user agent override for the given contexts or user contexts.

    Args:
        user_agent: User agent string to emulate, or None to clear the override.
        contexts: List of browsing context IDs to apply the override to.
        user_contexts: List of user context IDs to apply the override to.

    Raises:
        ValueError: If both contexts and user_contexts are provided, or if neither
            contexts nor user_contexts are provided.
    """
    if contexts is not None and user_contexts is not None:
        raise ValueError("Cannot specify both contexts and userContexts")

    if contexts is None and user_contexts is None:


 ... (clipped 10 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Input validation: The method forwards external inputs (user agent string and context identifiers) to a
backend without explicit validation or sanitization, which may be acceptable if trusted
but cannot be confirmed from the diff.

Referred Code
def set_user_agent_override(
    self,
    user_agent: Optional[str] = None,
    contexts: Optional[list[str]] = None,
    user_contexts: Optional[list[str]] = None,
) -> None:
    """Set user agent override for the given contexts or user contexts.

    Args:
        user_agent: User agent string to emulate, or None to clear the override.
        contexts: List of browsing context IDs to apply the override to.
        user_contexts: List of user context IDs to apply the override to.

    Raises:
        ValueError: If both contexts and user_contexts are provided, or if neither
            contexts nor user_contexts are provided.
    """
    if contexts is not None and user_contexts is not None:
        raise ValueError("Cannot specify both contexts and userContexts")

    if contexts is None and user_contexts is None:


 ... (clipped 10 lines)

Learn more about managing compliance generic rules or creating your own custom rules

@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Nov 14, 2025

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Validate against empty context lists

Add validation to ensure that contexts and user_contexts are not empty lists, in
addition to not being None, to align with the WebDriver BiDi specification.

py/selenium/webdriver/common/bidi/emulation.py [420-426]

 if contexts is not None and user_contexts is not None:
     raise ValueError("Cannot specify both contexts and userContexts")
 
-if contexts is None and user_contexts is None:
-    raise ValueError("Must specify either contexts or userContexts")
+if (contexts is None or len(contexts) == 0) and (user_contexts is None or len(user_contexts) == 0):
+    raise ValueError("Must specify either a non-empty list of contexts or userContexts")
 
 params: dict[str, Any] = {"userAgent": user_agent}
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that the implementation does not validate against empty lists for contexts or user_contexts, which violates the WebDriver BiDi specification and could lead to driver errors.

Medium
Learned
best practice
Validate user_agent input

Validate that when provided, user_agent is a non-empty string; raise a clear
ValueError otherwise.

py/selenium/webdriver/common/bidi/emulation.py [403-433]

 def set_user_agent_override(
     self,
     user_agent: Optional[str] = None,
     contexts: Optional[list[str]] = None,
     user_contexts: Optional[list[str]] = None,
 ) -> None:
-    ...
+    if contexts is not None and user_contexts is not None:
+        raise ValueError("Cannot specify both contexts and userContexts")
+    if contexts is None and user_contexts is None:
+        raise ValueError("Must specify either contexts or userContexts")
+    if user_agent is not None and (not isinstance(user_agent, str) or not user_agent.strip()):
+        raise ValueError("user_agent must be a non-empty string or None")
     params: dict[str, Any] = {"userAgent": user_agent}
+    if contexts is not None:
+        params["contexts"] = contexts
+    elif user_contexts is not None:
+        params["userContexts"] = user_contexts
+    self.conn.execute(command_builder("emulation.setUserAgentOverride", params))

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 6

__

Why:
Relevant best practice - Guard external API inputs with validation to prevent invalid values from propagating to the backend API.

Low
  • Update

@navin772 navin772 requested a review from cgoldberg November 14, 2025 13:33
Copy link
Member

@cgoldberg cgoldberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 very minor things mentioned in my review.. besides that, LGTM!

@navin772 navin772 requested a review from cgoldberg November 14, 2025 16:17
@navin772 navin772 merged commit 1df75c1 into SeleniumHQ:trunk Nov 14, 2025
22 checks passed
@navin772 navin772 deleted the py-user-agent-override branch November 14, 2025 17:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

B-devtools Includes everything BiDi or Chrome DevTools related C-py Python Bindings Review effort 2/5

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants