Skip to content

Commit a1a3b65

Browse files
committed
Improve CDP Mode
1 parent d200f49 commit a1a3b65

File tree

5 files changed

+138
-72
lines changed

5 files changed

+138
-72
lines changed

seleniumbase/core/browser_launcher.py

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1413,7 +1413,7 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
14131413
ctype = "cf_t"
14141414
else:
14151415
return
1416-
if not driver.is_connected():
1416+
if not driver.is_connected() and not __is_cdp_swap_needed(driver):
14171417
driver.connect()
14181418
time.sleep(2)
14191419
install_pyautogui_if_missing(driver)
@@ -1425,15 +1425,18 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
14251425
)
14261426
with gui_lock: # Prevent issues with multiple processes
14271427
needs_switch = False
1428-
is_in_frame = js_utils.is_in_frame(driver)
1428+
if not __is_cdp_swap_needed(driver):
1429+
is_in_frame = js_utils.is_in_frame(driver)
1430+
else:
1431+
is_in_frame = False
14291432
selector = "#challenge-stage"
14301433
if ctype == "g_rc":
14311434
selector = "#recaptcha-token"
14321435
if is_in_frame and driver.is_element_present(selector):
14331436
driver.switch_to.parent_frame()
14341437
needs_switch = True
14351438
is_in_frame = js_utils.is_in_frame(driver)
1436-
if not is_in_frame:
1439+
if not is_in_frame and not __is_cdp_swap_needed(driver):
14371440
# Make sure the window is on top
14381441
page_actions.switch_to_window(
14391442
driver, driver.current_window_handle, 2, uc_lock=False
@@ -1500,17 +1503,18 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
15001503
and frame == "iframe"
15011504
):
15021505
frame = 'iframe[title="reCAPTCHA"]'
1503-
if not is_in_frame or needs_switch:
1504-
# Currently not in frame (or nested frame outside CF one)
1505-
try:
1506-
if visible_iframe or ctype == "g_rc":
1507-
driver.switch_to_frame(frame)
1508-
except Exception:
1509-
if visible_iframe or ctype == "g_rc":
1510-
if driver.is_element_present("iframe"):
1511-
driver.switch_to_frame("iframe")
1512-
else:
1513-
return
1506+
if not __is_cdp_swap_needed(driver):
1507+
if not is_in_frame or needs_switch:
1508+
# Currently not in frame (or nested frame outside CF one)
1509+
try:
1510+
if visible_iframe or ctype == "g_rc":
1511+
driver.switch_to_frame(frame)
1512+
except Exception:
1513+
if visible_iframe or ctype == "g_rc":
1514+
if driver.is_element_present("iframe"):
1515+
driver.switch_to_frame("iframe")
1516+
else:
1517+
return
15141518
try:
15151519
selector = "div.cf-turnstile"
15161520
if ctype == "g_rc":
@@ -1526,11 +1530,11 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
15261530
tab_count += 1
15271531
time.sleep(0.027)
15281532
active_element_css = js_utils.get_active_element_css(driver)
1529-
print(active_element_css)
15301533
if (
15311534
active_element_css.startswith(selector)
15321535
or active_element_css.endswith(" > div" * 2)
15331536
or (special_form and active_element_css.endswith(" div"))
1537+
or (ctype == "g_rc" and "frame[name" in active_element_css)
15341538
):
15351539
found_checkbox = True
15361540
sb_config._saved_cf_tab_count = tab_count
@@ -1550,6 +1554,7 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
15501554
)
15511555
and hasattr(sb_config, "_saved_cf_tab_count")
15521556
and sb_config._saved_cf_tab_count
1557+
and not __is_cdp_swap_needed(driver)
15531558
):
15541559
driver.uc_open_with_disconnect(driver.current_url, 3.8)
15551560
with suppress(Exception):
@@ -4845,7 +4850,12 @@ def get_local_driver(
48454850
)
48464851
uc_activated = True
48474852
except URLError as e:
4848-
if cert in e.args[0] and IS_MAC:
4853+
if (
4854+
IS_MAC
4855+
and hasattr(e, "args")
4856+
and isinstance(e.args, (list, tuple))
4857+
and cert in e.args[0]
4858+
):
48494859
mac_certificate_error = True
48504860
else:
48514861
raise

seleniumbase/core/sb_cdp.py

Lines changed: 74 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ def click_nth_element(self, selector, number):
268268
if number < 0:
269269
number = 0
270270
element = elements[number]
271+
element.scroll_into_view()
271272
element.click()
272273

273274
def click_nth_visible_element(self, selector, number):
@@ -284,6 +285,7 @@ def click_nth_visible_element(self, selector, number):
284285
if number < 0:
285286
number = 0
286287
element = elements[number]
288+
element.scroll_into_view()
287289
element.click()
288290

289291
def click_link(self, link_text):
@@ -311,6 +313,13 @@ def __click(self, element):
311313
return result
312314

313315
def __flash(self, element, *args, **kwargs):
316+
element.scroll_into_view()
317+
if len(args) < 3 and "x_offset" not in kwargs:
318+
x_offset = self.__get_x_scroll_offset()
319+
kwargs["x_offset"] = x_offset
320+
if len(args) < 3 and "y_offset" not in kwargs:
321+
y_offset = self.__get_y_scroll_offset()
322+
kwargs["y_offset"] = y_offset
314323
return (
315324
self.loop.run_until_complete(
316325
element.flash_async(*args, **kwargs)
@@ -382,9 +391,9 @@ def __save_to_dom(self, element):
382391
)
383392

384393
def __scroll_into_view(self, element):
385-
return (
386-
self.loop.run_until_complete(element.scroll_into_view_async())
387-
)
394+
self.loop.run_until_complete(element.scroll_into_view_async())
395+
self.__add_light_pause()
396+
return None
388397

389398
def __select_option(self, element):
390399
return (
@@ -431,6 +440,18 @@ def __get_js_attributes(self, element):
431440
self.loop.run_until_complete(element.get_js_attributes_async())
432441
)
433442

443+
def __get_x_scroll_offset(self):
444+
x_scroll_offset = self.loop.run_until_complete(
445+
self.page.evaluate("window.pageXOffset")
446+
)
447+
return x_scroll_offset or 0
448+
449+
def __get_y_scroll_offset(self):
450+
y_scroll_offset = self.loop.run_until_complete(
451+
self.page.evaluate("window.pageYOffset")
452+
)
453+
return y_scroll_offset or 0
454+
434455
def tile_windows(self, windows=None, max_columns=0):
435456
"""Tile windows and return the grid of tiled windows."""
436457
driver = self.driver
@@ -504,7 +525,7 @@ def get_active_element_css(self):
504525
def click(self, selector, timeout=settings.SMALL_TIMEOUT):
505526
self.__slow_mode_pause_if_set()
506527
element = self.find_element(selector, timeout=timeout)
507-
self.__add_light_pause()
528+
element.scroll_into_view()
508529
element.click()
509530
self.__slow_mode_pause_if_set()
510531
self.loop.run_until_complete(self.page.wait())
@@ -518,7 +539,9 @@ def click_active_element(self):
518539

519540
def click_if_visible(self, selector):
520541
if self.is_element_visible(selector):
521-
self.find_element(selector).click()
542+
element = self.find_element(selector)
543+
element.scroll_into_view()
544+
element.click()
522545
self.__slow_mode_pause_if_set()
523546
self.loop.run_until_complete(self.page.wait())
524547

@@ -545,9 +568,10 @@ def click_visible_elements(self, selector, limit=0):
545568
except Exception:
546569
continue
547570
if (width != 0 or height != 0):
571+
element.scroll_into_view()
548572
element.click()
549573
click_count += 1
550-
time.sleep(0.044)
574+
time.sleep(0.042)
551575
self.__slow_mode_pause_if_set()
552576
self.loop.run_until_complete(self.page.wait())
553577
except Exception:
@@ -557,7 +581,7 @@ def mouse_click(self, selector, timeout=settings.SMALL_TIMEOUT):
557581
"""(Attempt simulating a mouse click)"""
558582
self.__slow_mode_pause_if_set()
559583
element = self.find_element(selector, timeout=timeout)
560-
self.__add_light_pause()
584+
element.scroll_into_view()
561585
element.mouse_click()
562586
self.__slow_mode_pause_if_set()
563587
self.loop.run_until_complete(self.page.wait())
@@ -579,6 +603,7 @@ def get_nested_element(self, parent_selector, selector):
579603

580604
def select_option_by_text(self, dropdown_selector, option):
581605
element = self.find_element(dropdown_selector)
606+
element.scroll_into_view()
582607
options = element.query_selector_all("option")
583608
for found_option in options:
584609
if found_option.text.strip() == option.strip():
@@ -599,25 +624,33 @@ def flash(
599624
"""Paint a quickly-vanishing dot over an element."""
600625
selector = self.__convert_to_css_if_xpath(selector)
601626
element = self.find_element(selector)
602-
element.flash(duration=duration, color=color)
627+
element.scroll_into_view()
628+
x_offset = self.__get_x_scroll_offset()
629+
y_offset = self.__get_y_scroll_offset()
630+
element.flash(duration, color, x_offset, y_offset)
603631
if pause and isinstance(pause, (int, float)):
604632
time.sleep(pause)
605633

606634
def highlight(self, selector):
607635
"""Highlight an element with multi-colors."""
608636
selector = self.__convert_to_css_if_xpath(selector)
609637
element = self.find_element(selector)
610-
element.flash(0.46, "44CC88")
638+
element.scroll_into_view()
639+
x_offset = self.__get_x_scroll_offset()
640+
y_offset = self.__get_y_scroll_offset()
641+
element.flash(0.46, "44CC88", x_offset, y_offset)
611642
time.sleep(0.15)
612-
element.flash(0.42, "8844CC")
643+
element.flash(0.42, "8844CC", x_offset, y_offset)
613644
time.sleep(0.15)
614-
element.flash(0.38, "CC8844")
645+
element.flash(0.38, "CC8844", x_offset, y_offset)
615646
time.sleep(0.15)
616-
element.flash(0.30, "44CC88")
647+
element.flash(0.30, "44CC88", x_offset, y_offset)
617648
time.sleep(0.30)
618649

619650
def focus(self, selector):
620-
self.find_element(selector).focus()
651+
element = self.find_element(selector)
652+
element.scroll_into_view()
653+
element.focus()
621654

622655
def highlight_overlay(self, selector):
623656
self.find_element(selector).highlight_overlay()
@@ -646,7 +679,7 @@ def remove_elements(self, selector):
646679
def send_keys(self, selector, text, timeout=settings.SMALL_TIMEOUT):
647680
self.__slow_mode_pause_if_set()
648681
element = self.select(selector, timeout=timeout)
649-
self.__add_light_pause()
682+
element.scroll_into_view()
650683
if text.endswith("\n") or text.endswith("\r"):
651684
text = text[:-1] + "\r\n"
652685
element.send_keys(text)
@@ -657,7 +690,7 @@ def press_keys(self, selector, text, timeout=settings.SMALL_TIMEOUT):
657690
"""Similar to send_keys(), but presses keys at human speed."""
658691
self.__slow_mode_pause_if_set()
659692
element = self.select(selector, timeout=timeout)
660-
self.__add_light_pause()
693+
element.scroll_into_view()
661694
submit = False
662695
if text.endswith("\n") or text.endswith("\r"):
663696
submit = True
@@ -675,7 +708,7 @@ def type(self, selector, text, timeout=settings.SMALL_TIMEOUT):
675708
"""Similar to send_keys(), but clears the text field first."""
676709
self.__slow_mode_pause_if_set()
677710
element = self.select(selector, timeout=timeout)
678-
self.__add_light_pause()
711+
element.scroll_into_view()
679712
with suppress(Exception):
680713
element.clear_input()
681714
if text.endswith("\n") or text.endswith("\r"):
@@ -688,8 +721,8 @@ def set_value(self, selector, text, timeout=settings.SMALL_TIMEOUT):
688721
"""Similar to send_keys(), but clears the text field first."""
689722
self.__slow_mode_pause_if_set()
690723
selector = self.__convert_to_css_if_xpath(selector)
691-
self.select(selector, timeout=timeout)
692-
self.__add_light_pause()
724+
element = self.select(selector, timeout=timeout)
725+
element.scroll_into_view()
693726
press_enter = False
694727
if text.endswith("\n"):
695728
text = text[:-1]
@@ -1655,38 +1688,52 @@ def assert_url_contains(self, substring):
16551688
raise Exception(error % (expected, actual))
16561689

16571690
def assert_text(
1658-
self, text, selector="html", timeout=settings.SMALL_TIMEOUT
1691+
self, text, selector="body", timeout=settings.SMALL_TIMEOUT
16591692
):
1693+
start_ms = time.time() * 1000.0
1694+
stop_ms = start_ms + (timeout * 1000.0)
16601695
text = text.strip()
16611696
element = None
16621697
try:
16631698
element = self.find_element(selector, timeout=timeout)
16641699
except Exception:
16651700
raise Exception("Element {%s} not found!" % selector)
1666-
for i in range(30):
1701+
for i in range(int(timeout * 10)):
1702+
with suppress(Exception):
1703+
element = self.find_element(selector, timeout=0.1)
16671704
if text in element.text_all:
16681705
return True
1706+
now_ms = time.time() * 1000.0
1707+
if now_ms >= stop_ms:
1708+
break
16691709
time.sleep(0.1)
16701710
raise Exception(
16711711
"Text {%s} not found in {%s}! Actual text: {%s}"
16721712
% (text, selector, element.text_all)
16731713
)
16741714

16751715
def assert_exact_text(
1676-
self, text, selector="html", timeout=settings.SMALL_TIMEOUT
1716+
self, text, selector="body", timeout=settings.SMALL_TIMEOUT
16771717
):
1718+
start_ms = time.time() * 1000.0
1719+
stop_ms = start_ms + (timeout * 1000.0)
16781720
text = text.strip()
16791721
element = None
16801722
try:
16811723
element = self.select(selector, timeout=timeout)
16821724
except Exception:
16831725
raise Exception("Element {%s} not found!" % selector)
1684-
for i in range(30):
1726+
for i in range(int(timeout * 10)):
1727+
with suppress(Exception):
1728+
element = self.select(selector, timeout=0.1)
16851729
if (
16861730
self.is_element_visible(selector)
16871731
and text.strip() == element.text_all.strip()
16881732
):
16891733
return True
1734+
now_ms = time.time() * 1000.0
1735+
if now_ms >= stop_ms:
1736+
break
16901737
time.sleep(0.1)
16911738
raise Exception(
16921739
"Expected Text {%s}, is not equal to {%s} in {%s}!"
@@ -1727,26 +1774,31 @@ def scroll_to_y(self, y):
17271774
with suppress(Exception):
17281775
self.loop.run_until_complete(self.page.evaluate(js_code))
17291776
self.loop.run_until_complete(self.page.wait())
1777+
self.__add_light_pause()
17301778

17311779
def scroll_to_top(self):
17321780
js_code = "window.scrollTo(0, 0);"
17331781
with suppress(Exception):
17341782
self.loop.run_until_complete(self.page.evaluate(js_code))
17351783
self.loop.run_until_complete(self.page.wait())
1784+
self.__add_light_pause()
17361785

17371786
def scroll_to_bottom(self):
17381787
js_code = "window.scrollTo(0, 10000);"
17391788
with suppress(Exception):
17401789
self.loop.run_until_complete(self.page.evaluate(js_code))
17411790
self.loop.run_until_complete(self.page.wait())
1791+
self.__add_light_pause()
17421792

17431793
def scroll_up(self, amount=25):
17441794
self.loop.run_until_complete(self.page.scroll_up(amount))
17451795
self.loop.run_until_complete(self.page.wait())
1796+
self.__add_light_pause()
17461797

17471798
def scroll_down(self, amount=25):
17481799
self.loop.run_until_complete(self.page.scroll_down(amount))
17491800
self.loop.run_until_complete(self.page.wait())
1801+
self.__add_light_pause()
17501802

17511803
def save_screenshot(self, name, folder=None, selector=None):
17521804
filename = name

0 commit comments

Comments
 (0)