-
-
Notifications
You must be signed in to change notification settings - Fork 8.7k
[py][bidi]: add speculation module #17162
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: trunk
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,198 @@ | ||||||||||||||
| # Licensed to the Software Freedom Conservancy (SFC) under one | ||||||||||||||
| # or more contributor license agreements. See the NOTICE file | ||||||||||||||
| # distributed with this work for additional information | ||||||||||||||
| # regarding copyright ownership. The SFC licenses this file | ||||||||||||||
| # to you under the Apache License, Version 2.0 (the | ||||||||||||||
| # "License"); you may not use this file except in compliance | ||||||||||||||
| # with the License. You may obtain a copy of the License at | ||||||||||||||
| # | ||||||||||||||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||||||||||||||
| # | ||||||||||||||
| # Unless required by applicable law or agreed to in writing, | ||||||||||||||
| # software distributed under the License is distributed on an | ||||||||||||||
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||||||||||||||
| # KIND, either express or implied. See the License for the | ||||||||||||||
| # specific language governing permissions and limitations | ||||||||||||||
| # under the License. | ||||||||||||||
|
|
||||||||||||||
| from __future__ import annotations | ||||||||||||||
|
|
||||||||||||||
| import threading | ||||||||||||||
| from collections.abc import Callable | ||||||||||||||
|
|
||||||||||||||
| from selenium.webdriver.common.bidi.session import Session | ||||||||||||||
|
|
||||||||||||||
|
|
||||||||||||||
| class PreloadingStatus: | ||||||||||||||
| """Represents the different types of preloading statuses. | ||||||||||||||
|
|
||||||||||||||
| This status is shared by prefetches and prerenders. | ||||||||||||||
| """ | ||||||||||||||
|
|
||||||||||||||
| PENDING = "pending" | ||||||||||||||
| READY = "ready" | ||||||||||||||
| SUCCESS = "success" | ||||||||||||||
| FAILURE = "failure" | ||||||||||||||
|
|
||||||||||||||
| VALID_STATUSES = {PENDING, READY, SUCCESS, FAILURE} | ||||||||||||||
|
|
||||||||||||||
|
|
||||||||||||||
| class PrefetchStatusUpdatedParams: | ||||||||||||||
| """Parameters for the speculation.prefetchStatusUpdated event.""" | ||||||||||||||
|
|
||||||||||||||
| def __init__(self, context: str, url: str, status: str): | ||||||||||||||
| self.context = context | ||||||||||||||
| self.url = url | ||||||||||||||
| self.status = status | ||||||||||||||
|
|
||||||||||||||
| @classmethod | ||||||||||||||
| def from_json(cls, json: dict) -> PrefetchStatusUpdatedParams: | ||||||||||||||
| """Creates a PrefetchStatusUpdatedParams instance from a dictionary. | ||||||||||||||
|
|
||||||||||||||
| Args: | ||||||||||||||
| json: A dictionary containing the prefetch status updated parameters. | ||||||||||||||
|
|
||||||||||||||
| Returns: | ||||||||||||||
| A new instance of PrefetchStatusUpdatedParams. | ||||||||||||||
|
|
||||||||||||||
| Raises: | ||||||||||||||
| ValueError: If required fields are missing or have invalid types. | ||||||||||||||
| """ | ||||||||||||||
| context = json.get("context") | ||||||||||||||
| if context is None or not isinstance(context, str): | ||||||||||||||
| raise ValueError("context is required and must be a string") | ||||||||||||||
|
|
||||||||||||||
| url = json.get("url") | ||||||||||||||
| if url is None or not isinstance(url, str): | ||||||||||||||
| raise ValueError("url is required and must be a string") | ||||||||||||||
|
|
||||||||||||||
| status = json.get("status") | ||||||||||||||
| if status is None or not isinstance(status, str): | ||||||||||||||
| raise ValueError("status is required and must be a string") | ||||||||||||||
|
|
||||||||||||||
| if status not in PreloadingStatus.VALID_STATUSES: | ||||||||||||||
| raise ValueError(f"Invalid status: {status}. Must be one of {PreloadingStatus.VALID_STATUSES}") | ||||||||||||||
|
|
||||||||||||||
| return cls( | ||||||||||||||
| context=context, | ||||||||||||||
| url=url, | ||||||||||||||
| status=status, | ||||||||||||||
| ) | ||||||||||||||
|
|
||||||||||||||
|
|
||||||||||||||
| class PrefetchStatusUpdated: | ||||||||||||||
| """Event class for speculation.prefetchStatusUpdated event.""" | ||||||||||||||
|
|
||||||||||||||
| event_class = "speculation.prefetchStatusUpdated" | ||||||||||||||
|
|
||||||||||||||
| @classmethod | ||||||||||||||
| def from_json(cls, json: dict): | ||||||||||||||
| if isinstance(json, PrefetchStatusUpdatedParams): | ||||||||||||||
| return json | ||||||||||||||
| return PrefetchStatusUpdatedParams.from_json(json) | ||||||||||||||
|
|
||||||||||||||
|
|
||||||||||||||
| class Speculation: | ||||||||||||||
| """BiDi implementation of the speculation module. | ||||||||||||||
|
|
||||||||||||||
| The speculation module contains commands for managing the remote end | ||||||||||||||
| behavior for prefetches, prerenders, and speculation rules. | ||||||||||||||
| """ | ||||||||||||||
|
|
||||||||||||||
| # Maps Python event names to (bidi_event_name, event_class) | ||||||||||||||
| _EVENT_MAP: dict[str, tuple[str, type]] = { | ||||||||||||||
| "prefetch_status_updated": ("speculation.prefetchStatusUpdated", PrefetchStatusUpdated), | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| # Reverse mapping: BiDi event name to event class | ||||||||||||||
| _BIDI_TO_CLASS: dict[str, type] = { | ||||||||||||||
| "speculation.prefetchStatusUpdated": PrefetchStatusUpdated, | ||||||||||||||
|
Comment on lines
+107
to
+109
|
||||||||||||||
| # Reverse mapping: BiDi event name to event class | |
| _BIDI_TO_CLASS: dict[str, type] = { | |
| "speculation.prefetchStatusUpdated": PrefetchStatusUpdated, | |
| # Reverse mapping: BiDi event name to event class, derived from _EVENT_MAP | |
| _BIDI_TO_CLASS: dict[str, type] = { | |
| bidi_event_name: event_class for _, (bidi_event_name, event_class) in _EVENT_MAP.items() |
Copilot
AI
Mar 2, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clear_event_handlers() only unsubscribes when event_class is found in _BIDI_TO_CLASS. If the maps ever drift, this will leave remote-end event subscriptions active (and leak callbacks locally). Consider always calling session.unsubscribe(bidi_event) even if the event class can’t be resolved, and/or ensuring the reverse mapping is automatically generated so this can’t happen.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -47,6 +47,7 @@ | |||||||||||||||||||||||||||||||||
| from selenium.webdriver.common.bidi.permissions import Permissions | ||||||||||||||||||||||||||||||||||
| from selenium.webdriver.common.bidi.script import Script | ||||||||||||||||||||||||||||||||||
| from selenium.webdriver.common.bidi.session import Session | ||||||||||||||||||||||||||||||||||
| from selenium.webdriver.common.bidi.speculation import Speculation | ||||||||||||||||||||||||||||||||||
| from selenium.webdriver.common.bidi.storage import Storage | ||||||||||||||||||||||||||||||||||
| from selenium.webdriver.common.bidi.webextension import WebExtension | ||||||||||||||||||||||||||||||||||
| from selenium.webdriver.common.by import By | ||||||||||||||||||||||||||||||||||
|
|
@@ -277,6 +278,7 @@ def __init__( | |||||||||||||||||||||||||||||||||
| self._browser: Browser | None = None | ||||||||||||||||||||||||||||||||||
| self._bidi_session: Session | None = None | ||||||||||||||||||||||||||||||||||
| self._browsing_context: BrowsingContext | None = None | ||||||||||||||||||||||||||||||||||
| self._speculation: Speculation | None = None | ||||||||||||||||||||||||||||||||||
| self._storage: Storage | None = None | ||||||||||||||||||||||||||||||||||
| self._webextension: WebExtension | None = None | ||||||||||||||||||||||||||||||||||
| self._permissions: Permissions | None = None | ||||||||||||||||||||||||||||||||||
|
|
@@ -1196,6 +1198,35 @@ def browsing_context(self) -> BrowsingContext: | |||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| return self._browsing_context | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| @property | ||||||||||||||||||||||||||||||||||
| def speculation(self) -> Speculation: | ||||||||||||||||||||||||||||||||||
| """Returns a speculation module object for BiDi speculation commands. | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| The speculation module contains commands for managing the remote end | ||||||||||||||||||||||||||||||||||
| behavior for prefetches, prerenders, and speculation rules. | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| Returns: | ||||||||||||||||||||||||||||||||||
| An object containing access to BiDi speculation events. | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+1203
to
+1209
|
||||||||||||||||||||||||||||||||||
| """Returns a speculation module object for BiDi speculation commands. | |
| The speculation module contains commands for managing the remote end | |
| behavior for prefetches, prerenders, and speculation rules. | |
| Returns: | |
| An object containing access to BiDi speculation events. | |
| """Returns a speculation module object for BiDi speculation events. | |
| The speculation module exposes events related to prefetches, prerenders, | |
| and speculation rules, and allows registering and removing event | |
| handlers for those events. | |
| Returns: | |
| A Speculation object used to register and remove BiDi speculation | |
| event handlers. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docstring says the speculation module provides “commands for managing … prefetches, prerenders, and speculation rules”, but the current
Speculationimplementation only exposes event subscription helpers. Consider adjusting the wording to match the actual API surface (events-only), or implement the described commands if they’re intended to be part of this module in this PR.