Skip to content

Commit 004a364

Browse files
authored
Merge pull request #1736 from seleniumbase/uc-mode-and-window-switching-updates
Update UC Mode and window-switching code
2 parents 5148599 + dd262db commit 004a364

File tree

11 files changed

+82
-26
lines changed

11 files changed

+82
-26
lines changed

examples/verify_undetected.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
if __name__ == "__main__":
77
from pytest import main
8-
main([__file__, "--uc", "--incognito", "-s"])
8+
main([__file__, "--uc", "--incognito", "-s", "--uc-cdp-events"])
99

1010

1111
class UndetectedTest(BaseCase):

mkdocs_build/requirements.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ Markdown==3.3.7
1111
MarkupSafe==2.1.2
1212
Jinja2==3.1.2
1313
click==8.1.3
14-
zipp==3.12.0
1514
ghp-import==2.1.0
1615
readme-renderer==37.3
1716
pymdown-extensions==9.9.2
@@ -29,6 +28,6 @@ tinycss2==1.2.1
2928
defusedxml==0.7.1
3029
mkdocs==1.4.2
3130
mkdocs-material==9.0.11
32-
mkdocs-exclude-search==0.6.4
31+
mkdocs-exclude-search==0.6.5
3332
mkdocs-simple-hooks==0.1.5
3433
mkdocs-material-extensions==1.1.1

requirements.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ filelock>=3.9.0;python_version>="3.7"
2020
platformdirs>=2.4.0;python_version<"3.7"
2121
platformdirs>=2.6.2;python_version>="3.7"
2222
pyparsing>=3.0.7;python_version<"3.7"
23+
zipp==3.6.0;python_version<"3.7"
24+
zipp>=3.12.1;python_version>="3.7"
2325
six==1.16.0
2426
idna==3.4
2527
chardet==4.0.0;python_version<"3.7"
@@ -67,7 +69,7 @@ pytest-rerunfailures==11.0;python_version>="3.7"
6769
pytest-xdist==2.5.0;python_version<"3.7"
6870
pytest-xdist==3.1.0;python_version>="3.7"
6971
parameterized==0.8.1
70-
sbvirtualdisplay==1.1.1
72+
sbvirtualdisplay==1.2.0
7173
behave==1.2.6
7274
parse==1.19.0
7375
parse-type==0.6.0

seleniumbase/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# seleniumbase package
2-
__version__ = "4.12.5"
2+
__version__ = "4.12.6"

seleniumbase/core/browser_launcher.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,7 @@ def _set_chrome_options(
734734
or IS_LINUX # switches to Xvfb (non-headless)
735735
)
736736
):
737+
chrome_options.add_argument("--disable-popup-blocking")
737738
# Skip remaining options that trigger anti-bot services
738739
return chrome_options
739740
chrome_options.add_argument("--test-type")

seleniumbase/fixtures/base_case.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2634,7 +2634,19 @@ def __element_click(self, element):
26342634
):
26352635
element.click()
26362636
else:
2637-
element.uc_click()
2637+
try:
2638+
href = element.get_attribute("href")
2639+
target = element.get_attribute("target")
2640+
if len(href) > 0 and target == "_blank":
2641+
self.driver.tab_new(href)
2642+
self.switch_to_window(-1)
2643+
time.sleep(0.01)
2644+
elif len(href) > 0:
2645+
element.uc_click()
2646+
else:
2647+
element.click()
2648+
except Exception:
2649+
element.click()
26382650

26392651
def __select_option(
26402652
self,
@@ -3344,7 +3356,7 @@ def open_new_window(self, switch_to=True):
33443356
if hasattr(self.driver, "tab_new"):
33453357
self.driver.tab_new("about:blank")
33463358
if switch_to:
3347-
self.switch_to_newest_window()
3359+
self.switch_to_window(-1)
33483360
time.sleep(0.01)
33493361
return
33503362
if selenium4_or_newer and switch_to:
@@ -3375,7 +3387,7 @@ def switch_to_default_window(self):
33753387
self.switch_to_window(0)
33763388

33773389
def switch_to_newest_window(self):
3378-
self.switch_to_window(len(self.driver.window_handles) - 1)
3390+
self.switch_to_window(-1)
33793391

33803392
def get_new_driver(
33813393
self,
@@ -12471,7 +12483,7 @@ def __make_css_match_first_element_only(self, selector):
1247112483
def __switch_to_newest_window_if_not_blank(self):
1247212484
current_window = self.driver.current_window_handle
1247312485
try:
12474-
self.switch_to_window(len(self.driver.window_handles) - 1)
12486+
self.switch_to_window(-1)
1247512487
if self.get_current_url() == "about:blank":
1247612488
self.switch_to_window(current_window)
1247712489
except Exception:
@@ -13603,9 +13615,7 @@ def setUp(self, masterqa_mode=False):
1360313615
has_url = True
1360413616
if len(self.driver.window_handles) > 1:
1360513617
while len(self.driver.window_handles) > 1:
13606-
self.switch_to_window(
13607-
len(self.driver.window_handles) - 1
13608-
)
13618+
self.switch_to_window(-1)
1360913619
self.driver.close()
1361013620
self.switch_to_window(0)
1361113621
if self._crumbs:

seleniumbase/fixtures/page_actions.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1328,16 +1328,17 @@ def switch_to_window(driver, window, timeout=settings.SMALL_TIMEOUT):
13281328
window - the window index or window handle
13291329
timeout - the time to wait for the window in seconds
13301330
"""
1331+
if window == -1:
1332+
window = len(driver.window_handles) - 1
13311333
start_ms = time.time() * 1000.0
13321334
stop_ms = start_ms + (timeout * 1000.0)
13331335
if isinstance(window, int):
13341336
caps = driver.capabilities
13351337
if (
13361338
caps["browserName"].lower() == "safari"
13371339
and "safari:platformVersion" in caps
1338-
and caps["safari:platformVersion"].split(".") < ["10", "15"]
13391340
):
1340-
# Fix reversed window_handles on Safari 10.14 or lower
1341+
# Reversed window_handles on Safari
13411342
window = len(driver.window_handles) - 1 - window
13421343
if window < 0:
13431344
window = 0

seleniumbase/undetected/__init__.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import selenium.webdriver.chrome.webdriver
1010
import selenium.webdriver.common.service
1111
import selenium.webdriver.remote.command
12+
from .cdp import CDP
13+
from .cdp import PageElement
1214
from .dprocess import start_detached
1315
from .options import ChromeOptions
1416
from .patcher import IS_POSIX
@@ -133,13 +135,12 @@ def __init__(
133135
constants.MultiBrowser.DRIVER_FIXING_LOCK
134136
)
135137
with uc_lock:
136-
patcher = Patcher(
138+
self.patcher = Patcher(
137139
executable_path=driver_executable_path,
138140
force=patcher_force_close,
139141
version_main=version_main,
140142
)
141-
patcher.auto()
142-
self.patcher = patcher
143+
self.patcher.auto()
143144
if not options:
144145
options = ChromeOptions()
145146
try:
@@ -280,7 +281,7 @@ def __init__(
280281
service_ = None
281282
if patch_driver:
282283
service_ = selenium.webdriver.chrome.service.Service(
283-
executable_path=patcher.executable_path,
284+
executable_path=self.patcher.executable_path,
284285
log_path=os.devnull,
285286
)
286287
else:
@@ -380,19 +381,28 @@ def clear_cdp_listeners(self):
380381
if self.reactor and isinstance(self.reactor, Reactor):
381382
self.reactor.handlers.clear()
382383

383-
def window_new(self):
384+
def window_new(self, url=None):
384385
self.execute(
385386
selenium.webdriver.remote.command.Command.NEW_WINDOW,
386387
{"type": "window"},
387388
)
389+
if url:
390+
self.remove_cdc_props_as_needed()
391+
return super().get(url)
392+
return None
388393

389394
def tab_new(self, url):
390395
"""This opens a url in a new tab."""
391396
if not hasattr(self, "cdp"):
392-
from .cdp import CDP
397+
self.cdp = CDP(self.options)
398+
self.cdp.tab_new(str(url))
393399

394-
cdp = CDP(self.options)
395-
cdp.tab_new(str(url))
400+
def tab_list(self):
401+
if not hasattr(self, "cdp"):
402+
self.cdp = CDP(self.options)
403+
404+
retval = self.get(self.cdp.endpoints["list"])
405+
return [PageElement(o) for o in retval]
396406

397407
def reconnect(self, timeout=0.1):
398408
try:

seleniumbase/undetected/cdp.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ async def send(self, method, params): # noqa
8989
self.log.info(self._last_json)
9090

9191
def get(self, uri):
92+
from urllib.parse import unquote
93+
94+
uri = unquote(uri, errors="strict")
9295
resp = self._session.get(self.server_addr + uri)
9396
try:
9497
self._last_resp = resp
@@ -99,6 +102,9 @@ def get(self, uri):
99102
return self._last_json
100103

101104
def post(self, uri, data=None):
105+
from urllib.parse import unquote
106+
107+
uri = unquote(uri, errors="strict")
102108
if not data:
103109
data = {}
104110
resp = self._session.post(self.server_addr + uri, json=data)

seleniumbase/undetected/patcher.py

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,9 @@ def __init__(self, executable_path=None, force=False, version_main=0):
4747
version_main: 0 = auto
4848
Specify main chrome version (rounded, ex: 82)
4949
"""
50-
import secrets
51-
5250
self.force = force
5351
self.executable_path = None
54-
prefix = secrets.token_hex(8)
52+
prefix = "undetected"
5553
if not os.path.exists(self.data_path):
5654
os.makedirs(self.data_path, exist_ok=True)
5755
if not executable_path:
@@ -222,6 +220,33 @@ def gen_call_function_js_cache_name(match):
222220
fh.write(file_bin)
223221
return True
224222

223+
def is_binary_patched_new(self, executable_path=None):
224+
executable_path = executable_path or self.executable_path
225+
with io.open(executable_path, "rb") as fh:
226+
return fh.read().find(b"undetected chromedriver") != -1
227+
228+
def patch_exe_new(self):
229+
logger.info("patching driver executable %s" % self.executable_path)
230+
with io.open(self.executable_path, "r+b") as fh:
231+
content = fh.read()
232+
match_injected_codeblock = re.search(rb"{window.*;}", content)
233+
if match_injected_codeblock:
234+
target_bytes = match_injected_codeblock[0]
235+
new_target_bytes = (
236+
b'{console.log("undetected chromedriver 1337!")}'.ljust(
237+
len(target_bytes), b" "
238+
)
239+
)
240+
new_content = content.replace(target_bytes, new_target_bytes)
241+
if new_content == content:
242+
pass # Failure to patch driver
243+
else:
244+
# Patch now
245+
fh.seek(0)
246+
fh.write(new_content)
247+
else:
248+
pass # Already patched
249+
225250
def __repr__(self):
226251
return "{0:s}({1:s})".format(
227252
self.__class__.__name__,

0 commit comments

Comments
 (0)