Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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)

AltStyle によって変換されたページ (->オリジナル) /