seleniumbase 4.41.3__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 (64) hide show
  1. sbase/steps.py +9 -0
  2. seleniumbase/__version__.py +1 -1
  3. seleniumbase/behave/behave_helper.py +2 -0
  4. seleniumbase/behave/behave_sb.py +21 -8
  5. seleniumbase/common/decorators.py +3 -1
  6. seleniumbase/console_scripts/run.py +1 -0
  7. seleniumbase/console_scripts/sb_caseplans.py +3 -4
  8. seleniumbase/console_scripts/sb_install.py +142 -11
  9. seleniumbase/console_scripts/sb_mkchart.py +1 -2
  10. seleniumbase/console_scripts/sb_mkdir.py +99 -29
  11. seleniumbase/console_scripts/sb_mkfile.py +1 -2
  12. seleniumbase/console_scripts/sb_mkpres.py +1 -2
  13. seleniumbase/console_scripts/sb_mkrec.py +26 -2
  14. seleniumbase/console_scripts/sb_objectify.py +4 -5
  15. seleniumbase/console_scripts/sb_print.py +1 -1
  16. seleniumbase/console_scripts/sb_recorder.py +40 -3
  17. seleniumbase/core/browser_launcher.py +474 -151
  18. seleniumbase/core/detect_b_ver.py +258 -16
  19. seleniumbase/core/log_helper.py +15 -21
  20. seleniumbase/core/mysql.py +1 -1
  21. seleniumbase/core/recorder_helper.py +3 -0
  22. seleniumbase/core/report_helper.py +9 -12
  23. seleniumbase/core/sb_cdp.py +734 -215
  24. seleniumbase/core/sb_driver.py +46 -5
  25. seleniumbase/core/session_helper.py +2 -4
  26. seleniumbase/core/tour_helper.py +1 -2
  27. seleniumbase/drivers/atlas_drivers/__init__.py +0 -0
  28. seleniumbase/drivers/brave_drivers/__init__.py +0 -0
  29. seleniumbase/drivers/chromium_drivers/__init__.py +0 -0
  30. seleniumbase/drivers/comet_drivers/__init__.py +0 -0
  31. seleniumbase/drivers/opera_drivers/__init__.py +0 -0
  32. seleniumbase/fixtures/base_case.py +448 -251
  33. seleniumbase/fixtures/constants.py +36 -9
  34. seleniumbase/fixtures/js_utils.py +77 -18
  35. seleniumbase/fixtures/page_actions.py +41 -13
  36. seleniumbase/fixtures/page_utils.py +19 -12
  37. seleniumbase/fixtures/shared_utils.py +64 -6
  38. seleniumbase/masterqa/master_qa.py +16 -2
  39. seleniumbase/plugins/base_plugin.py +8 -0
  40. seleniumbase/plugins/basic_test_info.py +2 -3
  41. seleniumbase/plugins/driver_manager.py +131 -5
  42. seleniumbase/plugins/page_source.py +2 -3
  43. seleniumbase/plugins/pytest_plugin.py +244 -79
  44. seleniumbase/plugins/sb_manager.py +143 -20
  45. seleniumbase/plugins/selenium_plugin.py +144 -12
  46. seleniumbase/translate/translator.py +2 -3
  47. seleniumbase/undetected/__init__.py +17 -13
  48. seleniumbase/undetected/cdp.py +1 -12
  49. seleniumbase/undetected/cdp_driver/browser.py +330 -129
  50. seleniumbase/undetected/cdp_driver/cdp_util.py +328 -61
  51. seleniumbase/undetected/cdp_driver/config.py +110 -14
  52. seleniumbase/undetected/cdp_driver/connection.py +18 -48
  53. seleniumbase/undetected/cdp_driver/element.py +105 -33
  54. seleniumbase/undetected/cdp_driver/tab.py +414 -39
  55. seleniumbase/utilities/selenium_grid/download_selenium_server.py +1 -1
  56. seleniumbase/utilities/selenium_grid/grid_hub.py +1 -2
  57. seleniumbase/utilities/selenium_grid/grid_node.py +2 -3
  58. seleniumbase/utilities/selenium_ide/convert_ide.py +2 -3
  59. {seleniumbase-4.41.3.dist-info → seleniumbase-4.45.10.dist-info}/METADATA +193 -166
  60. {seleniumbase-4.41.3.dist-info → seleniumbase-4.45.10.dist-info}/RECORD +64 -59
  61. {seleniumbase-4.41.3.dist-info → seleniumbase-4.45.10.dist-info}/licenses/LICENSE +1 -1
  62. {seleniumbase-4.41.3.dist-info → seleniumbase-4.45.10.dist-info}/WHEEL +0 -0
  63. {seleniumbase-4.41.3.dist-info → seleniumbase-4.45.10.dist-info}/entry_points.txt +0 -0
  64. {seleniumbase-4.41.3.dist-info → seleniumbase-4.45.10.dist-info}/top_level.txt +0 -0
@@ -12,6 +12,7 @@ import types
12
12
  import urllib3
13
13
  import warnings
14
14
  from contextlib import suppress
15
+ from filelock import FileLock
15
16
  from selenium import webdriver
16
17
  from selenium.common.exceptions import ElementClickInterceptedException
17
18
  from selenium.common.exceptions import InvalidSessionIdException
@@ -27,6 +28,11 @@ from seleniumbase import decorators
27
28
  from seleniumbase import drivers # webdriver storage folder for SeleniumBase
28
29
  from seleniumbase.drivers import cft_drivers # chrome-for-testing
29
30
  from seleniumbase.drivers import chs_drivers # chrome-headless-shell
31
+ from seleniumbase.drivers import opera_drivers # still uses chromedriver
32
+ from seleniumbase.drivers import brave_drivers # still uses chromedriver
33
+ from seleniumbase.drivers import comet_drivers # still uses chromedriver
34
+ from seleniumbase.drivers import atlas_drivers # still uses chromedriver
35
+ from seleniumbase.drivers import chromium_drivers # still uses chromedriver
30
36
  from seleniumbase import extensions # browser extensions storage folder
31
37
  from seleniumbase.config import settings
32
38
  from seleniumbase.core import detect_b_ver
@@ -43,6 +49,13 @@ urllib3.disable_warnings()
43
49
  DRIVER_DIR = os.path.dirname(os.path.realpath(drivers.__file__))
44
50
  DRIVER_DIR_CFT = os.path.dirname(os.path.realpath(cft_drivers.__file__))
45
51
  DRIVER_DIR_CHS = os.path.dirname(os.path.realpath(chs_drivers.__file__))
52
+ DRIVER_DIR_OPERA = os.path.dirname(os.path.realpath(opera_drivers.__file__))
53
+ DRIVER_DIR_BRAVE = os.path.dirname(os.path.realpath(brave_drivers.__file__))
54
+ DRIVER_DIR_COMET = os.path.dirname(os.path.realpath(comet_drivers.__file__))
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
+ )
46
59
  # Make sure that the SeleniumBase DRIVER_DIR is at the top of the System PATH
47
60
  # (Changes to the System PATH with os.environ only last during the test run)
48
61
  if not os.environ["PATH"].startswith(DRIVER_DIR):
@@ -95,10 +108,7 @@ else:
95
108
  def log_d(message):
96
109
  """If setting sb_config.settings.HIDE_DRIVER_DOWNLOADS to True,
97
110
  output from driver downloads are logged instead of printed."""
98
- if (
99
- hasattr(settings, "HIDE_DRIVER_DOWNLOADS")
100
- and settings.HIDE_DRIVER_DOWNLOADS
101
- ):
111
+ if getattr(settings, "HIDE_DRIVER_DOWNLOADS", None):
102
112
  logging.debug(message)
103
113
  else:
104
114
  print(message)
@@ -150,9 +160,21 @@ def extend_driver(
150
160
  # Extend the driver with new methods
151
161
  driver.default_find_element = driver.find_element
152
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
153
169
  DM = sb_driver.DriverMethods(driver)
154
170
  driver.find_element = DM.find_element
155
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
156
178
  driver.locator = DM.locator
157
179
  page = types.SimpleNamespace()
158
180
  page.open = DM.open_url
@@ -227,6 +249,8 @@ def extend_driver(
227
249
  driver.wait_for_element = DM.wait_for_element
228
250
  driver.wait_for_element_visible = DM.wait_for_element_visible
229
251
  driver.wait_for_element_present = DM.wait_for_element_present
252
+ driver.wait_for_element_absent = DM.wait_for_element_absent
253
+ driver.wait_for_element_not_visible = DM.wait_for_element_not_visible
230
254
  driver.wait_for_selector = DM.wait_for_selector
231
255
  driver.wait_for_text = DM.wait_for_text
232
256
  driver.wait_for_exact_text = DM.wait_for_exact_text
@@ -282,7 +306,19 @@ def extend_driver(
282
306
  )
283
307
  if hasattr(driver, "proxy"):
284
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
285
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)
286
322
  # Proxy needs a moment to load in Manifest V3
287
323
  if use_uc:
288
324
  time.sleep(0.14)
@@ -408,16 +444,16 @@ def has_captcha(text):
408
444
  "<title>403 Forbidden</title>" in text
409
445
  or "Permission Denied</title>" in text
410
446
  or 'id="challenge-error-text"' in text
447
+ or "/challenge-platform/h/b/" in text
411
448
  or "<title>Just a moment..." in text
412
449
  or 'action="/?__cf_chl_f_tk' in text
413
450
  or 'id="challenge-widget-' in text
414
451
  or 'src="chromedriver.js"' in text
452
+ or "com/recaptcha/api.js" in text
415
453
  or 'class="g-recaptcha"' in text
416
454
  or 'content="Pixelscan"' in text
417
455
  or 'id="challenge-form"' in text
418
- or "/challenge-platform" in text
419
456
  or "window._cf_chl_opt" in text
420
- or "/recaptcha/api.js" in text
421
457
  or "/turnstile/" in text
422
458
  ):
423
459
  return True
@@ -429,6 +465,18 @@ def __is_cdp_swap_needed(driver):
429
465
  return shared_utils.is_cdp_swap_needed(driver)
430
466
 
431
467
 
468
+ def uc_execute_cdp_cmd(driver, *args, **kwargs):
469
+ if not driver.is_connected():
470
+ driver.connect()
471
+ return driver.default_execute_cdp_cmd(*args, **kwargs)
472
+
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
+
432
480
  def uc_special_open_if_cf(
433
481
  driver,
434
482
  url,
@@ -438,6 +486,8 @@ def uc_special_open_if_cf(
438
486
  device_height=None,
439
487
  device_pixel_ratio=None,
440
488
  ):
489
+ if url and ":" not in url and "." in url:
490
+ url = "https:" + url
441
491
  if url.startswith("http:") or url.startswith("https:"):
442
492
  special = False
443
493
  with suppress(Exception):
@@ -534,6 +584,19 @@ def uc_open_with_tab(driver, url):
534
584
 
535
585
  def uc_open_with_reconnect(driver, url, reconnect_time=None):
536
586
  """Open a url, disconnect chromedriver, wait, and reconnect."""
587
+ if (
588
+ hasattr(sb_config, "_cdp_browser")
589
+ and sb_config._cdp_browser in ["comet", "opera", "atlas"]
590
+ ):
591
+ if not __is_cdp_swap_needed(driver):
592
+ if not driver.current_url.startswith(
593
+ ("about", "data", "chrome")
594
+ ):
595
+ driver.get("about:blank")
596
+ uc_activate_cdp_mode(driver, url)
597
+ else:
598
+ driver.cdp.open(url)
599
+ return
537
600
  url = shared_utils.fix_url_as_needed(url)
538
601
  if __is_cdp_swap_needed(driver):
539
602
  driver.cdp.get(url)
@@ -594,10 +657,8 @@ def uc_open_with_cdp_mode(driver, url=None, **kwargs):
594
657
  safe_url = False
595
658
 
596
659
  if (
597
- hasattr(driver, "_is_using_cdp")
598
- and driver._is_using_cdp
599
- and hasattr(driver, "cdp")
600
- and driver.cdp
660
+ getattr(driver, "_is_using_cdp", None)
661
+ and getattr(driver, "cdp", None)
601
662
  and hasattr(driver.cdp, "loop")
602
663
  ):
603
664
  # CDP Mode was already initialized
@@ -637,11 +698,12 @@ def uc_open_with_cdp_mode(driver, url=None, **kwargs):
637
698
  xvfb=xvfb,
638
699
  xvfb_metrics=xvfb_metrics,
639
700
  browser_executable_path=binary_location,
701
+ mobile=getattr(sb_config, "_cdp_mobile_mode", None),
640
702
  )
641
703
  )
642
704
  loop.run_until_complete(driver.cdp_base.wait(0))
643
705
 
644
- gui_lock = fasteners.InterProcessLock(constants.MultiBrowser.PYAUTOGUILOCK)
706
+ gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK)
645
707
 
646
708
  if (
647
709
  "chrome-extension://" in str(driver.cdp_base.main_tab)
@@ -700,6 +762,11 @@ def uc_open_with_cdp_mode(driver, url=None, **kwargs):
700
762
  cdp.refresh = CDPM.refresh
701
763
  cdp.add_handler = CDPM.add_handler
702
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
703
770
  cdp.find_element = CDPM.find_element
704
771
  cdp.find = CDPM.find_element
705
772
  cdp.locator = CDPM.find_element
@@ -734,6 +801,7 @@ def uc_open_with_cdp_mode(driver, url=None, **kwargs):
734
801
  cdp.click_active_element = CDPM.click_active_element
735
802
  cdp.click_if_visible = CDPM.click_if_visible
736
803
  cdp.click_visible_elements = CDPM.click_visible_elements
804
+ cdp.click_with_offset = CDPM.click_with_offset
737
805
  cdp.mouse_click = CDPM.mouse_click
738
806
  cdp.get_parent = CDPM.get_parent
739
807
  cdp.remove_element = CDPM.remove_element
@@ -747,21 +815,30 @@ def uc_open_with_cdp_mode(driver, url=None, **kwargs):
747
815
  cdp.set_value = CDPM.set_value
748
816
  cdp.submit = CDPM.submit
749
817
  cdp.evaluate = CDPM.evaluate
818
+ cdp.execute_script = CDPM.execute_script
750
819
  cdp.js_dumps = CDPM.js_dumps
751
820
  cdp.maximize = CDPM.maximize
752
821
  cdp.minimize = CDPM.minimize
753
822
  cdp.medimize = CDPM.medimize
754
823
  cdp.set_window_rect = CDPM.set_window_rect
755
824
  cdp.reset_window_size = CDPM.reset_window_size
825
+ cdp.activate_messenger = CDPM.activate_messenger
826
+ cdp.set_messenger_theme = CDPM.set_messenger_theme
827
+ cdp.post_message = CDPM.post_message
756
828
  cdp.set_locale = CDPM.set_locale
757
829
  cdp.set_local_storage_item = CDPM.set_local_storage_item
758
830
  cdp.set_session_storage_item = CDPM.set_session_storage_item
759
831
  cdp.set_attributes = CDPM.set_attributes
832
+ cdp.is_attribute_present = CDPM.is_attribute_present
833
+ cdp.is_online = CDPM.is_online
834
+ cdp.solve_captcha = CDPM.solve_captcha
835
+ cdp.click_captcha = CDPM.click_captcha
760
836
  cdp.gui_press_key = CDPM.gui_press_key
761
837
  cdp.gui_press_keys = CDPM.gui_press_keys
762
838
  cdp.gui_write = CDPM.gui_write
763
839
  cdp.gui_click_x_y = CDPM.gui_click_x_y
764
840
  cdp.gui_click_element = CDPM.gui_click_element
841
+ cdp.gui_click_with_offset = CDPM.gui_click_with_offset
765
842
  cdp.gui_click_captcha = CDPM.gui_click_captcha
766
843
  cdp.gui_drag_drop_points = CDPM.gui_drag_drop_points
767
844
  cdp.gui_drag_and_drop = CDPM.gui_drag_and_drop
@@ -769,6 +846,8 @@ def uc_open_with_cdp_mode(driver, url=None, **kwargs):
769
846
  cdp.gui_hover_x_y = CDPM.gui_hover_x_y
770
847
  cdp.gui_hover_element = CDPM.gui_hover_element
771
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
772
851
  cdp.internalize_links = CDPM.internalize_links
773
852
  cdp.open_new_window = CDPM.open_new_window
774
853
  cdp.switch_to_window = CDPM.switch_to_window
@@ -789,6 +868,7 @@ def uc_open_with_cdp_mode(driver, url=None, **kwargs):
789
868
  cdp.get_element_position = CDPM.get_element_position
790
869
  cdp.get_gui_element_rect = CDPM.get_gui_element_rect
791
870
  cdp.get_gui_element_center = CDPM.get_gui_element_center
871
+ cdp.get_html = CDPM.get_html
792
872
  cdp.get_page_source = CDPM.get_page_source
793
873
  cdp.get_user_agent = CDPM.get_user_agent
794
874
  cdp.get_cookie_string = CDPM.get_cookie_string
@@ -806,8 +886,12 @@ def uc_open_with_cdp_mode(driver, url=None, **kwargs):
806
886
  cdp.get_screen_rect = CDPM.get_screen_rect
807
887
  cdp.get_window_rect = CDPM.get_window_rect
808
888
  cdp.get_window_size = CDPM.get_window_size
889
+ cdp.get_mfa_code = CDPM.get_mfa_code
809
890
  cdp.nested_click = CDPM.nested_click
810
891
  cdp.select_option_by_text = CDPM.select_option_by_text
892
+ cdp.select_option_by_index = CDPM.select_option_by_index
893
+ cdp.select_option_by_value = CDPM.select_option_by_value
894
+ cdp.enter_mfa_code = CDPM.enter_mfa_code
811
895
  cdp.flash = CDPM.flash
812
896
  cdp.highlight = CDPM.highlight
813
897
  cdp.focus = CDPM.focus
@@ -826,6 +910,7 @@ def uc_open_with_cdp_mode(driver, url=None, **kwargs):
826
910
  cdp.wait_for_text = CDPM.wait_for_text
827
911
  cdp.wait_for_text_not_visible = CDPM.wait_for_text_not_visible
828
912
  cdp.wait_for_element_visible = CDPM.wait_for_element_visible
913
+ cdp.wait_for_element = CDPM.wait_for_element
829
914
  cdp.wait_for_element_not_visible = CDPM.wait_for_element_not_visible
830
915
  cdp.wait_for_element_absent = CDPM.wait_for_element_absent
831
916
  cdp.wait_for_any_of_elements_visible = (
@@ -857,12 +942,16 @@ def uc_open_with_cdp_mode(driver, url=None, **kwargs):
857
942
  cdp.assert_not_in = CDPM.assert_not_in
858
943
  cdp.scroll_into_view = CDPM.scroll_into_view
859
944
  cdp.scroll_to_y = CDPM.scroll_to_y
945
+ cdp.scroll_by_y = CDPM.scroll_by_y
860
946
  cdp.scroll_to_top = CDPM.scroll_to_top
861
947
  cdp.scroll_to_bottom = CDPM.scroll_to_bottom
862
948
  cdp.scroll_up = CDPM.scroll_up
863
949
  cdp.scroll_down = CDPM.scroll_down
950
+ cdp.save_page_source = CDPM.save_page_source
951
+ cdp.save_as_html = CDPM.save_as_html
864
952
  cdp.save_screenshot = CDPM.save_screenshot
865
953
  cdp.print_to_pdf = CDPM.print_to_pdf
954
+ cdp.save_as_pdf = CDPM.save_as_pdf
866
955
  cdp.page = page # async world
867
956
  cdp.driver = driver.cdp_base # async world
868
957
  cdp.tab = cdp.page # shortcut (original)
@@ -876,7 +965,17 @@ def uc_open_with_cdp_mode(driver, url=None, **kwargs):
876
965
  cdp.core = core_items
877
966
  cdp.loop = cdp.get_event_loop()
878
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
879
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)
880
979
 
881
980
 
882
981
  def uc_activate_cdp_mode(driver, url=None, **kwargs):
@@ -951,7 +1050,7 @@ def uc_click(
951
1050
  def verify_pyautogui_has_a_headed_browser(driver):
952
1051
  """PyAutoGUI requires a headed browser so that it can
953
1052
  focus on the correct element when performing actions."""
954
- if hasattr(driver, "_is_hidden") and driver._is_hidden:
1053
+ if getattr(driver, "_is_hidden", None):
955
1054
  raise Exception(
956
1055
  "PyAutoGUI can't be used in headless mode!"
957
1056
  )
@@ -962,17 +1061,15 @@ def __install_pyautogui_if_missing():
962
1061
  import pyautogui
963
1062
  with suppress(Exception):
964
1063
  use_pyautogui_ver = constants.PyAutoGUI.VER
965
- if pyautogui.__version__ != use_pyautogui_ver:
966
- del pyautogui
967
- shared_utils.pip_install(
968
- "pyautogui", version=use_pyautogui_ver
969
- )
1064
+ u_pv = shared_utils.make_version_tuple(use_pyautogui_ver)
1065
+ pv = shared_utils.make_version_tuple(pyautogui.__version__)
1066
+ if pv < u_pv:
1067
+ del pyautogui # To get newer ver
1068
+ shared_utils.pip_install("pyautogui", version="Latest")
970
1069
  import pyautogui
971
1070
  except Exception:
972
1071
  print("\nPyAutoGUI required! Installing now...")
973
- shared_utils.pip_install(
974
- "pyautogui", version=constants.PyAutoGUI.VER
975
- )
1072
+ shared_utils.pip_install("pyautogui", version="Latest")
976
1073
  try:
977
1074
  import pyautogui
978
1075
  except Exception:
@@ -989,11 +1086,9 @@ def __install_pyautogui_if_missing():
989
1086
  xvfb_width = 1366
990
1087
  xvfb_height = 768
991
1088
  if (
992
- hasattr(sb_config, "_xvfb_width")
993
- and sb_config._xvfb_width
1089
+ getattr(sb_config, "_xvfb_width", None)
994
1090
  and isinstance(sb_config._xvfb_width, int)
995
- and hasattr(sb_config, "_xvfb_height")
996
- and sb_config._xvfb_height
1091
+ and getattr(sb_config, "_xvfb_height", None)
997
1092
  and isinstance(sb_config._xvfb_height, int)
998
1093
  ):
999
1094
  xvfb_width = sb_config._xvfb_width
@@ -1020,8 +1115,7 @@ def __install_pyautogui_if_missing():
1020
1115
  sb_config._virtual_display = _xvfb_display
1021
1116
  sb_config.headless_active = True
1022
1117
  if (
1023
- hasattr(sb_config, "reuse_session")
1024
- and sb_config.reuse_session
1118
+ getattr(sb_config, "reuse_session", None)
1025
1119
  and hasattr(sb_config, "_vd_list")
1026
1120
  and isinstance(sb_config._vd_list, list)
1027
1121
  ):
@@ -1053,8 +1147,7 @@ def get_configured_pyautogui(pyautogui_copy):
1053
1147
  and "DISPLAY" in os.environ.keys()
1054
1148
  ):
1055
1149
  if (
1056
- hasattr(sb_config, "_pyautogui_x11_display")
1057
- and sb_config._pyautogui_x11_display
1150
+ getattr(sb_config, "_pyautogui_x11_display", None)
1058
1151
  and hasattr(pyautogui_copy._pyautogui_x11, "_display")
1059
1152
  and (
1060
1153
  sb_config._pyautogui_x11_display
@@ -1077,9 +1170,7 @@ def uc_gui_press_key(driver, key):
1077
1170
  install_pyautogui_if_missing(driver)
1078
1171
  import pyautogui
1079
1172
  pyautogui = get_configured_pyautogui(pyautogui)
1080
- gui_lock = fasteners.InterProcessLock(
1081
- constants.MultiBrowser.PYAUTOGUILOCK
1082
- )
1173
+ gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK)
1083
1174
  with gui_lock:
1084
1175
  pyautogui.press(key)
1085
1176
 
@@ -1088,9 +1179,7 @@ def uc_gui_press_keys(driver, keys):
1088
1179
  install_pyautogui_if_missing(driver)
1089
1180
  import pyautogui
1090
1181
  pyautogui = get_configured_pyautogui(pyautogui)
1091
- gui_lock = fasteners.InterProcessLock(
1092
- constants.MultiBrowser.PYAUTOGUILOCK
1093
- )
1182
+ gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK)
1094
1183
  with gui_lock:
1095
1184
  for key in keys:
1096
1185
  pyautogui.press(key)
@@ -1100,9 +1189,7 @@ def uc_gui_write(driver, text):
1100
1189
  install_pyautogui_if_missing(driver)
1101
1190
  import pyautogui
1102
1191
  pyautogui = get_configured_pyautogui(pyautogui)
1103
- gui_lock = fasteners.InterProcessLock(
1104
- constants.MultiBrowser.PYAUTOGUILOCK
1105
- )
1192
+ gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK)
1106
1193
  with gui_lock:
1107
1194
  pyautogui.write(text)
1108
1195
 
@@ -1135,9 +1222,7 @@ def _uc_gui_click_x_y(driver, x, y, timeframe=0.25, uc_lock=False):
1135
1222
  % (x, y, screen_width, screen_height)
1136
1223
  )
1137
1224
  if uc_lock:
1138
- gui_lock = fasteners.InterProcessLock(
1139
- constants.MultiBrowser.PYAUTOGUILOCK
1140
- )
1225
+ gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK)
1141
1226
  with gui_lock: # Prevent issues with multiple processes
1142
1227
  pyautogui.moveTo(x, y, timeframe, pyautogui.easeOutQuad)
1143
1228
  if timeframe >= 0.25:
@@ -1156,9 +1241,7 @@ def _uc_gui_click_x_y(driver, x, y, timeframe=0.25, uc_lock=False):
1156
1241
 
1157
1242
 
1158
1243
  def uc_gui_click_x_y(driver, x, y, timeframe=0.25):
1159
- gui_lock = fasteners.InterProcessLock(
1160
- constants.MultiBrowser.PYAUTOGUILOCK
1161
- )
1244
+ gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK)
1162
1245
  with gui_lock: # Prevent issues with multiple processes
1163
1246
  install_pyautogui_if_missing(driver)
1164
1247
  import pyautogui
@@ -1169,10 +1252,7 @@ def uc_gui_click_x_y(driver, x, y, timeframe=0.25):
1169
1252
  connected = driver.is_connected()
1170
1253
  if (
1171
1254
  not connected
1172
- and (
1173
- not hasattr(sb_config, "_saved_width_ratio")
1174
- or not sb_config._saved_width_ratio
1175
- )
1255
+ and not getattr(sb_config, "_saved_width_ratio", None)
1176
1256
  and not __is_cdp_swap_needed(driver)
1177
1257
  ):
1178
1258
  driver.reconnect(0.1)
@@ -1207,8 +1287,8 @@ def uc_gui_click_x_y(driver, x, y, timeframe=0.25):
1207
1287
  driver.cdp.minimize()
1208
1288
  driver.cdp.set_window_rect(win_x, win_y, width, height)
1209
1289
  if IS_WINDOWS:
1210
- x = x * width_ratio
1211
- y = y * width_ratio
1290
+ x = x * (width_ratio + 0.03)
1291
+ y = y * (width_ratio - 0.03)
1212
1292
  _uc_gui_click_x_y(driver, x, y, timeframe=timeframe, uc_lock=False)
1213
1293
  return
1214
1294
  with suppress(Exception):
@@ -1221,9 +1301,14 @@ def uc_gui_click_x_y(driver, x, y, timeframe=0.25):
1221
1301
  def _on_a_cf_turnstile_page(driver):
1222
1302
  source = driver.get_page_source()
1223
1303
  if (
1224
- 'data-callback="onCaptchaSuccess"' in source
1225
- 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
1226
1310
  or 'id="challenge-widget-' in source
1311
+ or "challenges.cloudf" in source
1227
1312
  or "cf-turnstile-" in source
1228
1313
  ):
1229
1314
  return True
@@ -1248,6 +1333,8 @@ def _uc_gui_click_captcha(
1248
1333
  ctype=None,
1249
1334
  ):
1250
1335
  cdp_mode_on_at_start = __is_cdp_swap_needed(driver)
1336
+ if cdp_mode_on_at_start:
1337
+ return driver.cdp.gui_click_captcha()
1251
1338
  _on_a_captcha_page = None
1252
1339
  if ctype == "cf_t":
1253
1340
  if not _on_a_cf_turnstile_page(driver):
@@ -1276,9 +1363,7 @@ def _uc_gui_click_captcha(
1276
1363
  x = None
1277
1364
  y = None
1278
1365
  visible_iframe = True
1279
- gui_lock = fasteners.InterProcessLock(
1280
- constants.MultiBrowser.PYAUTOGUILOCK
1281
- )
1366
+ gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK)
1282
1367
  with gui_lock: # Prevent issues with multiple processes
1283
1368
  needs_switch = False
1284
1369
  width_ratio = 1.0
@@ -1350,31 +1435,27 @@ def _uc_gui_click_captcha(
1350
1435
  and driver.is_element_present("%s div" % frame)
1351
1436
  ):
1352
1437
  frame = "%s div" % frame
1353
- elif (
1354
- driver.is_element_present('[name*="cf-turnstile-"]')
1355
- and driver.is_element_present("#challenge-form div > div")
1356
- ):
1438
+ elif driver.is_element_present("#challenge-form div > div"):
1357
1439
  frame = "#challenge-form div > div"
1358
1440
  elif (
1359
- driver.is_element_present('[name*="cf-turnstile-"]')
1360
- and driver.is_element_present(
1441
+ driver.is_element_present(
1361
1442
  '[style="display: grid;"] div div'
1362
1443
  )
1363
1444
  ):
1364
1445
  frame = '[style="display: grid;"] div div'
1365
1446
  elif (
1366
1447
  driver.is_element_present('[name*="cf-turnstile-"]')
1367
- and driver.is_element_present("[class*=spacer] + div div")
1448
+ and driver.is_element_present(
1449
+ ".spacer + div div:not([class])"
1450
+ )
1368
1451
  ):
1369
- frame = '[class*=spacer] + div div'
1452
+ frame = '.spacer + div div:not([class])'
1370
1453
  elif (
1371
- driver.is_element_present('[name*="cf-turnstile-"]')
1372
- and driver.is_element_present("div.spacer div")
1454
+ driver.is_element_present(".spacer div:not([class])")
1373
1455
  ):
1374
- frame = "div.spacer div"
1456
+ frame = ".spacer div:not([class])"
1375
1457
  elif (
1376
- driver.is_element_present('script[src*="challenges.c"]')
1377
- and driver.is_element_present(
1458
+ driver.is_element_present(
1378
1459
  '[data-testid*="challenge-"] div'
1379
1460
  )
1380
1461
  ):
@@ -1383,6 +1464,10 @@ def _uc_gui_click_captcha(
1383
1464
  "div#turnstile-widget div:not([class])"
1384
1465
  ):
1385
1466
  frame = "div#turnstile-widget div:not([class])"
1467
+ elif driver.is_element_present(
1468
+ "ngx-turnstile div:not([class])"
1469
+ ):
1470
+ frame = "ngx-turnstile div:not([class])"
1386
1471
  elif driver.is_element_present(
1387
1472
  'form div:not([class]):has(input[name*="cf-turn"])'
1388
1473
  ):
@@ -1403,10 +1488,22 @@ def _uc_gui_click_captcha(
1403
1488
  frame = ".cf-turnstile-wrapper"
1404
1489
  elif driver.is_element_present('[class="cf-turnstile"]'):
1405
1490
  frame = '[class="cf-turnstile"]'
1491
+ elif driver.is_element_present(
1492
+ '[id*="turnstile"] div:not([class])'
1493
+ ):
1494
+ frame = '[id*="turnstile"] div:not([class])'
1495
+ elif driver.is_element_present(
1496
+ '[class*="turnstile"] div:not([class])'
1497
+ ):
1498
+ frame = '[class*="turnstile"] div:not([class])'
1406
1499
  elif driver.is_element_present(
1407
1500
  '[data-callback="onCaptchaSuccess"]'
1408
1501
  ):
1409
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])"
1410
1507
  else:
1411
1508
  return
1412
1509
  if (
@@ -1454,9 +1551,11 @@ def _uc_gui_click_captcha(
1454
1551
  else:
1455
1552
  driver.execute_script(script)
1456
1553
  elif (
1457
- driver.is_element_present("form")
1458
- and driver.is_element_present(
1459
- 'form [id*="turnstile"] > div:not([class])'
1554
+ driver.is_element_present(
1555
+ 'form [id*="turnstile"] div:not([class])'
1556
+ )
1557
+ or driver.is_element_present(
1558
+ 'form [class*="turnstile"] div:not([class])'
1460
1559
  )
1461
1560
  ):
1462
1561
  script = (
@@ -1464,12 +1563,35 @@ def _uc_gui_click_captcha(
1464
1563
  'form [id*="turnstile"]');
1465
1564
  var index = 0, length = $elements.length;
1466
1565
  for(; index < length; index++){
1566
+ $elements[index].setAttribute('align', 'left');}
1567
+ var $elements = document.querySelectorAll(
1568
+ 'form [class*="turnstile"]');
1569
+ var index = 0, length = $elements.length;
1570
+ for(; index < length; index++){
1467
1571
  $elements[index].setAttribute('align', 'left');}"""
1468
1572
  )
1469
1573
  if __is_cdp_swap_needed(driver):
1470
1574
  driver.cdp.evaluate(script)
1471
1575
  else:
1472
1576
  driver.execute_script(script)
1577
+ elif (
1578
+ driver.is_element_present(
1579
+ '[style*="text-align: center;"] div:not([class])'
1580
+ )
1581
+ ):
1582
+ script = (
1583
+ """var $elements = document.querySelectorAll(
1584
+ '[style*="text-align: center;"]');
1585
+ var index = 0, length = $elements.length;
1586
+ for(; index < length; index++){
1587
+ the_style = $elements[index].getAttribute('style');
1588
+ new_style = the_style.replaceAll('center', 'left');
1589
+ $elements[index].setAttribute('style', new_style);}"""
1590
+ )
1591
+ if __is_cdp_swap_needed(driver):
1592
+ driver.cdp.evaluate(script)
1593
+ else:
1594
+ driver.execute_script(script)
1473
1595
  if not is_in_frame or needs_switch:
1474
1596
  # Currently not in frame (or nested frame outside CF one)
1475
1597
  try:
@@ -1639,9 +1761,7 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
1639
1761
  import pyautogui
1640
1762
  pyautogui = get_configured_pyautogui(pyautogui)
1641
1763
  visible_iframe = True
1642
- gui_lock = fasteners.InterProcessLock(
1643
- constants.MultiBrowser.PYAUTOGUILOCK
1644
- )
1764
+ gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK)
1645
1765
  with gui_lock: # Prevent issues with multiple processes
1646
1766
  needs_switch = False
1647
1767
  if not __is_cdp_swap_needed(driver):
@@ -1691,9 +1811,9 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
1691
1811
  frame = '[data-callback="onCaptchaSuccess"]'
1692
1812
  elif (
1693
1813
  driver.is_element_present('[name*="cf-turnstile-"]')
1694
- and driver.is_element_present("div.spacer div")
1814
+ and driver.is_element_present(".spacer div:not([class])")
1695
1815
  ):
1696
- frame = "div.spacer div"
1816
+ frame = ".spacer div:not([class])"
1697
1817
  elif (
1698
1818
  driver.is_element_present('script[src*="challenges.c"]')
1699
1819
  and driver.is_element_present(
@@ -1727,6 +1847,10 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
1727
1847
  frame = ".cf-turnstile-wrapper"
1728
1848
  elif driver.is_element_present('[class="cf-turnstile"]'):
1729
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])"
1730
1854
  else:
1731
1855
  return
1732
1856
  else:
@@ -1786,8 +1910,7 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
1786
1910
  driver.is_element_present(".footer .clearfix .ray-id")
1787
1911
  or driver.is_element_present("script[data-cf-beacon]")
1788
1912
  )
1789
- and hasattr(sb_config, "_saved_cf_tab_count")
1790
- and sb_config._saved_cf_tab_count
1913
+ and getattr(sb_config, "_saved_cf_tab_count", None)
1791
1914
  and not __is_cdp_swap_needed(driver)
1792
1915
  ):
1793
1916
  driver.uc_open_with_disconnect(driver.current_url, 3.8)
@@ -1903,6 +2026,15 @@ def get_valid_binary_names_for_browser(browser):
1903
2026
  raise Exception("Invalid combination for OS browser binaries!")
1904
2027
 
1905
2028
 
2029
+ def _special_binary_exists(location, name):
2030
+ filename = str(location).split("/")[-1].split("\\")[-1]
2031
+ return (
2032
+ location
2033
+ and str(name).lower() in filename.lower()
2034
+ and os.path.exists(location)
2035
+ )
2036
+
2037
+
1906
2038
  def _repair_chromedriver(chrome_options, headless_options, mcv=None):
1907
2039
  if mcv:
1908
2040
  subprocess.check_call(
@@ -1957,14 +2089,12 @@ def _repair_edgedriver(edge_version):
1957
2089
 
1958
2090
 
1959
2091
  def _mark_driver_repaired():
1960
- import codecs
1961
-
1962
2092
  abs_path = os.path.abspath(".")
1963
2093
  driver_repaired_lock = constants.MultiBrowser.DRIVER_REPAIRED
1964
2094
  file_path = os.path.join(abs_path, driver_repaired_lock)
1965
2095
  if not os.path.exists(DOWNLOADS_FOLDER):
1966
2096
  os.makedirs(DOWNLOADS_FOLDER)
1967
- out_file = codecs.open(file_path, "w+", encoding="utf-8")
2097
+ out_file = open(file_path, mode="w+", encoding="utf-8")
1968
2098
  out_file.writelines("")
1969
2099
  out_file.close()
1970
2100
 
@@ -2006,6 +2136,7 @@ def _add_chrome_proxy_extension(
2006
2136
  """Implementation of https://stackoverflow.com/a/35293284/7058266
2007
2137
  for https://stackoverflow.com/q/12848327/7058266
2008
2138
  (Run Selenium on a proxy server that requires authentication.)"""
2139
+ zip_it = False
2009
2140
  args = " ".join(sys.argv)
2010
2141
  bypass_list = proxy_bypass_list
2011
2142
  if (
@@ -2349,6 +2480,19 @@ def _set_chrome_options(
2349
2480
  and not recorder_ext
2350
2481
  and (not extension_zip and not extension_dir)
2351
2482
  ):
2483
+ if (
2484
+ binary_location
2485
+ and isinstance(binary_location, str)
2486
+ and (
2487
+ binary_location.lower().endswith("comet")
2488
+ or binary_location.lower().endswith("comet.exe")
2489
+ or binary_location.lower().endswith("atlas")
2490
+ or binary_location.lower().endswith("atlas.exe")
2491
+ )
2492
+ ):
2493
+ # AI browsers don't like Incognito / Guest Mode
2494
+ incognito = False
2495
+ guest_mode = False
2352
2496
  if incognito:
2353
2497
  # Use Chrome's Incognito Mode
2354
2498
  # Incognito Mode prevents Chrome extensions from loading,
@@ -2372,14 +2516,28 @@ def _set_chrome_options(
2372
2516
  # Can be a comma-separated list of .ZIP or .CRX files
2373
2517
  extension_zip_list = extension_zip.split(",")
2374
2518
  for extension_zip_item in extension_zip_list:
2375
- abs_path = os.path.abspath(extension_zip_item)
2376
- 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)
2377
2533
  if extension_dir:
2378
2534
  # load-extension input can be a comma-separated list
2379
2535
  abs_path = (
2380
- ",".join(os.path.abspath(p) for p in extension_dir.split(","))
2536
+ ",".join(os.path.realpath(p) for p in extension_dir.split(","))
2381
2537
  )
2382
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))
2383
2541
  if (
2384
2542
  page_load_strategy
2385
2543
  and page_load_strategy.lower() in ["eager", "none"]
@@ -2388,8 +2546,7 @@ def _set_chrome_options(
2388
2546
  chrome_options.page_load_strategy = page_load_strategy.lower()
2389
2547
  elif (
2390
2548
  not page_load_strategy
2391
- and hasattr(settings, "PAGE_LOAD_STRATEGY")
2392
- and settings.PAGE_LOAD_STRATEGY
2549
+ and getattr(settings, "PAGE_LOAD_STRATEGY", None)
2393
2550
  and settings.PAGE_LOAD_STRATEGY.lower() in ["eager", "none"]
2394
2551
  ):
2395
2552
  # Only change it if not "normal", which is the default.
@@ -2414,37 +2571,32 @@ def _set_chrome_options(
2414
2571
  if (settings.DISABLE_CSP_ON_CHROME or disable_csp) and not headless:
2415
2572
  # Headless Chrome does not support extensions, which are required
2416
2573
  # for disabling the Content Security Policy on Chrome.
2417
- if is_using_uc(undetectable, browser_name):
2418
- disable_csp_zip = DISABLE_CSP_ZIP_PATH
2419
- disable_csp_dir = os.path.join(DOWNLOADS_FOLDER, "disable_csp")
2420
- _unzip_to_new_folder(disable_csp_zip, disable_csp_dir)
2421
- chrome_options = add_chrome_ext_dir(
2422
- chrome_options, disable_csp_dir
2423
- )
2424
- else:
2425
- 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)
2426
2581
  if ad_block_on and not headless:
2427
2582
  # Headless Chrome does not support extensions.
2428
- if is_using_uc(undetectable, browser_name):
2429
- ad_block_zip = AD_BLOCK_ZIP_PATH
2430
- ad_block_dir = os.path.join(DOWNLOADS_FOLDER, "ad_block")
2431
- _unzip_to_new_folder(ad_block_zip, ad_block_dir)
2432
- chrome_options = add_chrome_ext_dir(chrome_options, ad_block_dir)
2433
- else:
2434
- 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)
2435
2588
  if recorder_ext and not headless:
2436
- if is_using_uc(undetectable, browser_name):
2437
- recorder_zip = RECORDER_ZIP_PATH
2438
- recorder_dir = os.path.join(DOWNLOADS_FOLDER, "recorder")
2439
- _unzip_to_new_folder(recorder_zip, recorder_dir)
2440
- chrome_options = add_chrome_ext_dir(chrome_options, recorder_dir)
2441
- else:
2442
- 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)
2443
2594
  if chromium_arg and "sbase" in chromium_arg:
2444
2595
  sbase_ext_zip = SBASE_EXT_ZIP_PATH
2445
2596
  sbase_ext_dir = os.path.join(DOWNLOADS_FOLDER, "sbase_ext")
2446
2597
  _unzip_to_new_folder(sbase_ext_zip, sbase_ext_dir)
2447
2598
  chrome_options = add_chrome_ext_dir(chrome_options, sbase_ext_dir)
2599
+ sb_config._ext_dirs.append(sbase_ext_dir)
2448
2600
  if proxy_string:
2449
2601
  if proxy_auth:
2450
2602
  zip_it = True
@@ -2527,15 +2679,20 @@ def _set_chrome_options(
2527
2679
  if is_using_uc(undetectable, browser_name):
2528
2680
  chrome_options.add_argument("--disable-application-cache")
2529
2681
  chrome_options.add_argument("--disable-setuid-sandbox")
2530
- if not binary_location:
2682
+ if not binary_location:
2683
+ if os.path.exists("/bin/google-chrome"):
2684
+ binary_location = "/bin/google-chrome"
2685
+ elif os.path.exists("/usr/bin/google-chrome-stable"):
2686
+ binary_location = "/usr/bin/google-chrome-stable"
2687
+ elif os.path.exists("/usr/bin/google-chrome"):
2688
+ binary_location = "/usr/bin/google-chrome"
2689
+ elif os.path.exists("/usr/bin/google-chrome-stable"):
2690
+ binary_location = "/usr/bin/google-chrome-stable"
2691
+ else:
2531
2692
  br_app = "google-chrome"
2532
2693
  binary_loc = detect_b_ver.get_binary_location(br_app, True)
2533
2694
  if os.path.exists(binary_loc):
2534
2695
  binary_location = binary_loc
2535
- elif os.path.exists("/usr/bin/google-chrome-stable"):
2536
- binary_location = "/usr/bin/google-chrome-stable"
2537
- elif os.path.exists("/usr/bin/google-chrome"):
2538
- binary_location = "/usr/bin/google-chrome"
2539
2696
  extra_disabled_features = []
2540
2697
  if chromium_arg:
2541
2698
  # Can be a comma-separated list of Chromium args or a list
@@ -2592,6 +2749,7 @@ def _set_chrome_options(
2592
2749
  chrome_options.add_argument("--disable-renderer-backgrounding")
2593
2750
  chrome_options.add_argument("--disable-backgrounding-occluded-windows")
2594
2751
  chrome_options.add_argument("--disable-client-side-phishing-detection")
2752
+ chrome_options.add_argument("--disable-device-discovery-notifications")
2595
2753
  chrome_options.add_argument("--disable-oopr-debug-crash-dump")
2596
2754
  chrome_options.add_argument("--disable-top-sites")
2597
2755
  chrome_options.add_argument("--ash-no-nudges")
@@ -2608,6 +2766,7 @@ def _set_chrome_options(
2608
2766
  included_disabled_features.append("OptimizationHints")
2609
2767
  included_disabled_features.append("OptimizationHintsFetching")
2610
2768
  included_disabled_features.append("Translate")
2769
+ included_disabled_features.append("ComponentUpdater")
2611
2770
  included_disabled_features.append("OptimizationTargetPrediction")
2612
2771
  included_disabled_features.append("OptimizationGuideModelDownloading")
2613
2772
  included_disabled_features.append("DownloadBubble")
@@ -2618,11 +2777,24 @@ def _set_chrome_options(
2618
2777
  included_disabled_features.append("SidePanelPinning")
2619
2778
  included_disabled_features.append("UserAgentClientHint")
2620
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")
2621
2785
  for item in extra_disabled_features:
2622
2786
  if item not in included_disabled_features:
2623
2787
  included_disabled_features.append(item)
2624
2788
  d_f_string = ",".join(included_disabled_features)
2625
2789
  chrome_options.add_argument("--disable-features=%s" % d_f_string)
2790
+ chrome_options.add_argument("--enable-unsafe-extension-debugging")
2791
+ if proxy_string:
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
2626
2798
  if (
2627
2799
  is_using_uc(undetectable, browser_name)
2628
2800
  and (
@@ -2640,7 +2812,8 @@ def _set_chrome_options(
2640
2812
  chrome_options.add_argument("--disable-popup-blocking")
2641
2813
  # Skip remaining options that trigger anti-bot services
2642
2814
  return chrome_options
2643
- chrome_options.add_argument("--test-type")
2815
+ if not proxy_string:
2816
+ chrome_options.add_argument("--test-type")
2644
2817
  chrome_options.add_argument("--log-level=3")
2645
2818
  chrome_options.add_argument("--no-first-run")
2646
2819
  chrome_options.add_argument("--allow-insecure-localhost")
@@ -2886,21 +3059,29 @@ def get_driver(
2886
3059
  device_pixel_ratio=None,
2887
3060
  browser=None, # A duplicate of browser_name to avoid confusion
2888
3061
  ):
3062
+ sb_config._ext_dirs = []
2889
3063
  driver_dir = DRIVER_DIR
2890
- if (
2891
- hasattr(sb_config, "binary_location")
2892
- and sb_config.binary_location == "cft"
2893
- ):
3064
+ if binary_location == "_chromium_":
3065
+ driver_dir = DRIVER_DIR_CHROMIUM
3066
+ elif binary_location == "cft":
2894
3067
  driver_dir = DRIVER_DIR_CFT
2895
- if (
2896
- hasattr(sb_config, "binary_location")
2897
- and sb_config.binary_location == "chs"
2898
- ):
3068
+ elif binary_location == "chs":
2899
3069
  driver_dir = DRIVER_DIR_CHS
3070
+ elif _special_binary_exists(binary_location, "opera"):
3071
+ driver_dir = DRIVER_DIR_OPERA
3072
+ sb_config._cdp_browser = "opera"
3073
+ elif _special_binary_exists(binary_location, "brave"):
3074
+ driver_dir = DRIVER_DIR_BRAVE
3075
+ sb_config._cdp_browser = "brave"
3076
+ elif _special_binary_exists(binary_location, "comet"):
3077
+ driver_dir = DRIVER_DIR_COMET
3078
+ sb_config._cdp_browser = "comet"
3079
+ elif _special_binary_exists(binary_location, "atlas"):
3080
+ driver_dir = DRIVER_DIR_ATLAS
3081
+ sb_config._cdp_browser = "atlas"
2900
3082
  if (
2901
3083
  hasattr(sb_config, "settings")
2902
- and hasattr(sb_config.settings, "NEW_DRIVER_DIR")
2903
- and sb_config.settings.NEW_DRIVER_DIR
3084
+ and getattr(sb_config.settings, "NEW_DRIVER_DIR", None)
2904
3085
  and os.path.exists(sb_config.settings.NEW_DRIVER_DIR)
2905
3086
  ):
2906
3087
  driver_dir = sb_config.settings.NEW_DRIVER_DIR
@@ -2909,7 +3090,26 @@ def get_driver(
2909
3090
  browser_name = browser
2910
3091
  else:
2911
3092
  browser_name = "chrome" # The default if not specified
3093
+ if browser_name in constants.ChromiumSubs.chromium_subs:
3094
+ browser_name = "chrome"
2912
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
2913
3113
  if headless2 and browser_name == constants.Browser.FIREFOX:
2914
3114
  headless2 = False # Only for Chromium
2915
3115
  headless = True
@@ -2930,6 +3130,51 @@ def get_driver(
2930
3130
  or browser_name == constants.Browser.EDGE
2931
3131
  )
2932
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
2933
3178
  if (
2934
3179
  binary_location.lower() == "cft"
2935
3180
  and browser_name == constants.Browser.GOOGLE_CHROME
@@ -3067,12 +3312,25 @@ def get_driver(
3067
3312
  binary_name = "Google Chrome for Testing"
3068
3313
  binary_location += "/Google Chrome for Testing.app"
3069
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"
3070
3322
  elif binary_name == "chrome-linux64":
3071
3323
  binary_name = "chrome"
3072
3324
  binary_location += "/chrome"
3325
+ elif binary_name == "chrome-linux":
3326
+ binary_name = "chrome"
3327
+ binary_location += "/chrome"
3073
3328
  elif binary_name in ["chrome-win32", "chrome-win64"]:
3074
3329
  binary_name = "chrome.exe"
3075
3330
  binary_location += "\\chrome.exe"
3331
+ elif binary_name in ["chrome-win"]:
3332
+ binary_name = "chrome.exe"
3333
+ binary_location += "\\chrome.exe"
3076
3334
  elif binary_name in [
3077
3335
  "chrome-headless-shell-mac-arm64",
3078
3336
  "chrome-headless-shell-mac-x64",
@@ -3110,8 +3368,8 @@ def get_driver(
3110
3368
  proxy_pass = None
3111
3369
  proxy_scheme = "http"
3112
3370
  if proxy_string:
3113
- # (The code below was for the Chrome 137 extension fix)
3114
- # 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
3115
3373
  username_and_password = None
3116
3374
  if "@" in proxy_string:
3117
3375
  # Format => username:password@hostname:port
@@ -3844,22 +4102,34 @@ def get_local_driver(
3844
4102
  downloads_path = DOWNLOADS_FOLDER
3845
4103
  driver_dir = DRIVER_DIR
3846
4104
  special_chrome = False
3847
- if (
3848
- hasattr(sb_config, "binary_location")
3849
- and sb_config.binary_location == "cft"
3850
- ):
3851
- special_chrome = True
3852
- driver_dir = DRIVER_DIR_CFT
3853
- if (
3854
- hasattr(sb_config, "binary_location")
3855
- and sb_config.binary_location == "chs"
3856
- ):
3857
- special_chrome = True
3858
- driver_dir = DRIVER_DIR_CHS
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
3859
4130
  if (
3860
4131
  hasattr(sb_config, "settings")
3861
- and hasattr(sb_config.settings, "NEW_DRIVER_DIR")
3862
- and sb_config.settings.NEW_DRIVER_DIR
4132
+ and getattr(sb_config.settings, "NEW_DRIVER_DIR", None)
3863
4133
  and os.path.exists(sb_config.settings.NEW_DRIVER_DIR)
3864
4134
  ):
3865
4135
  driver_dir = sb_config.settings.NEW_DRIVER_DIR
@@ -4269,8 +4539,8 @@ def get_local_driver(
4269
4539
  sys.argv = sys_args # Put back the original sys args
4270
4540
 
4271
4541
  # For Microsoft Edge (Chromium) version 80 or higher
4272
- Edge = webdriver.edge.webdriver.WebDriver
4273
- EdgeOptions = webdriver.edge.webdriver.Options
4542
+ Edge = webdriver.Edge
4543
+ EdgeOptions = webdriver.EdgeOptions
4274
4544
  if local_edgedriver and os.path.exists(local_edgedriver):
4275
4545
  try:
4276
4546
  make_driver_executable_if_not(local_edgedriver)
@@ -4424,12 +4694,12 @@ def get_local_driver(
4424
4694
  # Can be a comma-separated list of .ZIP or .CRX files
4425
4695
  extension_zip_list = extension_zip.split(",")
4426
4696
  for extension_zip_item in extension_zip_list:
4427
- abs_path = os.path.abspath(extension_zip_item)
4697
+ abs_path = os.path.realpath(extension_zip_item)
4428
4698
  edge_options.add_extension(abs_path)
4429
4699
  if extension_dir:
4430
4700
  # load-extension input can be a comma-separated list
4431
4701
  abs_path = (
4432
- ",".join(os.path.abspath(p) for p in extension_dir.split(","))
4702
+ ",".join(os.path.realpath(p) for p in extension_dir.split(","))
4433
4703
  )
4434
4704
  edge_options = add_chrome_ext_dir(edge_options, abs_path)
4435
4705
  edge_options.add_argument("--disable-infobars")
@@ -4469,8 +4739,7 @@ def get_local_driver(
4469
4739
  edge_options.page_load_strategy = page_load_strategy.lower()
4470
4740
  elif (
4471
4741
  not page_load_strategy
4472
- and hasattr(settings, "PAGE_LOAD_STRATEGY")
4473
- and settings.PAGE_LOAD_STRATEGY
4742
+ and getattr(settings, "PAGE_LOAD_STRATEGY", None)
4474
4743
  and settings.PAGE_LOAD_STRATEGY.lower() in ["eager", "none"]
4475
4744
  ):
4476
4745
  # Only change it if not "normal", which is the default.
@@ -4581,6 +4850,7 @@ def get_local_driver(
4581
4850
  included_disabled_features.append("OptimizationHints")
4582
4851
  included_disabled_features.append("OptimizationHintsFetching")
4583
4852
  included_disabled_features.append("Translate")
4853
+ included_disabled_features.append("ComponentUpdater")
4584
4854
  included_disabled_features.append("OptimizationTargetPrediction")
4585
4855
  included_disabled_features.append("OptimizationGuideModelDownloading")
4586
4856
  included_disabled_features.append("InsecureDownloadWarnings")
@@ -4591,6 +4861,11 @@ def get_local_driver(
4591
4861
  included_disabled_features.append(
4592
4862
  "DisableLoadExtensionCommandLineSwitch"
4593
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")
4594
4869
  for item in extra_disabled_features:
4595
4870
  if item not in included_disabled_features:
4596
4871
  included_disabled_features.append(item)
@@ -4682,8 +4957,7 @@ def get_local_driver(
4682
4957
  options.page_load_strategy = page_load_strategy.lower()
4683
4958
  elif (
4684
4959
  not page_load_strategy
4685
- and hasattr(settings, "PAGE_LOAD_STRATEGY")
4686
- and settings.PAGE_LOAD_STRATEGY
4960
+ and getattr(settings, "PAGE_LOAD_STRATEGY", None)
4687
4961
  and settings.PAGE_LOAD_STRATEGY.lower() in ["eager", "none"]
4688
4962
  ):
4689
4963
  # Only change it if not "normal", which is the default.
@@ -4693,6 +4967,27 @@ def get_local_driver(
4693
4967
  )
4694
4968
  return extend_driver(driver)
4695
4969
  elif browser_name == constants.Browser.GOOGLE_CHROME:
4970
+ set_chromium = None
4971
+ if _special_binary_exists(binary_location, "opera"):
4972
+ set_chromium = "opera"
4973
+ local_chromedriver = DRIVER_DIR_OPERA + "/chromedriver"
4974
+ if IS_WINDOWS:
4975
+ local_chromedriver = DRIVER_DIR_OPERA + "/chromedriver.exe"
4976
+ if _special_binary_exists(binary_location, "brave"):
4977
+ set_chromium = "brave"
4978
+ local_chromedriver = DRIVER_DIR_BRAVE + "/chromedriver"
4979
+ if IS_WINDOWS:
4980
+ local_chromedriver = DRIVER_DIR_BRAVE + "/chromedriver.exe"
4981
+ if _special_binary_exists(binary_location, "comet"):
4982
+ set_chromium = "comet"
4983
+ local_chromedriver = DRIVER_DIR_COMET + "/chromedriver"
4984
+ if IS_WINDOWS:
4985
+ local_chromedriver = DRIVER_DIR_COMET + "/chromedriver.exe"
4986
+ if _special_binary_exists(binary_location, "atlas"):
4987
+ set_chromium = "atlas"
4988
+ local_chromedriver = DRIVER_DIR_ATLAS + "/chromedriver"
4989
+ if IS_WINDOWS:
4990
+ local_chromedriver = DRIVER_DIR_ATLAS + "/chromedriver.exe"
4696
4991
  try:
4697
4992
  chrome_options = _set_chrome_options(
4698
4993
  browser_name,
@@ -4750,6 +5045,8 @@ def get_local_driver(
4750
5045
  device_height,
4751
5046
  device_pixel_ratio,
4752
5047
  )
5048
+ if binary_location and "chromium_drivers" in binary_location:
5049
+ chrome_options.add_argument("--use-mock-keychain")
4753
5050
  use_version = "latest"
4754
5051
  major_chrome_version = None
4755
5052
  saved_mcv = None
@@ -4814,6 +5111,12 @@ def get_local_driver(
4814
5111
  major_chrome_version = None
4815
5112
  if major_chrome_version:
4816
5113
  use_version = major_chrome_version
5114
+ if (
5115
+ set_chromium == "opera"
5116
+ and use_version.isnumeric()
5117
+ and int(use_version) < 130
5118
+ ):
5119
+ use_version = "130" # Special case
4817
5120
  ch_driver_version = None
4818
5121
  path_chromedriver = chromedriver_on_path()
4819
5122
  if os.path.exists(local_chromedriver):
@@ -4831,7 +5134,7 @@ def get_local_driver(
4831
5134
  ch_driver_version = output
4832
5135
  if driver_version == "keep":
4833
5136
  driver_version = ch_driver_version
4834
- elif path_chromedriver:
5137
+ elif path_chromedriver and not set_chromium:
4835
5138
  try:
4836
5139
  make_driver_executable_if_not(path_chromedriver)
4837
5140
  except Exception as e:
@@ -5593,6 +5896,7 @@ def get_local_driver(
5593
5896
  )
5594
5897
  driver.default_get = driver.get # Save copy of original
5595
5898
  driver.cdp = None # Set a placeholder
5899
+ driver._is_using_uc = False
5596
5900
  driver._is_using_cdp = False
5597
5901
  driver._is_connected = True
5598
5902
  if uc_activated:
@@ -5627,6 +5931,7 @@ def get_local_driver(
5627
5931
  driver, *args, **kwargs
5628
5932
  )
5629
5933
  )
5934
+ driver.activate_cdp_mode = driver.uc_activate_cdp_mode
5630
5935
  driver.uc_open_with_cdp_mode = (
5631
5936
  lambda *args, **kwargs: uc_open_with_cdp_mode(
5632
5937
  driver, *args, **kwargs
@@ -5687,6 +5992,12 @@ def get_local_driver(
5687
5992
  driver, *args, **kwargs
5688
5993
  )
5689
5994
  )
5995
+ driver.default_execute_cdp_cmd = driver.execute_cdp_cmd
5996
+ driver.execute_cdp_cmd = (
5997
+ lambda *args, **kwargs: uc_execute_cdp_cmd(
5998
+ driver, *args, **kwargs
5999
+ )
6000
+ )
5690
6001
  driver._is_hidden = (headless or headless2)
5691
6002
  driver._is_using_uc = True
5692
6003
  with suppress(Exception):
@@ -5700,8 +6011,18 @@ def get_local_driver(
5700
6011
  time.sleep(0.003)
5701
6012
  driver.switch_to.window(driver.window_handles[0])
5702
6013
  time.sleep(0.003)
5703
- driver.connect()
5704
- 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)
5705
6026
  if mobile_emulator:
5706
6027
  uc_metrics = {}
5707
6028
  if (
@@ -5729,6 +6050,8 @@ def get_local_driver(
5729
6050
  'Emulation.setDeviceMetricsOverride',
5730
6051
  set_device_metrics_override
5731
6052
  )
6053
+ else:
6054
+ driver.get = lambda url: updated_get(driver, url)
5732
6055
  return extend_driver(
5733
6056
  driver, proxy_auth, use_uc, recorder_ext
5734
6057
  )