Skip to content

Commit 16c1cc9

Browse files
authored
Merge pull request #3815 from seleniumbase/cdp-mode-patch-49
CDP Mode: Patch 49
2 parents 916bdb1 + 01ac27d commit 16c1cc9

File tree

8 files changed

+89
-26
lines changed

8 files changed

+89
-26
lines changed

examples/cdp_mode/ReadMe.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ with SB(uc=True, test=True, locale="en", pls="none") as sb:
364364
sb.cdp.get(url, **kwargs)
365365
sb.cdp.open(url, **kwargs)
366366
sb.cdp.reload(ignore_cache=True, script_to_evaluate_on_load=None)
367-
sb.cdp.refresh()
367+
sb.cdp.refresh(*args, **kwargs)
368368
sb.cdp.get_event_loop()
369369
sb.cdp.add_handler(event, handler)
370370
sb.cdp.find_element(selector, best_match=False, timeout=None)
@@ -384,6 +384,8 @@ sb.cdp.go_back()
384384
sb.cdp.go_forward()
385385
sb.cdp.get_navigation_history()
386386
sb.cdp.tile_windows(windows=None, max_columns=0)
387+
sb.cdp.grant_permissions(permissions, origin=None)
388+
sb.cdp.grant_all_permissions()
387389
sb.cdp.get_all_cookies(*args, **kwargs)
388390
sb.cdp.set_all_cookies(*args, **kwargs)
389391
sb.cdp.save_cookies(*args, **kwargs)

requirements.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,9 @@ rich>=14.0.0,<15
7777
# ("pip install -r requirements.txt" also installs this, but "pip install -e ." won't.)
7878

7979
coverage>=7.6.1;python_version<"3.9"
80-
coverage>=7.8.2;python_version>="3.9"
80+
coverage>=7.9.0;python_version>="3.9"
8181
pytest-cov>=5.0.0;python_version<"3.9"
82-
pytest-cov>=6.1.1;python_version>="3.9"
82+
pytest-cov>=6.2.1;python_version>="3.9"
8383
flake8==5.0.4;python_version<"3.9"
8484
flake8==7.2.0;python_version>="3.9"
8585
mccabe==0.7.0

seleniumbase/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# seleniumbase package
2-
__version__ = "4.39.3"
2+
__version__ = "4.39.4"

seleniumbase/core/browser_launcher.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,7 @@ def uc_open_with_reconnect(driver, url, reconnect_time=None):
532532

533533

534534
def uc_open_with_cdp_mode(driver, url=None, **kwargs):
535+
"""Activate CDP Mode with the URL and kwargs."""
535536
import asyncio
536537
from seleniumbase.undetected.cdp_driver import cdp_util
537538

@@ -679,6 +680,8 @@ def uc_open_with_cdp_mode(driver, url=None, **kwargs):
679680
cdp.go_forward = CDPM.go_forward
680681
cdp.get_navigation_history = CDPM.get_navigation_history
681682
cdp.tile_windows = CDPM.tile_windows
683+
cdp.grant_permissions = CDPM.grant_permissions
684+
cdp.grant_all_permissions = CDPM.grant_all_permissions
682685
cdp.get_all_cookies = CDPM.get_all_cookies
683686
cdp.set_all_cookies = CDPM.set_all_cookies
684687
cdp.save_cookies = CDPM.save_cookies
@@ -2144,6 +2147,7 @@ def _set_chrome_options(
21442147
prefs["download.prompt_for_download"] = False
21452148
prefs["download_bubble.partial_view_enabled"] = False
21462149
prefs["credentials_enable_service"] = False
2150+
prefs["autofill.credit_card_enabled"] = False
21472151
prefs["local_discovery.notifications_enabled"] = False
21482152
prefs["safebrowsing.enabled"] = False # Prevent PW "data breach" pop-ups
21492153
prefs["safebrowsing.disable_download_protection"] = True
@@ -4002,6 +4006,7 @@ def get_local_driver(
40024006
"download.directory_upgrade": True,
40034007
"download.prompt_for_download": False,
40044008
"credentials_enable_service": False,
4009+
"autofill.credit_card_enabled": False,
40054010
"local_discovery.notifications_enabled": False,
40064011
"safebrowsing.disable_download_protection": True,
40074012
"safebrowsing.enabled": False, # Prevent PW "data breach" pop-ups

seleniumbase/core/sb_cdp.py

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,23 @@ def tile_windows(self, windows=None, max_columns=0):
645645
driver.tile_windows(windows, max_columns)
646646
)
647647

648+
def grant_permissions(self, permissions, origin=None):
649+
"""Grant specific permissions to the current window.
650+
Applies to all origins if no origin is specified."""
651+
driver = self.driver
652+
if hasattr(driver, "cdp_base"):
653+
driver = driver.cdp_base
654+
return self.loop.run_until_complete(
655+
driver.grant_permissions(permissions, origin)
656+
)
657+
658+
def grant_all_permissions(self):
659+
"""Grant all permissions to the current window for all origins."""
660+
driver = self.driver
661+
if hasattr(driver, "cdp_base"):
662+
driver = driver.cdp_base
663+
return self.loop.run_until_complete(driver.grant_all_permissions())
664+
648665
def get_all_cookies(self, *args, **kwargs):
649666
driver = self.driver
650667
if hasattr(driver, "cdp_base"):
@@ -681,9 +698,7 @@ def clear_cookies(self):
681698
driver = self.driver
682699
if hasattr(driver, "cdp_base"):
683700
driver = driver.cdp_base
684-
return self.loop.run_until_complete(
685-
driver.cookies.clear()
686-
)
701+
return self.loop.run_until_complete(driver.cookies.clear())
687702

688703
def sleep(self, seconds):
689704
time.sleep(seconds)
@@ -702,9 +717,7 @@ def get_active_element_css(self):
702717

703718
js_code = active_css_js.get_active_element_css
704719
js_code = js_code.replace("return getBestSelector", "getBestSelector")
705-
return self.loop.run_until_complete(
706-
self.page.evaluate(js_code)
707-
)
720+
return self.loop.run_until_complete(self.page.evaluate(js_code))
708721

709722
def click(self, selector, timeout=None):
710723
if not timeout:
@@ -978,17 +991,13 @@ def evaluate(self, expression):
978991
"\n".join(exp_list[0:-1]) + "\n"
979992
+ exp_list[-1].strip()[len("return "):]
980993
).strip()
981-
return self.loop.run_until_complete(
982-
self.page.evaluate(expression)
983-
)
994+
return self.loop.run_until_complete(self.page.evaluate(expression))
984995

985996
def js_dumps(self, obj_name):
986997
"""Similar to evaluate(), but for dictionary results."""
987998
if obj_name.startswith("return "):
988999
obj_name = obj_name[len("return "):]
989-
return self.loop.run_until_complete(
990-
self.page.js_dumps(obj_name)
991-
)
1000+
return self.loop.run_until_complete(self.page.js_dumps(obj_name))
9921001

9931002
def maximize(self):
9941003
if self.get_window()[1].window_state.value == "maximized":
@@ -1309,6 +1318,8 @@ def get_element_attributes(self, selector):
13091318
)
13101319

13111320
def get_element_attribute(self, selector, attribute):
1321+
"""Find an element and return the value of an attribute.
1322+
Raises an exception if there's no such element or attribute."""
13121323
attributes = self.get_element_attributes(selector)
13131324
with suppress(Exception):
13141325
return attributes[attribute]
@@ -1319,10 +1330,16 @@ def get_element_attribute(self, selector, attribute):
13191330
return value
13201331

13211332
def get_attribute(self, selector, attribute):
1333+
"""Find an element and return the value of an attribute.
1334+
If the element doesn't exist: Raises an exception.
1335+
If the attribute doesn't exist: Returns None."""
13221336
return self.find_element(selector).get_attribute(attribute)
13231337

13241338
def get_element_html(self, selector):
1339+
"""Find an element and return the outerHTML."""
13251340
selector = self.__convert_to_css_if_xpath(selector)
1341+
self.find_element(selector)
1342+
self.__add_light_pause()
13261343
return self.loop.run_until_complete(
13271344
self.page.evaluate(
13281345
"""document.querySelector('%s').outerHTML"""

seleniumbase/fixtures/base_case.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1910,7 +1910,10 @@ def get_attribute(
19101910
timeout = self.__get_new_timeout(timeout)
19111911
selector, by = self.__recalculate_selector(selector, by)
19121912
if self.__is_cdp_swap_needed():
1913-
return self.cdp.get_element_attribute(selector, attribute)
1913+
if hard_fail:
1914+
return self.cdp.get_element_attribute(selector, attribute)
1915+
else:
1916+
return self.cdp.get_attribute(selector, attribute)
19141917
self.wait_for_ready_state_complete()
19151918
time.sleep(0.01)
19161919
if self.__is_shadow_selector(selector):
@@ -4883,6 +4886,7 @@ def deactivate_design_mode(self, url=None):
48834886
self.execute_script(script)
48844887

48854888
def activate_cdp_mode(self, url=None, **kwargs):
4889+
"""Activate CDP Mode with the URL and kwargs."""
48864890
if hasattr(self.driver, "_is_using_uc") and self.driver._is_using_uc:
48874891
if self.__is_cdp_swap_needed():
48884892
return # CDP Mode is already active
@@ -4898,6 +4902,8 @@ def activate_cdp_mode(self, url=None, **kwargs):
48984902
self.cdp = self.driver.cdp
48994903

49004904
def activate_recorder(self):
4905+
"""Activate Recorder Mode on the current tab/window.
4906+
For persistent Recorder Mode, use the extension instead."""
49014907
from seleniumbase.js_code.recorder_js import recorder_js
49024908

49034909
if not self.is_chromium():

seleniumbase/undetected/cdp_driver/browser.py

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import warnings
1717
from collections import defaultdict
1818
from seleniumbase import config as sb_config
19-
from typing import List, Set, Tuple, Union
19+
from typing import List, Optional, Set, Tuple, Union
2020
import mycdp as cdp
2121
from . import cdp_util as util
2222
from . import tab
@@ -504,10 +504,22 @@ async def start(self=None) -> Browser:
504504
# self.connection.handlers[cdp.inspector.Detached] = [self.stop]
505505
# return self
506506

507+
async def grant_permissions(
508+
self,
509+
permissions: List[str] | str,
510+
origin: Optional[str] = None,
511+
):
512+
"""Grant specific permissions to the current window.
513+
Applies to all origins if no origin is specified."""
514+
if isinstance(permissions, str):
515+
permissions = [permissions]
516+
await self.connection.send(
517+
cdp.browser.grant_permissions(permissions, origin)
518+
)
519+
507520
async def grant_all_permissions(self):
508521
"""
509522
Grant permissions for:
510-
accessibilityEvents
511523
audioCapture
512524
backgroundSync
513525
backgroundFetch
@@ -524,19 +536,39 @@ async def grant_all_permissions(self):
524536
notifications
525537
paymentHandler
526538
periodicBackgroundSync
527-
protectedMediaIdentifier
528539
sensors
529540
storageAccess
530541
topLevelStorageAccess
531542
videoCapture
532-
videoCapturePanTiltZoom
533543
wakeLockScreen
534544
wakeLockSystem
535545
windowManagement
536546
"""
537-
permissions = list(cdp.browser.PermissionType)
538-
permissions.remove(cdp.browser.PermissionType.FLASH)
539-
permissions.remove(cdp.browser.PermissionType.CAPTURED_SURFACE_CONTROL)
547+
permissions = [
548+
"audioCapture",
549+
"backgroundSync",
550+
"backgroundFetch",
551+
"clipboardReadWrite",
552+
"clipboardSanitizedWrite",
553+
"displayCapture",
554+
"durableStorage",
555+
"geolocation",
556+
"idleDetection",
557+
"localFonts",
558+
"midi",
559+
"midiSysex",
560+
"nfc",
561+
"notifications",
562+
"paymentHandler",
563+
"periodicBackgroundSync",
564+
"sensors",
565+
"storageAccess",
566+
"topLevelStorageAccess",
567+
"videoCapture",
568+
"wakeLockScreen",
569+
"wakeLockSystem",
570+
"windowManagement",
571+
]
540572
await self.connection.send(cdp.browser.grant_permissions(permissions))
541573

542574
async def tile_windows(self, windows=None, max_columns: int = 0):

setup.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@
126126
"Programming Language :: Python :: 3.11",
127127
"Programming Language :: Python :: 3.12",
128128
"Programming Language :: Python :: 3.13",
129+
"Programming Language :: Python :: 3.14",
129130
"Topic :: Internet",
130131
"Topic :: Internet :: WWW/HTTP :: Browsers",
131132
"Topic :: Scientific/Engineering",
@@ -233,9 +234,9 @@
233234
# Usage: coverage run -m pytest; coverage html; coverage report
234235
"coverage": [
235236
'coverage>=7.6.1;python_version<"3.9"',
236-
'coverage>=7.8.2;python_version>="3.9"',
237+
'coverage>=7.9.0;python_version>="3.9"',
237238
'pytest-cov>=5.0.0;python_version<"3.9"',
238-
'pytest-cov>=6.1.1;python_version>="3.9"',
239+
'pytest-cov>=6.2.1;python_version>="3.9"',
239240
],
240241
# pip install -e .[flake8]
241242
# Usage: flake8

0 commit comments

Comments
 (0)