Skip to content

Commit f242c8d

Browse files
committed
Update CDP Mode
1 parent d0625e8 commit f242c8d

File tree

4 files changed

+337
-2
lines changed

4 files changed

+337
-2
lines changed

examples/cdp_mode/ReadMe.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ sb.cdp.find_all(selector)
231231
sb.cdp.find_elements_by_text(text, tag_name=None)
232232
sb.cdp.select(selector)
233233
sb.cdp.select_all(selector)
234+
sb.cdp.find_elements(selector)
234235
sb.cdp.click_link(link_text)
235236
sb.cdp.tile_windows(windows=None, max_columns=0)
236237
sb.cdp.get_all_cookies(*args, **kwargs)
@@ -290,13 +291,17 @@ sb.cdp.get_element_attributes(selector)
290291
sb.cdp.get_element_html(selector)
291292
sb.cdp.set_locale(locale)
292293
sb.cdp.set_attributes(selector, attribute, value)
294+
sb.cdp.gui_click_x_y(x, y)
295+
sb.cdp.gui_click_element(selector)
293296
sb.cdp.internalize_links()
294297
sb.cdp.is_element_present(selector)
295298
sb.cdp.is_element_visible(selector)
296299
sb.cdp.assert_element(selector)
297300
sb.cdp.assert_element_present(selector)
298301
sb.cdp.assert_text(text, selector="html")
299302
sb.cdp.assert_exact_text(text, selector="html")
303+
sb.cdp.scroll_down(amount=25)
304+
sb.cdp.scroll_up(amount=25)
300305
sb.cdp.save_screenshot(name, folder=None, selector=None)
301306
```
302307

seleniumbase/core/browser_launcher.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,7 @@ def uc_open_with_cdp_mode(driver, url=None):
588588
cdp.find_elements_by_text = CDPM.find_elements_by_text
589589
cdp.select = CDPM.select
590590
cdp.select_all = CDPM.select_all
591+
cdp.find_elements = CDPM.find_elements
591592
cdp.click_link = CDPM.click_link
592593
cdp.tile_windows = CDPM.tile_windows
593594
cdp.get_all_cookies = CDPM.get_all_cookies
@@ -619,6 +620,8 @@ def uc_open_with_cdp_mode(driver, url=None):
619620
cdp.reset_window_size = CDPM.reset_window_size
620621
cdp.set_locale = CDPM.set_locale
621622
cdp.set_attributes = CDPM.set_attributes
623+
cdp.gui_click_x_y = CDPM.gui_click_x_y
624+
cdp.gui_click_element = CDPM.gui_click_element
622625
cdp.internalize_links = CDPM.internalize_links
623626
cdp.get_window = CDPM.get_window
624627
cdp.get_element_attributes = CDPM.get_element_attributes
@@ -655,6 +658,8 @@ def uc_open_with_cdp_mode(driver, url=None):
655658
cdp.assert_element_visible = CDPM.assert_element
656659
cdp.assert_text = CDPM.assert_text
657660
cdp.assert_exact_text = CDPM.assert_exact_text
661+
cdp.scroll_down = CDPM.scroll_down
662+
cdp.scroll_up = CDPM.scroll_up
658663
cdp.save_screenshot = CDPM.save_screenshot
659664
cdp.page = page # async world
660665
cdp.driver = driver.cdp_base # async world
@@ -2218,7 +2223,6 @@ def _set_chrome_options(
22182223
)
22192224
):
22202225
chrome_options.add_argument("--no-pings")
2221-
chrome_options.add_argument("--disable-popup-blocking")
22222226
chrome_options.add_argument("--homepage=chrome://version/")
22232227
chrome_options.add_argument("--animation-duration-scale=0")
22242228
chrome_options.add_argument("--wm-window-animations-disabled")

seleniumbase/core/sb_cdp.py

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
"""Add CDP methods to extend the driver"""
2+
import fasteners
23
import math
34
import os
45
import re
6+
import sys
57
import time
68
from contextlib import suppress
79
from seleniumbase import config as sb_config
@@ -239,6 +241,9 @@ def select_all(self, selector, timeout=settings.SMALL_TIMEOUT):
239241
self.__slow_mode_pause_if_set()
240242
return updated_elements
241243

244+
def find_elements(self, selector, timeout=settings.SMALL_TIMEOUT):
245+
return self.select_all(selector, timeout=timeout)
246+
242247
def click_link(self, link_text):
243248
self.find_elements_by_text(link_text, "a")[0].click()
244249

@@ -835,6 +840,194 @@ def set_attributes(self, selector, attribute, value):
835840
with suppress(Exception):
836841
self.loop.run_until_complete(self.page.evaluate(js_code))
837842

843+
def __verify_pyautogui_has_a_headed_browser(self):
844+
"""PyAutoGUI requires a headed browser so that it can
845+
focus on the correct element when performing actions."""
846+
driver = self.driver
847+
if hasattr(driver, "cdp_base"):
848+
driver = driver.cdp_base
849+
if driver.config.headless:
850+
raise Exception(
851+
"PyAutoGUI can't be used in headless mode!"
852+
)
853+
854+
def __install_pyautogui_if_missing(self):
855+
self.__verify_pyautogui_has_a_headed_browser()
856+
driver = self.driver
857+
if hasattr(driver, "cdp_base"):
858+
driver = driver.cdp_base
859+
pip_find_lock = fasteners.InterProcessLock(
860+
constants.PipInstall.FINDLOCK
861+
)
862+
with pip_find_lock: # Prevent issues with multiple processes
863+
try:
864+
import pyautogui
865+
with suppress(Exception):
866+
use_pyautogui_ver = constants.PyAutoGUI.VER
867+
if pyautogui.__version__ != use_pyautogui_ver:
868+
del pyautogui
869+
shared_utils.pip_install(
870+
"pyautogui", version=use_pyautogui_ver
871+
)
872+
import pyautogui
873+
except Exception:
874+
print("\nPyAutoGUI required! Installing now...")
875+
shared_utils.pip_install(
876+
"pyautogui", version=constants.PyAutoGUI.VER
877+
)
878+
try:
879+
import pyautogui
880+
except Exception:
881+
if (
882+
shared_utils.is_linux()
883+
and (not sb_config.headed or sb_config.xvfb)
884+
and not driver.config.headless
885+
):
886+
from sbvirtualdisplay import Display
887+
xvfb_width = 1366
888+
xvfb_height = 768
889+
if (
890+
hasattr(sb_config, "_xvfb_width")
891+
and sb_config._xvfb_width
892+
and isinstance(sb_config._xvfb_width, int)
893+
and hasattr(sb_config, "_xvfb_height")
894+
and sb_config._xvfb_height
895+
and isinstance(sb_config._xvfb_height, int)
896+
):
897+
xvfb_width = sb_config._xvfb_width
898+
xvfb_height = sb_config._xvfb_height
899+
if xvfb_width < 1024:
900+
xvfb_width = 1024
901+
sb_config._xvfb_width = xvfb_width
902+
if xvfb_height < 768:
903+
xvfb_height = 768
904+
sb_config._xvfb_height = xvfb_height
905+
with suppress(Exception):
906+
xvfb_display = Display(
907+
visible=True,
908+
size=(xvfb_width, xvfb_height),
909+
backend="xvfb",
910+
use_xauth=True,
911+
)
912+
xvfb_display.start()
913+
914+
def __get_configured_pyautogui(self, pyautogui_copy):
915+
if (
916+
shared_utils.is_linux()
917+
and hasattr(pyautogui_copy, "_pyautogui_x11")
918+
and "DISPLAY" in os.environ.keys()
919+
):
920+
if (
921+
hasattr(sb_config, "_pyautogui_x11_display")
922+
and sb_config._pyautogui_x11_display
923+
and hasattr(pyautogui_copy._pyautogui_x11, "_display")
924+
and (
925+
sb_config._pyautogui_x11_display
926+
== pyautogui_copy._pyautogui_x11._display
927+
)
928+
):
929+
pass
930+
else:
931+
import Xlib.display
932+
pyautogui_copy._pyautogui_x11._display = (
933+
Xlib.display.Display(os.environ['DISPLAY'])
934+
)
935+
sb_config._pyautogui_x11_display = (
936+
pyautogui_copy._pyautogui_x11._display
937+
)
938+
return pyautogui_copy
939+
940+
def __gui_click_x_y(self, x, y, timeframe=0.25, uc_lock=False):
941+
self.__install_pyautogui_if_missing()
942+
import pyautogui
943+
pyautogui = self.__get_configured_pyautogui(pyautogui)
944+
screen_width, screen_height = pyautogui.size()
945+
if x < 0 or y < 0 or x > screen_width or y > screen_height:
946+
raise Exception(
947+
"PyAutoGUI cannot click on point (%s, %s)"
948+
" outside screen. (Width: %s, Height: %s)"
949+
% (x, y, screen_width, screen_height)
950+
)
951+
if uc_lock:
952+
gui_lock = fasteners.InterProcessLock(
953+
constants.MultiBrowser.PYAUTOGUILOCK
954+
)
955+
with gui_lock: # Prevent issues with multiple processes
956+
pyautogui.moveTo(x, y, timeframe, pyautogui.easeOutQuad)
957+
if timeframe >= 0.25:
958+
time.sleep(0.056) # Wait if moving at human-speed
959+
if "--debug" in sys.argv:
960+
print(" <DEBUG> pyautogui.click(%s, %s)" % (x, y))
961+
pyautogui.click(x=x, y=y)
962+
else:
963+
# Called from a method where the gui_lock is already active
964+
pyautogui.moveTo(x, y, timeframe, pyautogui.easeOutQuad)
965+
if timeframe >= 0.25:
966+
time.sleep(0.056) # Wait if moving at human-speed
967+
if "--debug" in sys.argv:
968+
print(" <DEBUG> pyautogui.click(%s, %s)" % (x, y))
969+
pyautogui.click(x=x, y=y)
970+
971+
def gui_click_x_y(self, x, y, timeframe=0.25):
972+
gui_lock = fasteners.InterProcessLock(
973+
constants.MultiBrowser.PYAUTOGUILOCK
974+
)
975+
with gui_lock: # Prevent issues with multiple processes
976+
self.__install_pyautogui_if_missing()
977+
import pyautogui
978+
pyautogui = self.__get_configured_pyautogui(pyautogui)
979+
width_ratio = 1.0
980+
if (
981+
shared_utils.is_windows()
982+
and (
983+
not hasattr(sb_config, "_saved_width_ratio")
984+
or not sb_config._saved_width_ratio
985+
)
986+
):
987+
window_rect = self.get_window_rect()
988+
width = window_rect["width"]
989+
height = window_rect["height"]
990+
win_x = window_rect["x"]
991+
win_y = window_rect["y"]
992+
if (
993+
hasattr(sb_config, "_saved_width_ratio")
994+
and sb_config._saved_width_ratio
995+
):
996+
width_ratio = sb_config._saved_width_ratio
997+
else:
998+
scr_width = pyautogui.size().width
999+
self.maximize()
1000+
win_width = self.get_window_size()["width"]
1001+
width_ratio = round(float(scr_width) / float(win_width), 2)
1002+
width_ratio += 0.01
1003+
if width_ratio < 0.45 or width_ratio > 2.55:
1004+
width_ratio = 1.01
1005+
sb_config._saved_width_ratio = width_ratio
1006+
self.set_window_rect(win_x, win_y, width, height)
1007+
self.bring_active_window_to_front()
1008+
elif (
1009+
shared_utils.is_windows()
1010+
and hasattr(sb_config, "_saved_width_ratio")
1011+
and sb_config._saved_width_ratio
1012+
):
1013+
width_ratio = sb_config._saved_width_ratio
1014+
self.bring_active_window_to_front()
1015+
if shared_utils.is_windows():
1016+
x = x * width_ratio
1017+
y = y * width_ratio
1018+
self.__gui_click_x_y(x, y, timeframe=timeframe, uc_lock=False)
1019+
return
1020+
self.bring_active_window_to_front()
1021+
self.__gui_click_x_y(x, y, timeframe=timeframe, uc_lock=False)
1022+
1023+
def gui_click_element(self, selector, timeframe=0.25):
1024+
self.__slow_mode_pause_if_set()
1025+
x, y = self.get_gui_element_center(selector)
1026+
self.__add_light_pause()
1027+
self.gui_click_x_y(x, y, timeframe=timeframe)
1028+
self.__slow_mode_pause_if_set()
1029+
self.loop.run_until_complete(self.page.wait())
1030+
8381031
def internalize_links(self):
8391032
"""All `target="_blank"` links become `target="_self"`.
8401033
This prevents those links from opening in a new tab."""
@@ -938,6 +1131,16 @@ def assert_exact_text(
9381131
% (text, element.text_all, selector)
9391132
)
9401133

1134+
def scroll_down(self, amount=25):
1135+
self.loop.run_until_complete(
1136+
self.page.scroll_down(amount)
1137+
)
1138+
1139+
def scroll_up(self, amount=25):
1140+
self.loop.run_until_complete(
1141+
self.page.scroll_up(amount)
1142+
)
1143+
9411144
def save_screenshot(self, name, folder=None, selector=None):
9421145
filename = name
9431146
if folder:

0 commit comments

Comments
 (0)