Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions examples/cdp_mode/ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@

That disconnects WebDriver from Chrome (which prevents detection), and gives you access to `sb.cdp` methods (which don't trigger anti-bot checks).

> (**New:** Calling **`sb.open(url)`** from UC Mode also activates CDP Mode now.)

Simple example from [SeleniumBase/examples/cdp_mode/raw_gitlab.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/raw_gitlab.py):

```python
Expand Down
15 changes: 15 additions & 0 deletions examples/cdp_mode/raw_cdp_tabs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from seleniumbase import sb_cdp

sb = sb_cdp.Chrome()
sb.open("data:text/html,<h1>Page A</h1>")
sb.assert_text("Page A")
sb.open_new_tab()
sb.open("data:text/html,<h1>Page B</h1>")
sb.assert_text("Page B")
sb.switch_to_tab(0)
sb.assert_text("Page A")
sb.assert_text_not_visible("Page B")
sb.switch_to_tab(1)
sb.assert_text("Page B")
sb.assert_text_not_visible("Page A")
sb.driver.stop()
4 changes: 2 additions & 2 deletions examples/cdp_mode/raw_driver.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import atexit
from seleniumbase import Driver

driver = Driver(uc=True)
driver = Driver(uc=True, guest=True)
atexit.register(driver.quit)
url = "www.planetminecraft.com/account"
driver.uc_activate_cdp_mode(url)
driver.activate_cdp_mode(url)
driver.sleep(1)
driver.solve_captcha()
driver.wait_for_element_absent("input[disabled]")
Expand Down
2 changes: 1 addition & 1 deletion examples/cdp_mode/raw_planetmc.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from seleniumbase import SB

with SB(uc=True, test=True) as sb:
with SB(uc=True, test=True, guest=True) as sb:
url = "www.planetminecraft.com/account/sign_in/"
sb.activate_cdp_mode(url)
sb.sleep(1.2)
Expand Down
4 changes: 2 additions & 2 deletions mkdocs_build/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
# Minimum Python version: 3.10 (for generating docs only)

regex>=2025.11.3
pymdown-extensions>=10.16.1
pipdeptree>=2.29.0
pymdown-extensions>=10.17.1
pipdeptree>=2.30.0
python-dateutil>=2.8.2
Markdown==3.10
click==8.3.0
Expand Down
8 changes: 5 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ setuptools>=80.9.0;python_version>="3.10"
wheel>=0.45.1
attrs~=25.3.0;python_version<"3.9"
attrs>=25.4.0;python_version>="3.9"
certifi>=2025.10.5
certifi>=2025.11.12
exceptiongroup>=1.3.0
websockets~=13.1;python_version<"3.9"
websockets>=15.0.1;python_version>="3.9"
Expand Down Expand Up @@ -48,7 +48,8 @@ trio==0.27.0;python_version<"3.9"
trio>=0.31.0,<1;python_version>="3.9" and python_version<"3.10"
trio>=0.32.0,<1;python_version>="3.10"
trio-websocket~=0.12.2
wsproto==1.2.0
wsproto==1.2.0;python_version<"3.10"
wsproto==1.3.1;python_version>="3.10"
websocket-client~=1.8.0;python_version<"3.9"
websocket-client~=1.9.0;python_version>="3.9"
selenium==4.27.1;python_version<"3.9"
Expand All @@ -57,7 +58,8 @@ selenium==4.38.0;python_version>="3.10"
cssselect==1.2.0;python_version<"3.9"
cssselect==1.3.0;python_version>="3.9"
sortedcontainers==2.4.0
execnet==2.1.1
execnet==2.1.1;python_version<"3.10"
execnet==2.1.2;python_version>="3.10"
iniconfig==2.1.0;python_version<"3.10"
iniconfig==2.3.0;python_version>="3.10"
pluggy==1.5.0;python_version<"3.9"
Expand Down
2 changes: 1 addition & 1 deletion seleniumbase/__version__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# seleniumbase package
__version__ = "4.44.10"
__version__ = "4.44.11"
14 changes: 4 additions & 10 deletions seleniumbase/core/browser_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -1410,14 +1410,10 @@ def _uc_gui_click_captcha(
and driver.is_element_present("%s div" % frame)
):
frame = "%s div" % frame
elif (
driver.is_element_present('[name*="cf-turnstile-"]')
and driver.is_element_present("#challenge-form div > div")
):
elif driver.is_element_present("#challenge-form div > div"):
frame = "#challenge-form div > div"
elif (
driver.is_element_present('[name*="cf-turnstile-"]')
and driver.is_element_present(
driver.is_element_present(
'[style="display: grid;"] div div'
)
):
Expand All @@ -1430,13 +1426,11 @@ def _uc_gui_click_captcha(
):
frame = '.spacer + div div:not([class])'
elif (
driver.is_element_present('[name*="cf-turnstile-"]')
and driver.is_element_present(".spacer div:not([class])")
driver.is_element_present(".spacer div:not([class])")
):
frame = ".spacer div:not([class])"
elif (
driver.is_element_present('script[src*="challenges.c"]')
and driver.is_element_present(
driver.is_element_present(
'[data-testid*="challenge-"] div'
)
):
Expand Down
28 changes: 27 additions & 1 deletion seleniumbase/core/sb_cdp.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Add CDP methods to extend the driver"""
import asyncio
import fasteners
import mycdp
import os
import random
import re
Expand All @@ -15,6 +16,7 @@
from seleniumbase.fixtures import page_utils
from seleniumbase.fixtures import shared_utils
from seleniumbase.undetected.cdp_driver import cdp_util
from seleniumbase.undetected.cdp_driver import tab as cdp_tab


class CDPMethods():
Expand Down Expand Up @@ -1122,9 +1124,33 @@ def switch_to_newest_window(self):
self.switch_to_tab(-1)

def open_new_tab(self, url=None, switch_to=True):
driver = self.driver
if not isinstance(url, str):
url = "about:blank"
self.loop.run_until_complete(self.page.get(url, new_tab=True))
if hasattr(driver, "cdp_base"):
self.loop.run_until_complete(self.page.get(url, new_tab=True))
if switch_to:
self.switch_to_newest_tab()
return

target_id = self.loop.run_until_complete(
self.page.send(mycdp.target.create_target(url))
)
found_target = None
targets = self.loop.run_until_complete(
self.page.send(mycdp.target.get_targets())
)
for target in targets:
if str(target_id) in str(target):
found_target = target
break
if found_target:
tab_url = driver.tabs[0].websocket_url
pre_tab_url = tab_url.split("/page/")[0] + "/page/"
new_tab_url = pre_tab_url + target_id
new_tab = cdp_tab.Tab(new_tab_url, found_target, driver)
driver.targets.append(new_tab)
driver.tabs.append(new_tab)
if switch_to:
self.switch_to_newest_tab()

Expand Down
7 changes: 2 additions & 5 deletions seleniumbase/core/sb_driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,11 +288,8 @@ def highlight(self, *args, **kwargs):
selector = kwargs["selector"]
else:
selector = args[0]
if ":contains(" not in selector:
self.driver.cdp.highlight(selector)
return
else:
self.driver.connect()
self.driver.cdp.highlight(selector)
return
if "scroll" in kwargs:
kwargs.pop("scroll")
w_args = kwargs.copy()
Expand Down
14 changes: 10 additions & 4 deletions seleniumbase/fixtures/base_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,15 +230,16 @@ def open(self, url):
elif (
hasattr(self.driver, "_is_using_uc")
and self.driver._is_using_uc
and hasattr(self.driver, "_is_using_auth")
and self.driver._is_using_auth
# and hasattr(self.driver, "_is_using_auth")
# and self.driver._is_using_auth
and (
not hasattr(self.driver, "_is_using_cdp")
or not self.driver._is_using_cdp
)
):
# Auth in UC Mode requires CDP Mode
logging.info("UC Mode requires CDP Mode for auth. Activating now.")
# (and now we're always forcing it)
logging.info("open() in UC Mode now always activates CDP Mode.")
self.activate_cdp_mode(url)
return
elif (
Expand Down Expand Up @@ -6313,7 +6314,12 @@ def highlight(
scroll - the option to scroll to the element first (Default: True)
timeout - the time to wait for the element to appear """
self.__check_scope()
if not self.__is_cdp_swap_needed():
if self.__is_cdp_swap_needed():
if page_utils.is_xpath_selector(selector):
if "contains(" in selector:
self.cdp.highlight(selector)
return
else:
self._check_browser()
self.__skip_if_esc()
if isinstance(selector, WebElement):
Expand Down
5 changes: 3 additions & 2 deletions seleniumbase/fixtures/page_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -1759,14 +1759,15 @@ def open_url(driver, url):
elif (
hasattr(driver, "_is_using_uc")
and driver._is_using_uc
and hasattr(driver, "_is_using_auth")
and driver._is_using_auth
# and hasattr(driver, "_is_using_auth")
# and driver._is_using_auth
and (
not hasattr(driver, "_is_using_cdp")
or not driver._is_using_cdp
)
):
# Auth in UC Mode requires CDP Mode
# (and now we're always forcing it)
driver.uc_activate_cdp_mode(url)
return
elif (
Expand Down
20 changes: 5 additions & 15 deletions seleniumbase/plugins/pytest_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -1918,26 +1918,16 @@ def pytest_configure(config):
or " -n=" in arg_join
or " -n" in arg_join
or "-c" in sys_argv
or (
"addopts" in config.inicfg.keys()
and (
"-n=" in config.inicfg["addopts"]
or "-n " in config.inicfg["addopts"]
or "-n" in config.inicfg["addopts"]
)
)
or "-n=" in config.getini("addopts")
or "-n " in config.getini("addopts")
or "-n" in config.getini("addopts")
):
sb_config._multithreaded = True
if (
"--html" in sys_argv
or " --html=" in arg_join
or (
"addopts" in config.inicfg.keys()
and (
"--html=" in config.inicfg["addopts"]
or "--html " in config.inicfg["addopts"]
)
)
or "--html=" in config.getini("addopts")
or "--html " in config.getini("addopts")
):
sb_config._using_html_report = True
sb_config._html_report_name = config.getoption("htmlpath")
Expand Down
8 changes: 5 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@
'wheel>=0.45.1',
'attrs~=25.3.0;python_version<"3.9"',
'attrs>=25.4.0;python_version>="3.9"',
"certifi>=2025.10.5",
"certifi>=2025.11.12",
"exceptiongroup>=1.3.0",
'websockets~=13.1;python_version<"3.9"',
'websockets>=15.0.1;python_version>="3.9"',
Expand Down Expand Up @@ -197,7 +197,8 @@
'trio>=0.31.0,<1;python_version>="3.9" and python_version<"3.10"',
'trio>=0.32.0,<1;python_version>="3.10"',
'trio-websocket~=0.12.2',
'wsproto==1.2.0',
'wsproto==1.2.0;python_version<"3.10"',
'wsproto==1.3.1;python_version>="3.10"',
'websocket-client~=1.8.0;python_version<"3.9"',
'websocket-client~=1.9.0;python_version>="3.9"',
'selenium==4.27.1;python_version<"3.9"',
Expand All @@ -206,7 +207,8 @@
'cssselect==1.2.0;python_version<"3.9"',
'cssselect==1.3.0;python_version>="3.9"',
"sortedcontainers==2.4.0",
'execnet==2.1.1',
'execnet==2.1.1;python_version<"3.10"',
'execnet==2.1.2;python_version>="3.10"',
'iniconfig==2.1.0;python_version<"3.10"',
'iniconfig==2.3.0;python_version>="3.10"',
'pluggy==1.5.0;python_version<"3.9"',
Expand Down