seleniumbase 4.44.2__py3-none-any.whl → 4.45.10__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. seleniumbase/__version__.py +1 -1
  2. seleniumbase/behave/behave_sb.py +14 -0
  3. seleniumbase/console_scripts/run.py +1 -0
  4. seleniumbase/console_scripts/sb_install.py +142 -11
  5. seleniumbase/console_scripts/sb_mkdir.py +76 -0
  6. seleniumbase/console_scripts/sb_mkrec.py +25 -0
  7. seleniumbase/console_scripts/sb_recorder.py +40 -3
  8. seleniumbase/core/browser_launcher.py +279 -117
  9. seleniumbase/core/detect_b_ver.py +8 -6
  10. seleniumbase/core/log_helper.py +11 -16
  11. seleniumbase/core/mysql.py +1 -1
  12. seleniumbase/core/report_helper.py +3 -7
  13. seleniumbase/core/sb_cdp.py +275 -78
  14. seleniumbase/core/sb_driver.py +36 -5
  15. seleniumbase/core/session_helper.py +2 -4
  16. seleniumbase/drivers/chromium_drivers/__init__.py +0 -0
  17. seleniumbase/fixtures/base_case.py +251 -201
  18. seleniumbase/fixtures/constants.py +1 -0
  19. seleniumbase/fixtures/js_utils.py +52 -14
  20. seleniumbase/fixtures/page_actions.py +18 -7
  21. seleniumbase/fixtures/page_utils.py +4 -2
  22. seleniumbase/fixtures/shared_utils.py +2 -4
  23. seleniumbase/masterqa/master_qa.py +16 -2
  24. seleniumbase/plugins/base_plugin.py +8 -0
  25. seleniumbase/plugins/driver_manager.py +15 -5
  26. seleniumbase/plugins/pytest_plugin.py +43 -57
  27. seleniumbase/plugins/sb_manager.py +23 -19
  28. seleniumbase/plugins/selenium_plugin.py +20 -13
  29. seleniumbase/undetected/__init__.py +11 -10
  30. seleniumbase/undetected/cdp.py +1 -12
  31. seleniumbase/undetected/cdp_driver/browser.py +330 -128
  32. seleniumbase/undetected/cdp_driver/cdp_util.py +48 -14
  33. seleniumbase/undetected/cdp_driver/config.py +78 -11
  34. seleniumbase/undetected/cdp_driver/connection.py +15 -43
  35. seleniumbase/undetected/cdp_driver/element.py +37 -22
  36. seleniumbase/undetected/cdp_driver/tab.py +414 -39
  37. {seleniumbase-4.44.2.dist-info → seleniumbase-4.45.10.dist-info}/METADATA +140 -152
  38. {seleniumbase-4.44.2.dist-info → seleniumbase-4.45.10.dist-info}/RECORD +42 -41
  39. {seleniumbase-4.44.2.dist-info → seleniumbase-4.45.10.dist-info}/licenses/LICENSE +1 -1
  40. {seleniumbase-4.44.2.dist-info → seleniumbase-4.45.10.dist-info}/WHEEL +0 -0
  41. {seleniumbase-4.44.2.dist-info → seleniumbase-4.45.10.dist-info}/entry_points.txt +0 -0
  42. {seleniumbase-4.44.2.dist-info → seleniumbase-4.45.10.dist-info}/top_level.txt +0 -0
@@ -32,6 +32,7 @@ from seleniumbase.drivers import opera_drivers # still uses chromedriver
32
32
  from seleniumbase.drivers import brave_drivers # still uses chromedriver
33
33
  from seleniumbase.drivers import comet_drivers # still uses chromedriver
34
34
  from seleniumbase.drivers import atlas_drivers # still uses chromedriver
35
+ from seleniumbase.drivers import chromium_drivers # still uses chromedriver
35
36
  from seleniumbase import extensions # browser extensions storage folder
36
37
  from seleniumbase.config import settings
37
38
  from seleniumbase.core import detect_b_ver
@@ -52,6 +53,9 @@ DRIVER_DIR_OPERA = os.path.dirname(os.path.realpath(opera_drivers.__file__))
52
53
  DRIVER_DIR_BRAVE = os.path.dirname(os.path.realpath(brave_drivers.__file__))
53
54
  DRIVER_DIR_COMET = os.path.dirname(os.path.realpath(comet_drivers.__file__))
54
55
  DRIVER_DIR_ATLAS = os.path.dirname(os.path.realpath(atlas_drivers.__file__))
56
+ DRIVER_DIR_CHROMIUM = os.path.dirname(
57
+ os.path.realpath(chromium_drivers.__file__)
58
+ )
55
59
  # Make sure that the SeleniumBase DRIVER_DIR is at the top of the System PATH
56
60
  # (Changes to the System PATH with os.environ only last during the test run)
57
61
  if not os.environ["PATH"].startswith(DRIVER_DIR):
@@ -104,10 +108,7 @@ else:
104
108
  def log_d(message):
105
109
  """If setting sb_config.settings.HIDE_DRIVER_DOWNLOADS to True,
106
110
  output from driver downloads are logged instead of printed."""
107
- if (
108
- hasattr(settings, "HIDE_DRIVER_DOWNLOADS")
109
- and settings.HIDE_DRIVER_DOWNLOADS
110
- ):
111
+ if getattr(settings, "HIDE_DRIVER_DOWNLOADS", None):
111
112
  logging.debug(message)
112
113
  else:
113
114
  print(message)
@@ -159,9 +160,21 @@ def extend_driver(
159
160
  # Extend the driver with new methods
160
161
  driver.default_find_element = driver.find_element
161
162
  driver.default_find_elements = driver.find_elements
163
+ driver.default_add_cookie = driver.add_cookie
164
+ driver.default_get_cookie = driver.get_cookie
165
+ driver.default_delete_cookie = driver.delete_cookie
166
+ driver.default_back = driver.back
167
+ driver.default_forward = driver.forward
168
+ driver.default_refresh = driver.refresh
162
169
  DM = sb_driver.DriverMethods(driver)
163
170
  driver.find_element = DM.find_element
164
171
  driver.find_elements = DM.find_elements
172
+ driver.add_cookie = DM.add_cookie
173
+ driver.get_cookie = DM.get_cookie
174
+ driver.delete_cookie = DM.delete_cookie
175
+ driver.back = DM.back
176
+ driver.forward = DM.forward
177
+ driver.refresh = DM.refresh
165
178
  driver.locator = DM.locator
166
179
  page = types.SimpleNamespace()
167
180
  page.open = DM.open_url
@@ -293,7 +306,19 @@ def extend_driver(
293
306
  )
294
307
  if hasattr(driver, "proxy"):
295
308
  driver.set_wire_proxy = DM.set_wire_proxy
309
+ completed_loads = []
310
+ for ext_dir in sb_config._ext_dirs:
311
+ if ext_dir not in completed_loads:
312
+ completed_loads.append(ext_dir)
313
+ if not use_uc and os.path.exists(os.path.realpath(ext_dir)):
314
+ with suppress(Exception):
315
+ driver.webextension.install(os.path.realpath(ext_dir))
316
+ driver._is_using_auth = False
296
317
  if proxy_auth:
318
+ driver._is_using_auth = True
319
+ if not use_uc and os.path.exists(proxy_helper.PROXY_DIR_PATH):
320
+ with suppress(Exception):
321
+ driver.webextension.install(proxy_helper.PROXY_DIR_PATH)
297
322
  # Proxy needs a moment to load in Manifest V3
298
323
  if use_uc:
299
324
  time.sleep(0.14)
@@ -419,16 +444,16 @@ def has_captcha(text):
419
444
  "<title>403 Forbidden</title>" in text
420
445
  or "Permission Denied</title>" in text
421
446
  or 'id="challenge-error-text"' in text
447
+ or "/challenge-platform/h/b/" in text
422
448
  or "<title>Just a moment..." in text
423
449
  or 'action="/?__cf_chl_f_tk' in text
424
450
  or 'id="challenge-widget-' in text
425
451
  or 'src="chromedriver.js"' in text
452
+ or "com/recaptcha/api.js" in text
426
453
  or 'class="g-recaptcha"' in text
427
454
  or 'content="Pixelscan"' in text
428
455
  or 'id="challenge-form"' in text
429
- or "/challenge-platform" in text
430
456
  or "window._cf_chl_opt" in text
431
- or "/recaptcha/api.js" in text
432
457
  or "/turnstile/" in text
433
458
  ):
434
459
  return True
@@ -446,6 +471,12 @@ def uc_execute_cdp_cmd(driver, *args, **kwargs):
446
471
  return driver.default_execute_cdp_cmd(*args, **kwargs)
447
472
 
448
473
 
474
+ def updated_get(driver, url):
475
+ if url and ":" not in url and "." in url:
476
+ url = "https:" + url
477
+ driver.default_get(url)
478
+
479
+
449
480
  def uc_special_open_if_cf(
450
481
  driver,
451
482
  url,
@@ -455,6 +486,8 @@ def uc_special_open_if_cf(
455
486
  device_height=None,
456
487
  device_pixel_ratio=None,
457
488
  ):
489
+ if url and ":" not in url and "." in url:
490
+ url = "https:" + url
458
491
  if url.startswith("http:") or url.startswith("https:"):
459
492
  special = False
460
493
  with suppress(Exception):
@@ -624,10 +657,8 @@ def uc_open_with_cdp_mode(driver, url=None, **kwargs):
624
657
  safe_url = False
625
658
 
626
659
  if (
627
- hasattr(driver, "_is_using_cdp")
628
- and driver._is_using_cdp
629
- and hasattr(driver, "cdp")
630
- and driver.cdp
660
+ getattr(driver, "_is_using_cdp", None)
661
+ and getattr(driver, "cdp", None)
631
662
  and hasattr(driver.cdp, "loop")
632
663
  ):
633
664
  # CDP Mode was already initialized
@@ -667,6 +698,7 @@ def uc_open_with_cdp_mode(driver, url=None, **kwargs):
667
698
  xvfb=xvfb,
668
699
  xvfb_metrics=xvfb_metrics,
669
700
  browser_executable_path=binary_location,
701
+ mobile=getattr(sb_config, "_cdp_mobile_mode", None),
670
702
  )
671
703
  )
672
704
  loop.run_until_complete(driver.cdp_base.wait(0))
@@ -730,6 +762,11 @@ def uc_open_with_cdp_mode(driver, url=None, **kwargs):
730
762
  cdp.refresh = CDPM.refresh
731
763
  cdp.add_handler = CDPM.add_handler
732
764
  cdp.get_event_loop = CDPM.get_event_loop
765
+ cdp.get_rd_host = CDPM.get_rd_host
766
+ cdp.get_rd_port = CDPM.get_rd_port
767
+ cdp.get_rd_url = CDPM.get_rd_url
768
+ cdp.get_endpoint_url = CDPM.get_endpoint_url
769
+ cdp.get_port = CDPM.get_port
733
770
  cdp.find_element = CDPM.find_element
734
771
  cdp.find = CDPM.find_element
735
772
  cdp.locator = CDPM.find_element
@@ -795,6 +832,7 @@ def uc_open_with_cdp_mode(driver, url=None, **kwargs):
795
832
  cdp.is_attribute_present = CDPM.is_attribute_present
796
833
  cdp.is_online = CDPM.is_online
797
834
  cdp.solve_captcha = CDPM.solve_captcha
835
+ cdp.click_captcha = CDPM.click_captcha
798
836
  cdp.gui_press_key = CDPM.gui_press_key
799
837
  cdp.gui_press_keys = CDPM.gui_press_keys
800
838
  cdp.gui_write = CDPM.gui_write
@@ -808,6 +846,8 @@ def uc_open_with_cdp_mode(driver, url=None, **kwargs):
808
846
  cdp.gui_hover_x_y = CDPM.gui_hover_x_y
809
847
  cdp.gui_hover_element = CDPM.gui_hover_element
810
848
  cdp.gui_hover_and_click = CDPM.gui_hover_and_click
849
+ cdp.hover_element = CDPM.hover_element
850
+ cdp.hover_and_click = CDPM.hover_and_click
811
851
  cdp.internalize_links = CDPM.internalize_links
812
852
  cdp.open_new_window = CDPM.open_new_window
813
853
  cdp.switch_to_window = CDPM.switch_to_window
@@ -828,6 +868,7 @@ def uc_open_with_cdp_mode(driver, url=None, **kwargs):
828
868
  cdp.get_element_position = CDPM.get_element_position
829
869
  cdp.get_gui_element_rect = CDPM.get_gui_element_rect
830
870
  cdp.get_gui_element_center = CDPM.get_gui_element_center
871
+ cdp.get_html = CDPM.get_html
831
872
  cdp.get_page_source = CDPM.get_page_source
832
873
  cdp.get_user_agent = CDPM.get_user_agent
833
874
  cdp.get_cookie_string = CDPM.get_cookie_string
@@ -924,7 +965,17 @@ def uc_open_with_cdp_mode(driver, url=None, **kwargs):
924
965
  cdp.core = core_items
925
966
  cdp.loop = cdp.get_event_loop()
926
967
  driver.cdp = cdp
968
+ driver.solve_captcha = CDPM.solve_captcha
969
+ driver.click_captcha = CDPM.click_captcha
970
+ driver.find_element_by_text = CDPM.find_element_by_text
927
971
  driver._is_using_cdp = True
972
+ if (
973
+ getattr(sb_config, "_cdp_proxy", None)
974
+ and "@" in sb_config._cdp_proxy
975
+ ):
976
+ time.sleep(0.077)
977
+ loop.run_until_complete(page.wait(0.25))
978
+ time.sleep(0.022)
928
979
 
929
980
 
930
981
  def uc_activate_cdp_mode(driver, url=None, **kwargs):
@@ -999,7 +1050,7 @@ def uc_click(
999
1050
  def verify_pyautogui_has_a_headed_browser(driver):
1000
1051
  """PyAutoGUI requires a headed browser so that it can
1001
1052
  focus on the correct element when performing actions."""
1002
- if hasattr(driver, "_is_hidden") and driver._is_hidden:
1053
+ if getattr(driver, "_is_hidden", None):
1003
1054
  raise Exception(
1004
1055
  "PyAutoGUI can't be used in headless mode!"
1005
1056
  )
@@ -1035,11 +1086,9 @@ def __install_pyautogui_if_missing():
1035
1086
  xvfb_width = 1366
1036
1087
  xvfb_height = 768
1037
1088
  if (
1038
- hasattr(sb_config, "_xvfb_width")
1039
- and sb_config._xvfb_width
1089
+ getattr(sb_config, "_xvfb_width", None)
1040
1090
  and isinstance(sb_config._xvfb_width, int)
1041
- and hasattr(sb_config, "_xvfb_height")
1042
- and sb_config._xvfb_height
1091
+ and getattr(sb_config, "_xvfb_height", None)
1043
1092
  and isinstance(sb_config._xvfb_height, int)
1044
1093
  ):
1045
1094
  xvfb_width = sb_config._xvfb_width
@@ -1066,8 +1115,7 @@ def __install_pyautogui_if_missing():
1066
1115
  sb_config._virtual_display = _xvfb_display
1067
1116
  sb_config.headless_active = True
1068
1117
  if (
1069
- hasattr(sb_config, "reuse_session")
1070
- and sb_config.reuse_session
1118
+ getattr(sb_config, "reuse_session", None)
1071
1119
  and hasattr(sb_config, "_vd_list")
1072
1120
  and isinstance(sb_config._vd_list, list)
1073
1121
  ):
@@ -1099,8 +1147,7 @@ def get_configured_pyautogui(pyautogui_copy):
1099
1147
  and "DISPLAY" in os.environ.keys()
1100
1148
  ):
1101
1149
  if (
1102
- hasattr(sb_config, "_pyautogui_x11_display")
1103
- and sb_config._pyautogui_x11_display
1150
+ getattr(sb_config, "_pyautogui_x11_display", None)
1104
1151
  and hasattr(pyautogui_copy._pyautogui_x11, "_display")
1105
1152
  and (
1106
1153
  sb_config._pyautogui_x11_display
@@ -1205,10 +1252,7 @@ def uc_gui_click_x_y(driver, x, y, timeframe=0.25):
1205
1252
  connected = driver.is_connected()
1206
1253
  if (
1207
1254
  not connected
1208
- and (
1209
- not hasattr(sb_config, "_saved_width_ratio")
1210
- or not sb_config._saved_width_ratio
1211
- )
1255
+ and not getattr(sb_config, "_saved_width_ratio", None)
1212
1256
  and not __is_cdp_swap_needed(driver)
1213
1257
  ):
1214
1258
  driver.reconnect(0.1)
@@ -1257,8 +1301,12 @@ def uc_gui_click_x_y(driver, x, y, timeframe=0.25):
1257
1301
  def _on_a_cf_turnstile_page(driver):
1258
1302
  source = driver.get_page_source()
1259
1303
  if (
1260
- 'data-callback="onCaptchaSuccess"' in source
1261
- or "/challenge-platform/scripts/" in source
1304
+ (
1305
+ 'data-callback="onCaptchaSuccess"' in source
1306
+ and 'title="reCAPTCHA"' not in source
1307
+ and 'id="recaptcha-token"' not in source
1308
+ )
1309
+ or "/challenge-platform/h/b/" in source
1262
1310
  or 'id="challenge-widget-' in source
1263
1311
  or "challenges.cloudf" in source
1264
1312
  or "cf-turnstile-" in source
@@ -1387,14 +1435,10 @@ def _uc_gui_click_captcha(
1387
1435
  and driver.is_element_present("%s div" % frame)
1388
1436
  ):
1389
1437
  frame = "%s div" % frame
1390
- elif (
1391
- driver.is_element_present('[name*="cf-turnstile-"]')
1392
- and driver.is_element_present("#challenge-form div > div")
1393
- ):
1438
+ elif driver.is_element_present("#challenge-form div > div"):
1394
1439
  frame = "#challenge-form div > div"
1395
1440
  elif (
1396
- driver.is_element_present('[name*="cf-turnstile-"]')
1397
- and driver.is_element_present(
1441
+ driver.is_element_present(
1398
1442
  '[style="display: grid;"] div div'
1399
1443
  )
1400
1444
  ):
@@ -1407,13 +1451,11 @@ def _uc_gui_click_captcha(
1407
1451
  ):
1408
1452
  frame = '.spacer + div div:not([class])'
1409
1453
  elif (
1410
- driver.is_element_present('[name*="cf-turnstile-"]')
1411
- and driver.is_element_present(".spacer div:not([class])")
1454
+ driver.is_element_present(".spacer div:not([class])")
1412
1455
  ):
1413
1456
  frame = ".spacer div:not([class])"
1414
1457
  elif (
1415
- driver.is_element_present('script[src*="challenges.c"]')
1416
- and driver.is_element_present(
1458
+ driver.is_element_present(
1417
1459
  '[data-testid*="challenge-"] div'
1418
1460
  )
1419
1461
  ):
@@ -1458,6 +1500,10 @@ def _uc_gui_click_captcha(
1458
1500
  '[data-callback="onCaptchaSuccess"]'
1459
1501
  ):
1460
1502
  frame = '[data-callback="onCaptchaSuccess"]'
1503
+ elif driver.is_element_present(
1504
+ "div:not([class]) > div:not([class])"
1505
+ ):
1506
+ frame = "div:not([class]) > div:not([class])"
1461
1507
  else:
1462
1508
  return
1463
1509
  if (
@@ -1801,6 +1847,10 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
1801
1847
  frame = ".cf-turnstile-wrapper"
1802
1848
  elif driver.is_element_present('[class="cf-turnstile"]'):
1803
1849
  frame = '[class="cf-turnstile"]'
1850
+ elif driver.is_element_present(
1851
+ "div:not([class]) > div:not([class])"
1852
+ ):
1853
+ frame = "div:not([class]) > div:not([class])"
1804
1854
  else:
1805
1855
  return
1806
1856
  else:
@@ -1860,8 +1910,7 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
1860
1910
  driver.is_element_present(".footer .clearfix .ray-id")
1861
1911
  or driver.is_element_present("script[data-cf-beacon]")
1862
1912
  )
1863
- and hasattr(sb_config, "_saved_cf_tab_count")
1864
- and sb_config._saved_cf_tab_count
1913
+ and getattr(sb_config, "_saved_cf_tab_count", None)
1865
1914
  and not __is_cdp_swap_needed(driver)
1866
1915
  ):
1867
1916
  driver.uc_open_with_disconnect(driver.current_url, 3.8)
@@ -2087,6 +2136,7 @@ def _add_chrome_proxy_extension(
2087
2136
  """Implementation of https://stackoverflow.com/a/35293284/7058266
2088
2137
  for https://stackoverflow.com/q/12848327/7058266
2089
2138
  (Run Selenium on a proxy server that requires authentication.)"""
2139
+ zip_it = False
2090
2140
  args = " ".join(sys.argv)
2091
2141
  bypass_list = proxy_bypass_list
2092
2142
  if (
@@ -2466,14 +2516,28 @@ def _set_chrome_options(
2466
2516
  # Can be a comma-separated list of .ZIP or .CRX files
2467
2517
  extension_zip_list = extension_zip.split(",")
2468
2518
  for extension_zip_item in extension_zip_list:
2469
- abs_path = os.path.abspath(extension_zip_item)
2470
- chrome_options.add_extension(abs_path)
2519
+ abs_path = os.path.realpath(extension_zip_item)
2520
+ if os.path.exists(abs_path):
2521
+ try:
2522
+ abs_path_dir = os.path.join(
2523
+ DOWNLOADS_FOLDER, abs_path.split(".")[0]
2524
+ )
2525
+ _unzip_to_new_folder(abs_path, abs_path_dir)
2526
+ chrome_options = add_chrome_ext_dir(
2527
+ chrome_options, abs_path_dir
2528
+ )
2529
+ sb_config._ext_dirs.append(abs_path_dir)
2530
+ except Exception:
2531
+ with suppress(Exception):
2532
+ chrome_options.add_extension(abs_path)
2471
2533
  if extension_dir:
2472
2534
  # load-extension input can be a comma-separated list
2473
2535
  abs_path = (
2474
- ",".join(os.path.abspath(p) for p in extension_dir.split(","))
2536
+ ",".join(os.path.realpath(p) for p in extension_dir.split(","))
2475
2537
  )
2476
2538
  chrome_options = add_chrome_ext_dir(chrome_options, abs_path)
2539
+ for p in extension_dir.split(","):
2540
+ sb_config._ext_dirs.append(os.path.realpath(p))
2477
2541
  if (
2478
2542
  page_load_strategy
2479
2543
  and page_load_strategy.lower() in ["eager", "none"]
@@ -2482,8 +2546,7 @@ def _set_chrome_options(
2482
2546
  chrome_options.page_load_strategy = page_load_strategy.lower()
2483
2547
  elif (
2484
2548
  not page_load_strategy
2485
- and hasattr(settings, "PAGE_LOAD_STRATEGY")
2486
- and settings.PAGE_LOAD_STRATEGY
2549
+ and getattr(settings, "PAGE_LOAD_STRATEGY", None)
2487
2550
  and settings.PAGE_LOAD_STRATEGY.lower() in ["eager", "none"]
2488
2551
  ):
2489
2552
  # Only change it if not "normal", which is the default.
@@ -2508,37 +2571,32 @@ def _set_chrome_options(
2508
2571
  if (settings.DISABLE_CSP_ON_CHROME or disable_csp) and not headless:
2509
2572
  # Headless Chrome does not support extensions, which are required
2510
2573
  # for disabling the Content Security Policy on Chrome.
2511
- if is_using_uc(undetectable, browser_name):
2512
- disable_csp_zip = DISABLE_CSP_ZIP_PATH
2513
- disable_csp_dir = os.path.join(DOWNLOADS_FOLDER, "disable_csp")
2514
- _unzip_to_new_folder(disable_csp_zip, disable_csp_dir)
2515
- chrome_options = add_chrome_ext_dir(
2516
- chrome_options, disable_csp_dir
2517
- )
2518
- else:
2519
- chrome_options = _add_chrome_disable_csp_extension(chrome_options)
2574
+ disable_csp_zip = DISABLE_CSP_ZIP_PATH
2575
+ disable_csp_dir = os.path.join(DOWNLOADS_FOLDER, "disable_csp")
2576
+ _unzip_to_new_folder(disable_csp_zip, disable_csp_dir)
2577
+ chrome_options = add_chrome_ext_dir(
2578
+ chrome_options, disable_csp_dir
2579
+ )
2580
+ sb_config._ext_dirs.append(disable_csp_dir)
2520
2581
  if ad_block_on and not headless:
2521
2582
  # Headless Chrome does not support extensions.
2522
- if is_using_uc(undetectable, browser_name):
2523
- ad_block_zip = AD_BLOCK_ZIP_PATH
2524
- ad_block_dir = os.path.join(DOWNLOADS_FOLDER, "ad_block")
2525
- _unzip_to_new_folder(ad_block_zip, ad_block_dir)
2526
- chrome_options = add_chrome_ext_dir(chrome_options, ad_block_dir)
2527
- else:
2528
- chrome_options = _add_chrome_ad_block_extension(chrome_options)
2583
+ ad_block_zip = AD_BLOCK_ZIP_PATH
2584
+ ad_block_dir = os.path.join(DOWNLOADS_FOLDER, "ad_block")
2585
+ _unzip_to_new_folder(ad_block_zip, ad_block_dir)
2586
+ chrome_options = add_chrome_ext_dir(chrome_options, ad_block_dir)
2587
+ sb_config._ext_dirs.append(ad_block_dir)
2529
2588
  if recorder_ext and not headless:
2530
- if is_using_uc(undetectable, browser_name):
2531
- recorder_zip = RECORDER_ZIP_PATH
2532
- recorder_dir = os.path.join(DOWNLOADS_FOLDER, "recorder")
2533
- _unzip_to_new_folder(recorder_zip, recorder_dir)
2534
- chrome_options = add_chrome_ext_dir(chrome_options, recorder_dir)
2535
- else:
2536
- chrome_options = _add_chrome_recorder_extension(chrome_options)
2589
+ recorder_zip = RECORDER_ZIP_PATH
2590
+ recorder_dir = os.path.join(DOWNLOADS_FOLDER, "recorder")
2591
+ _unzip_to_new_folder(recorder_zip, recorder_dir)
2592
+ chrome_options = add_chrome_ext_dir(chrome_options, recorder_dir)
2593
+ sb_config._ext_dirs.append(recorder_dir)
2537
2594
  if chromium_arg and "sbase" in chromium_arg:
2538
2595
  sbase_ext_zip = SBASE_EXT_ZIP_PATH
2539
2596
  sbase_ext_dir = os.path.join(DOWNLOADS_FOLDER, "sbase_ext")
2540
2597
  _unzip_to_new_folder(sbase_ext_zip, sbase_ext_dir)
2541
2598
  chrome_options = add_chrome_ext_dir(chrome_options, sbase_ext_dir)
2599
+ sb_config._ext_dirs.append(sbase_ext_dir)
2542
2600
  if proxy_string:
2543
2601
  if proxy_auth:
2544
2602
  zip_it = True
@@ -2691,6 +2749,7 @@ def _set_chrome_options(
2691
2749
  chrome_options.add_argument("--disable-renderer-backgrounding")
2692
2750
  chrome_options.add_argument("--disable-backgrounding-occluded-windows")
2693
2751
  chrome_options.add_argument("--disable-client-side-phishing-detection")
2752
+ chrome_options.add_argument("--disable-device-discovery-notifications")
2694
2753
  chrome_options.add_argument("--disable-oopr-debug-crash-dump")
2695
2754
  chrome_options.add_argument("--disable-top-sites")
2696
2755
  chrome_options.add_argument("--ash-no-nudges")
@@ -2707,6 +2766,7 @@ def _set_chrome_options(
2707
2766
  included_disabled_features.append("OptimizationHints")
2708
2767
  included_disabled_features.append("OptimizationHintsFetching")
2709
2768
  included_disabled_features.append("Translate")
2769
+ included_disabled_features.append("ComponentUpdater")
2710
2770
  included_disabled_features.append("OptimizationTargetPrediction")
2711
2771
  included_disabled_features.append("OptimizationGuideModelDownloading")
2712
2772
  included_disabled_features.append("DownloadBubble")
@@ -2717,13 +2777,24 @@ def _set_chrome_options(
2717
2777
  included_disabled_features.append("SidePanelPinning")
2718
2778
  included_disabled_features.append("UserAgentClientHint")
2719
2779
  included_disabled_features.append("DisableLoadExtensionCommandLineSwitch")
2780
+ included_disabled_features.append("Bluetooth")
2781
+ included_disabled_features.append("WebBluetooth")
2782
+ included_disabled_features.append("UnifiedWebBluetooth")
2783
+ included_disabled_features.append("WebAuthentication")
2784
+ included_disabled_features.append("PasskeyAuth")
2720
2785
  for item in extra_disabled_features:
2721
2786
  if item not in included_disabled_features:
2722
2787
  included_disabled_features.append(item)
2723
2788
  d_f_string = ",".join(included_disabled_features)
2724
2789
  chrome_options.add_argument("--disable-features=%s" % d_f_string)
2725
- if proxy_auth:
2790
+ chrome_options.add_argument("--enable-unsafe-extension-debugging")
2791
+ if proxy_string:
2726
2792
  chrome_options.add_argument("--test-type")
2793
+ if proxy_auth or sb_config._ext_dirs:
2794
+ if not is_using_uc(undetectable, browser_name):
2795
+ chrome_options.add_argument("--remote-debugging-pipe")
2796
+ chrome_options.enable_webextensions = True
2797
+ chrome_options.enable_bidi = True
2727
2798
  if (
2728
2799
  is_using_uc(undetectable, browser_name)
2729
2800
  and (
@@ -2741,7 +2812,7 @@ def _set_chrome_options(
2741
2812
  chrome_options.add_argument("--disable-popup-blocking")
2742
2813
  # Skip remaining options that trigger anti-bot services
2743
2814
  return chrome_options
2744
- if not proxy_auth:
2815
+ if not proxy_string:
2745
2816
  chrome_options.add_argument("--test-type")
2746
2817
  chrome_options.add_argument("--log-level=3")
2747
2818
  chrome_options.add_argument("--no-first-run")
@@ -2988,33 +3059,29 @@ def get_driver(
2988
3059
  device_pixel_ratio=None,
2989
3060
  browser=None, # A duplicate of browser_name to avoid confusion
2990
3061
  ):
3062
+ sb_config._ext_dirs = []
2991
3063
  driver_dir = DRIVER_DIR
2992
- if (
2993
- hasattr(sb_config, "binary_location")
2994
- and sb_config.binary_location == "cft"
2995
- ):
3064
+ if binary_location == "_chromium_":
3065
+ driver_dir = DRIVER_DIR_CHROMIUM
3066
+ elif binary_location == "cft":
2996
3067
  driver_dir = DRIVER_DIR_CFT
2997
- if (
2998
- hasattr(sb_config, "binary_location")
2999
- and sb_config.binary_location == "chs"
3000
- ):
3068
+ elif binary_location == "chs":
3001
3069
  driver_dir = DRIVER_DIR_CHS
3002
- if _special_binary_exists(binary_location, "opera"):
3070
+ elif _special_binary_exists(binary_location, "opera"):
3003
3071
  driver_dir = DRIVER_DIR_OPERA
3004
3072
  sb_config._cdp_browser = "opera"
3005
- if _special_binary_exists(binary_location, "brave"):
3073
+ elif _special_binary_exists(binary_location, "brave"):
3006
3074
  driver_dir = DRIVER_DIR_BRAVE
3007
3075
  sb_config._cdp_browser = "brave"
3008
- if _special_binary_exists(binary_location, "comet"):
3076
+ elif _special_binary_exists(binary_location, "comet"):
3009
3077
  driver_dir = DRIVER_DIR_COMET
3010
3078
  sb_config._cdp_browser = "comet"
3011
- if _special_binary_exists(binary_location, "atlas"):
3079
+ elif _special_binary_exists(binary_location, "atlas"):
3012
3080
  driver_dir = DRIVER_DIR_ATLAS
3013
3081
  sb_config._cdp_browser = "atlas"
3014
3082
  if (
3015
3083
  hasattr(sb_config, "settings")
3016
- and hasattr(sb_config.settings, "NEW_DRIVER_DIR")
3017
- and sb_config.settings.NEW_DRIVER_DIR
3084
+ and getattr(sb_config.settings, "NEW_DRIVER_DIR", None)
3018
3085
  and os.path.exists(sb_config.settings.NEW_DRIVER_DIR)
3019
3086
  ):
3020
3087
  driver_dir = sb_config.settings.NEW_DRIVER_DIR
@@ -3026,6 +3093,23 @@ def get_driver(
3026
3093
  if browser_name in constants.ChromiumSubs.chromium_subs:
3027
3094
  browser_name = "chrome"
3028
3095
  browser_name = browser_name.lower()
3096
+ if is_using_uc(undetectable, browser_name):
3097
+ if ad_block_on:
3098
+ sb_config.ad_block_on = True
3099
+ else:
3100
+ sb_config.ad_block_on = False
3101
+ if disable_csp:
3102
+ sb_config.disable_csp = True
3103
+ else:
3104
+ sb_config.disable_csp = False
3105
+ if mobile_emulator:
3106
+ # For stealthy mobile mode, see the CDP Mode examples
3107
+ # to learn how to properly configure it.
3108
+ user_agent = None # Undo the override
3109
+ mobile_emulator = False # Instead, set from CDP Mode
3110
+ sb_config._cdp_mobile_mode = True
3111
+ else:
3112
+ sb_config._cdp_mobile_mode = False
3029
3113
  if headless2 and browser_name == constants.Browser.FIREFOX:
3030
3114
  headless2 = False # Only for Chromium
3031
3115
  headless = True
@@ -3046,6 +3130,51 @@ def get_driver(
3046
3130
  or browser_name == constants.Browser.EDGE
3047
3131
  )
3048
3132
  ):
3133
+ if (
3134
+ binary_location.lower() == "_chromium_"
3135
+ and browser_name == constants.Browser.GOOGLE_CHROME
3136
+ ):
3137
+ binary_folder = None
3138
+ if IS_MAC:
3139
+ binary_folder = "chrome-mac"
3140
+ elif IS_LINUX:
3141
+ binary_folder = "chrome-linux"
3142
+ elif IS_WINDOWS:
3143
+ binary_folder = "chrome-win"
3144
+ if binary_folder:
3145
+ binary_location = os.path.join(driver_dir, binary_folder)
3146
+ if not os.path.exists(binary_location):
3147
+ from seleniumbase.console_scripts import sb_install
3148
+ args = " ".join(sys.argv)
3149
+ if not (
3150
+ "-n" in sys.argv or " -n=" in args or args == "-c"
3151
+ ):
3152
+ # (Not multithreaded)
3153
+ sys_args = sys.argv # Save a copy of current sys args
3154
+ log_d("\nWarning: Chromium binary not found...")
3155
+ try:
3156
+ sb_install.main(override="chromium")
3157
+ except Exception as e:
3158
+ log_d("\nWarning: Chrome download failed: %s" % e)
3159
+ sys.argv = sys_args # Put back the original sys args
3160
+ else:
3161
+ chrome_fixing_lock = fasteners.InterProcessLock(
3162
+ constants.MultiBrowser.DRIVER_FIXING_LOCK
3163
+ )
3164
+ with chrome_fixing_lock:
3165
+ with suppress(Exception):
3166
+ shared_utils.make_writable(
3167
+ constants.MultiBrowser.DRIVER_FIXING_LOCK
3168
+ )
3169
+ if not os.path.exists(binary_location):
3170
+ sys_args = sys.argv # Save a copy of sys args
3171
+ log_d(
3172
+ "\nWarning: Chromium binary not found..."
3173
+ )
3174
+ sb_install.main(override="chromium")
3175
+ sys.argv = sys_args # Put back original args
3176
+ else:
3177
+ binary_location = None
3049
3178
  if (
3050
3179
  binary_location.lower() == "cft"
3051
3180
  and browser_name == constants.Browser.GOOGLE_CHROME
@@ -3183,12 +3312,25 @@ def get_driver(
3183
3312
  binary_name = "Google Chrome for Testing"
3184
3313
  binary_location += "/Google Chrome for Testing.app"
3185
3314
  binary_location += "/Contents/MacOS/Google Chrome for Testing"
3315
+ elif binary_name == "Chromium.app":
3316
+ binary_name = "Chromium"
3317
+ binary_location += "/Contents/MacOS/Chromium"
3318
+ elif binary_name in ["chrome-mac"]:
3319
+ binary_name = "Chromium"
3320
+ binary_location += "/Chromium.app"
3321
+ binary_location += "/Contents/MacOS/Chromium"
3186
3322
  elif binary_name == "chrome-linux64":
3187
3323
  binary_name = "chrome"
3188
3324
  binary_location += "/chrome"
3325
+ elif binary_name == "chrome-linux":
3326
+ binary_name = "chrome"
3327
+ binary_location += "/chrome"
3189
3328
  elif binary_name in ["chrome-win32", "chrome-win64"]:
3190
3329
  binary_name = "chrome.exe"
3191
3330
  binary_location += "\\chrome.exe"
3331
+ elif binary_name in ["chrome-win"]:
3332
+ binary_name = "chrome.exe"
3333
+ binary_location += "\\chrome.exe"
3192
3334
  elif binary_name in [
3193
3335
  "chrome-headless-shell-mac-arm64",
3194
3336
  "chrome-headless-shell-mac-x64",
@@ -3226,8 +3368,8 @@ def get_driver(
3226
3368
  proxy_pass = None
3227
3369
  proxy_scheme = "http"
3228
3370
  if proxy_string:
3229
- # (The code below was for the Chrome 137 extension fix)
3230
- # sb_config._cdp_proxy = proxy_string
3371
+ # (The line below is for the Chrome 142 proxy auth fix)
3372
+ sb_config._cdp_proxy = proxy_string
3231
3373
  username_and_password = None
3232
3374
  if "@" in proxy_string:
3233
3375
  # Format => username:password@hostname:port
@@ -3960,34 +4102,34 @@ def get_local_driver(
3960
4102
  downloads_path = DOWNLOADS_FOLDER
3961
4103
  driver_dir = DRIVER_DIR
3962
4104
  special_chrome = False
3963
- if (
3964
- hasattr(sb_config, "binary_location")
3965
- and sb_config.binary_location == "cft"
3966
- ):
3967
- special_chrome = True
3968
- driver_dir = DRIVER_DIR_CFT
3969
- if (
3970
- hasattr(sb_config, "binary_location")
3971
- and sb_config.binary_location == "chs"
3972
- ):
3973
- special_chrome = True
3974
- driver_dir = DRIVER_DIR_CHS
3975
- if _special_binary_exists(binary_location, "opera"):
3976
- special_chrome = True
3977
- driver_dir = DRIVER_DIR_OPERA
3978
- if _special_binary_exists(binary_location, "brave"):
3979
- special_chrome = True
3980
- driver_dir = DRIVER_DIR_BRAVE
3981
- if _special_binary_exists(binary_location, "comet"):
3982
- special_chrome = True
3983
- driver_dir = DRIVER_DIR_COMET
3984
- if _special_binary_exists(binary_location, "atlas"):
3985
- special_chrome = True
3986
- driver_dir = DRIVER_DIR_ATLAS
4105
+ if binary_location:
4106
+ if (
4107
+ binary_location == "_chromium_"
4108
+ or "chromium_drivers" in binary_location
4109
+ ):
4110
+ special_chrome = True
4111
+ driver_dir = DRIVER_DIR_CHROMIUM
4112
+ elif binary_location == "cft" or "cft_drivers" in binary_location:
4113
+ special_chrome = True
4114
+ driver_dir = DRIVER_DIR_CFT
4115
+ elif binary_location == "chs" or "chs_drivers" in binary_location:
4116
+ special_chrome = True
4117
+ driver_dir = DRIVER_DIR_CHS
4118
+ elif _special_binary_exists(binary_location, "opera"):
4119
+ special_chrome = True
4120
+ driver_dir = DRIVER_DIR_OPERA
4121
+ elif _special_binary_exists(binary_location, "brave"):
4122
+ special_chrome = True
4123
+ driver_dir = DRIVER_DIR_BRAVE
4124
+ elif _special_binary_exists(binary_location, "comet"):
4125
+ special_chrome = True
4126
+ driver_dir = DRIVER_DIR_COMET
4127
+ elif _special_binary_exists(binary_location, "atlas"):
4128
+ special_chrome = True
4129
+ driver_dir = DRIVER_DIR_ATLAS
3987
4130
  if (
3988
4131
  hasattr(sb_config, "settings")
3989
- and hasattr(sb_config.settings, "NEW_DRIVER_DIR")
3990
- and sb_config.settings.NEW_DRIVER_DIR
4132
+ and getattr(sb_config.settings, "NEW_DRIVER_DIR", None)
3991
4133
  and os.path.exists(sb_config.settings.NEW_DRIVER_DIR)
3992
4134
  ):
3993
4135
  driver_dir = sb_config.settings.NEW_DRIVER_DIR
@@ -4552,12 +4694,12 @@ def get_local_driver(
4552
4694
  # Can be a comma-separated list of .ZIP or .CRX files
4553
4695
  extension_zip_list = extension_zip.split(",")
4554
4696
  for extension_zip_item in extension_zip_list:
4555
- abs_path = os.path.abspath(extension_zip_item)
4697
+ abs_path = os.path.realpath(extension_zip_item)
4556
4698
  edge_options.add_extension(abs_path)
4557
4699
  if extension_dir:
4558
4700
  # load-extension input can be a comma-separated list
4559
4701
  abs_path = (
4560
- ",".join(os.path.abspath(p) for p in extension_dir.split(","))
4702
+ ",".join(os.path.realpath(p) for p in extension_dir.split(","))
4561
4703
  )
4562
4704
  edge_options = add_chrome_ext_dir(edge_options, abs_path)
4563
4705
  edge_options.add_argument("--disable-infobars")
@@ -4597,8 +4739,7 @@ def get_local_driver(
4597
4739
  edge_options.page_load_strategy = page_load_strategy.lower()
4598
4740
  elif (
4599
4741
  not page_load_strategy
4600
- and hasattr(settings, "PAGE_LOAD_STRATEGY")
4601
- and settings.PAGE_LOAD_STRATEGY
4742
+ and getattr(settings, "PAGE_LOAD_STRATEGY", None)
4602
4743
  and settings.PAGE_LOAD_STRATEGY.lower() in ["eager", "none"]
4603
4744
  ):
4604
4745
  # Only change it if not "normal", which is the default.
@@ -4709,6 +4850,7 @@ def get_local_driver(
4709
4850
  included_disabled_features.append("OptimizationHints")
4710
4851
  included_disabled_features.append("OptimizationHintsFetching")
4711
4852
  included_disabled_features.append("Translate")
4853
+ included_disabled_features.append("ComponentUpdater")
4712
4854
  included_disabled_features.append("OptimizationTargetPrediction")
4713
4855
  included_disabled_features.append("OptimizationGuideModelDownloading")
4714
4856
  included_disabled_features.append("InsecureDownloadWarnings")
@@ -4719,6 +4861,11 @@ def get_local_driver(
4719
4861
  included_disabled_features.append(
4720
4862
  "DisableLoadExtensionCommandLineSwitch"
4721
4863
  )
4864
+ included_disabled_features.append("Bluetooth")
4865
+ included_disabled_features.append("WebBluetooth")
4866
+ included_disabled_features.append("UnifiedWebBluetooth")
4867
+ included_disabled_features.append("WebAuthentication")
4868
+ included_disabled_features.append("PasskeyAuth")
4722
4869
  for item in extra_disabled_features:
4723
4870
  if item not in included_disabled_features:
4724
4871
  included_disabled_features.append(item)
@@ -4810,8 +4957,7 @@ def get_local_driver(
4810
4957
  options.page_load_strategy = page_load_strategy.lower()
4811
4958
  elif (
4812
4959
  not page_load_strategy
4813
- and hasattr(settings, "PAGE_LOAD_STRATEGY")
4814
- and settings.PAGE_LOAD_STRATEGY
4960
+ and getattr(settings, "PAGE_LOAD_STRATEGY", None)
4815
4961
  and settings.PAGE_LOAD_STRATEGY.lower() in ["eager", "none"]
4816
4962
  ):
4817
4963
  # Only change it if not "normal", which is the default.
@@ -4899,6 +5045,8 @@ def get_local_driver(
4899
5045
  device_height,
4900
5046
  device_pixel_ratio,
4901
5047
  )
5048
+ if binary_location and "chromium_drivers" in binary_location:
5049
+ chrome_options.add_argument("--use-mock-keychain")
4902
5050
  use_version = "latest"
4903
5051
  major_chrome_version = None
4904
5052
  saved_mcv = None
@@ -5748,6 +5896,7 @@ def get_local_driver(
5748
5896
  )
5749
5897
  driver.default_get = driver.get # Save copy of original
5750
5898
  driver.cdp = None # Set a placeholder
5899
+ driver._is_using_uc = False
5751
5900
  driver._is_using_cdp = False
5752
5901
  driver._is_connected = True
5753
5902
  if uc_activated:
@@ -5782,6 +5931,7 @@ def get_local_driver(
5782
5931
  driver, *args, **kwargs
5783
5932
  )
5784
5933
  )
5934
+ driver.activate_cdp_mode = driver.uc_activate_cdp_mode
5785
5935
  driver.uc_open_with_cdp_mode = (
5786
5936
  lambda *args, **kwargs: uc_open_with_cdp_mode(
5787
5937
  driver, *args, **kwargs
@@ -5861,8 +6011,18 @@ def get_local_driver(
5861
6011
  time.sleep(0.003)
5862
6012
  driver.switch_to.window(driver.window_handles[0])
5863
6013
  time.sleep(0.003)
5864
- driver.connect()
5865
- time.sleep(0.003)
6014
+ # seleniumbase/SeleniumBase/discussions/4190
6015
+ if getattr(sb_config, "skip_133_patch", None):
6016
+ # To skip the connect() patch for Chrome 133+:
6017
+ # from seleniumbase import config as sb_config
6018
+ # sb_config.skip_133_patch = True
6019
+ # (Do the above before launching the browser.)
6020
+ pass
6021
+ else:
6022
+ # This fixes an issue on Chrome 133+
6023
+ # (Some people might not need it though.)
6024
+ driver.connect()
6025
+ time.sleep(0.003)
5866
6026
  if mobile_emulator:
5867
6027
  uc_metrics = {}
5868
6028
  if (
@@ -5890,6 +6050,8 @@ def get_local_driver(
5890
6050
  'Emulation.setDeviceMetricsOverride',
5891
6051
  set_device_metrics_override
5892
6052
  )
6053
+ else:
6054
+ driver.get = lambda url: updated_get(driver, url)
5893
6055
  return extend_driver(
5894
6056
  driver, proxy_auth, use_uc, recorder_ext
5895
6057
  )