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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. seleniumbase/__version__.py +1 -1
  2. seleniumbase/behave/behave_sb.py +14 -0
  3. seleniumbase/console_scripts/run.py +1 -0
  4. seleniumbase/console_scripts/sb_install.py +142 -11
  5. seleniumbase/console_scripts/sb_mkdir.py +76 -0
  6. seleniumbase/console_scripts/sb_mkrec.py +25 -0
  7. seleniumbase/console_scripts/sb_recorder.py +40 -3
  8. seleniumbase/core/browser_launcher.py +279 -117
  9. seleniumbase/core/detect_b_ver.py +8 -6
  10. seleniumbase/core/log_helper.py +11 -16
  11. seleniumbase/core/mysql.py +1 -1
  12. seleniumbase/core/report_helper.py +3 -7
  13. seleniumbase/core/sb_cdp.py +275 -78
  14. seleniumbase/core/sb_driver.py +36 -5
  15. seleniumbase/core/session_helper.py +2 -4
  16. seleniumbase/drivers/chromium_drivers/__init__.py +0 -0
  17. seleniumbase/fixtures/base_case.py +251 -201
  18. seleniumbase/fixtures/constants.py +1 -0
  19. seleniumbase/fixtures/js_utils.py +52 -14
  20. seleniumbase/fixtures/page_actions.py +18 -7
  21. seleniumbase/fixtures/page_utils.py +4 -2
  22. seleniumbase/fixtures/shared_utils.py +2 -4
  23. seleniumbase/masterqa/master_qa.py +16 -2
  24. seleniumbase/plugins/base_plugin.py +8 -0
  25. seleniumbase/plugins/driver_manager.py +15 -5
  26. seleniumbase/plugins/pytest_plugin.py +43 -57
  27. seleniumbase/plugins/sb_manager.py +23 -19
  28. seleniumbase/plugins/selenium_plugin.py +20 -13
  29. seleniumbase/undetected/__init__.py +11 -10
  30. seleniumbase/undetected/cdp.py +1 -12
  31. seleniumbase/undetected/cdp_driver/browser.py +330 -128
  32. seleniumbase/undetected/cdp_driver/cdp_util.py +48 -14
  33. seleniumbase/undetected/cdp_driver/config.py +78 -11
  34. seleniumbase/undetected/cdp_driver/connection.py +15 -43
  35. seleniumbase/undetected/cdp_driver/element.py +37 -22
  36. seleniumbase/undetected/cdp_driver/tab.py +414 -39
  37. {seleniumbase-4.44.2.dist-info → seleniumbase-4.45.10.dist-info}/METADATA +140 -152
  38. {seleniumbase-4.44.2.dist-info → seleniumbase-4.45.10.dist-info}/RECORD +42 -41
  39. {seleniumbase-4.44.2.dist-info → seleniumbase-4.45.10.dist-info}/licenses/LICENSE +1 -1
  40. {seleniumbase-4.44.2.dist-info → seleniumbase-4.45.10.dist-info}/WHEEL +0 -0
  41. {seleniumbase-4.44.2.dist-info → seleniumbase-4.45.10.dist-info}/entry_points.txt +0 -0
  42. {seleniumbase-4.44.2.dist-info → seleniumbase-4.45.10.dist-info}/top_level.txt +0 -0
@@ -51,12 +51,27 @@ def __activate_virtual_display_as_needed(
51
51
  headless, headed, xvfb, xvfb_metrics
52
52
  ):
53
53
  """This is only needed on Linux."""
54
+ reset_virtual_display = False
55
+ if IS_LINUX and (not headed or xvfb):
56
+ if (
57
+ not hasattr(sb_config, "_closed_connection_ids")
58
+ or not isinstance(sb_config._closed_connection_ids, list)
59
+ ):
60
+ sb_config._closed_connection_ids = []
61
+ if (
62
+ not hasattr(sb_config, "_xvfb_users")
63
+ or not isinstance(sb_config._xvfb_users, int)
64
+ ):
65
+ reset_virtual_display = True
66
+ sb_config._xvfb_users = 0
67
+ sb_config._xvfb_users += 1
54
68
  if (
55
69
  IS_LINUX
56
70
  and (not headed or xvfb)
57
71
  and (
58
72
  not hasattr(sb_config, "_virtual_display")
59
73
  or not sb_config._virtual_display
74
+ or reset_virtual_display
60
75
  )
61
76
  ):
62
77
  from sbvirtualdisplay import Display
@@ -102,7 +117,8 @@ def __activate_virtual_display_as_needed(
102
117
  _xvfb_display.start()
103
118
  if "DISPLAY" not in os.environ.keys():
104
119
  print(
105
- "\nX11 display failed! Will use regular xvfb!"
120
+ "\n X11 display failed! Is Xvfb installed? "
121
+ "\n Try this: `sudo apt install -y xvfb`"
106
122
  )
107
123
  __activate_standard_virtual_display()
108
124
  else:
@@ -280,7 +296,10 @@ async def start(
280
296
  proxy: Optional[str] = None, # "host:port" or "user:pass@host:port"
281
297
  tzone: Optional[str] = None, # Eg "America/New_York", "Asia/Kolkata"
282
298
  geoloc: Optional[list | tuple] = None, # Eg (48.87645, 2.26340)
299
+ mobile: Optional[bool] = None, # Use Mobile Mode with default args
300
+ disable_csp: Optional[str] = None, # Disable content security policy
283
301
  extension_dir: Optional[str] = None, # Chrome extension directory
302
+ use_chromium: Optional[str] = None, # Use the base Chromium browser
284
303
  **kwargs: Optional[dict],
285
304
  ) -> Browser:
286
305
  """
@@ -351,11 +370,25 @@ async def start(
351
370
  guest = True
352
371
  else:
353
372
  guest = False
373
+ if mobile is None:
374
+ if "--mobile" in sys_argv:
375
+ mobile = True
376
+ else:
377
+ mobile = False
378
+ if mobile:
379
+ sb_config._cdp_mobile_mode = True
380
+ else:
381
+ sb_config._cdp_mobile_mode = False
354
382
  if ad_block is None:
355
383
  if "--ad-block" in sys_argv or "--ad_block" in sys_argv:
356
384
  ad_block = True
357
385
  else:
358
386
  ad_block = False
387
+ if disable_csp is None:
388
+ if "--disable-csp" in sys_argv or "--disable_csp" in sys_argv:
389
+ disable_csp = True
390
+ else:
391
+ disable_csp = False
359
392
  if xvfb_metrics is None and "--xvfb-metrics" in arg_join:
360
393
  x_m = xvfb_metrics
361
394
  count = 0
@@ -482,6 +515,8 @@ async def start(
482
515
  print(" Using default Chrome browser instead!")
483
516
  bin_loc = None
484
517
  browser_executable_path = bin_loc
518
+ elif use_chromium or "--use-chromium" in arg_join:
519
+ browser_executable_path = "_chromium_"
485
520
  if proxy is None and "--proxy" in arg_join:
486
521
  proxy_string = None
487
522
  if "--proxy=" in arg_join:
@@ -530,7 +565,11 @@ async def start(
530
565
  platform_var = platform_var[1:-1]
531
566
  if IS_LINUX and not headless and not headed and not xvfb:
532
567
  xvfb = True # The default setting on Linux
533
- __activate_virtual_display_as_needed(headless, headed, xvfb, xvfb_metrics)
568
+ if not host or not port:
569
+ # The browser hasn't been launched yet. (May need a virtual display)
570
+ __activate_virtual_display_as_needed(
571
+ headless, headed, xvfb, xvfb_metrics
572
+ )
534
573
  if proxy and "@" in str(proxy):
535
574
  user_with_pass = proxy.split("@")[0]
536
575
  if ":" in user_with_pass:
@@ -548,12 +587,15 @@ async def start(
548
587
  proxy_scheme,
549
588
  )
550
589
  if ad_block:
590
+ sb_config.ad_block_on = True
551
591
  incognito = False
552
592
  guest = False
553
593
  ad_block_zip = AD_BLOCK_ZIP_PATH
554
594
  ad_block_dir = os.path.join(DOWNLOADS_FOLDER, "ad_block")
555
595
  __unzip_to_new_folder(ad_block_zip, ad_block_dir)
556
596
  extension_dir = __add_chrome_ext_dir(extension_dir, ad_block_dir)
597
+ if disable_csp:
598
+ sb_config.disable_csp = True
557
599
  if "binary_location" in kwargs and not browser_executable_path:
558
600
  browser_executable_path = kwargs["binary_location"]
559
601
  if not browser_executable_path:
@@ -607,6 +649,8 @@ async def start(
607
649
  sb_config._cdp_browser = "atlas"
608
650
  else:
609
651
  sb_config._cdp_browser = "chrome"
652
+ sb_config.incognito = incognito
653
+ sb_config.guest_mode = guest
610
654
  if not config:
611
655
  config = Config(
612
656
  user_data_dir,
@@ -666,6 +710,8 @@ async def start(
666
710
  sb_config._cdp_platform = platform_var
667
711
  else:
668
712
  sb_config._cdp_platform = None
713
+ driver.page = driver.main_tab
714
+ driver.solve_captcha = driver.page.solve_captcha
669
715
  return driver
670
716
 
671
717
 
@@ -695,18 +741,6 @@ def start_sync(*args, **kwargs) -> Browser:
695
741
  loop = kwargs["loop"]
696
742
  else:
697
743
  loop = asyncio.new_event_loop()
698
- if "user_data_dir" in kwargs and kwargs["user_data_dir"]:
699
- headless = False
700
- if "headless" in kwargs:
701
- headless = kwargs["headless"]
702
- decoy_args = kwargs
703
- decoy_args["headless"] = True
704
- driver = loop.run_until_complete(start(**decoy_args))
705
- kwargs["headless"] = headless
706
- kwargs["user_data_dir"] = driver.config.user_data_dir
707
- time.sleep(0.2)
708
- driver.stop() # Due to Chrome-130, must stop & start
709
- time.sleep(0.1)
710
744
  return loop.run_until_complete(start(*args, **kwargs))
711
745
 
712
746
 
@@ -7,6 +7,9 @@ import tempfile
7
7
  import zipfile
8
8
  from contextlib import suppress
9
9
  from seleniumbase.config import settings
10
+ from seleniumbase.drivers import chromium_drivers
11
+ from seleniumbase.fixtures import constants
12
+ from seleniumbase.fixtures import shared_utils
10
13
  from typing import Union, List, Optional
11
14
 
12
15
  __all__ = [
@@ -23,6 +26,12 @@ is_posix = sys.platform.startswith(("darwin", "cygwin", "linux", "linux2"))
23
26
 
24
27
  PathLike = Union[str, pathlib.Path]
25
28
  AUTO = None
29
+ IS_MAC = shared_utils.is_mac()
30
+ IS_LINUX = shared_utils.is_linux()
31
+ IS_WINDOWS = shared_utils.is_windows()
32
+ CHROMIUM_DIR = os.path.dirname(
33
+ os.path.realpath(chromium_drivers.__file__)
34
+ )
26
35
 
27
36
 
28
37
  class Config:
@@ -79,7 +88,8 @@ class Config:
79
88
  if not browser_args:
80
89
  browser_args = []
81
90
  if not user_data_dir:
82
- self._user_data_dir = temp_profile_dir()
91
+ self.user_data_dir = temp_profile_dir()
92
+ self._user_data_dir = self.user_data_dir
83
93
  self._custom_data_dir = False
84
94
  else:
85
95
  self.user_data_dir = user_data_dir
@@ -91,8 +101,51 @@ class Config:
91
101
  os.makedirs(profile)
92
102
  with open(preferences_file, "w") as f:
93
103
  f.write(preferences)
104
+ mock_keychain = False
94
105
  if not browser_executable_path:
95
106
  browser_executable_path = find_chrome_executable()
107
+ elif browser_executable_path == "_chromium_":
108
+ from filelock import FileLock
109
+ binary_folder = None
110
+ if IS_MAC:
111
+ binary_folder = "chrome-mac"
112
+ elif IS_LINUX:
113
+ binary_folder = "chrome-linux"
114
+ elif IS_WINDOWS:
115
+ binary_folder = "chrome-win"
116
+ binary_location = os.path.join(CHROMIUM_DIR, binary_folder)
117
+ gui_lock = FileLock(constants.MultiBrowser.DRIVER_FIXING_LOCK)
118
+ with gui_lock:
119
+ with suppress(Exception):
120
+ shared_utils.make_writable(
121
+ constants.MultiBrowser.DRIVER_FIXING_LOCK
122
+ )
123
+ if not os.path.exists(binary_location):
124
+ from seleniumbase.console_scripts import sb_install
125
+ sys_args = sys.argv # Save a copy of sys args
126
+ sb_install.log_d("\nWarning: Chromium binary not found...")
127
+ sb_install.main(override="chromium")
128
+ sys.argv = sys_args # Put back original args
129
+ binary_name = binary_location.split("/")[-1].split("\\")[-1]
130
+ if binary_name in ["chrome-mac"]:
131
+ binary_name = "Chromium"
132
+ binary_location += "/Chromium.app"
133
+ binary_location += "/Contents/MacOS/Chromium"
134
+ elif binary_name == "chrome-linux":
135
+ binary_name = "chrome"
136
+ binary_location += "/chrome"
137
+ elif binary_name in ["chrome-win"]:
138
+ binary_name = "chrome.exe"
139
+ binary_location += "\\chrome.exe"
140
+ if os.path.exists(binary_location):
141
+ mock_keychain = True
142
+ browser_executable_path = binary_location
143
+ else:
144
+ print(
145
+ f"{binary_location} not found. "
146
+ f"Defaulting to regular Chrome!"
147
+ )
148
+ browser_executable_path = find_chrome_executable()
96
149
  self._browser_args = browser_args
97
150
  self.browser_executable_path = browser_executable_path
98
151
  self.headless = headless
@@ -122,35 +175,43 @@ class Config:
122
175
  self._default_browser_args = [
123
176
  "--window-size=%s,%s" % (start_width, start_height),
124
177
  "--window-position=%s,%s" % (start_x, start_y),
125
- "--remote-allow-origins=*",
126
178
  "--no-first-run",
127
179
  "--no-service-autorun",
128
180
  "--disable-auto-reload",
129
181
  "--no-default-browser-check",
130
182
  "--homepage=about:blank",
131
183
  "--no-pings",
184
+ "--enable-unsafe-extension-debugging",
132
185
  "--wm-window-animations-disabled",
133
186
  "--animation-duration-scale=0",
134
187
  "--enable-privacy-sandbox-ads-apis",
135
188
  "--safebrowsing-disable-download-protection",
136
189
  '--simulate-outdated-no-au="Tue, 31 Dec 2099 23:59:59 GMT"',
190
+ "--test-type",
191
+ "--ash-no-nudges",
137
192
  "--password-store=basic",
138
193
  "--deny-permission-prompts",
139
- "--disable-infobars",
140
194
  "--disable-breakpad",
195
+ "--disable-setuid-sandbox",
141
196
  "--disable-prompt-on-repost",
197
+ "--disable-application-cache",
142
198
  "--disable-password-generation",
199
+ "--disable-save-password-bubble",
200
+ "--disable-single-click-autofill",
143
201
  "--disable-ipc-flooding-protection",
144
202
  "--disable-background-timer-throttling",
145
203
  "--disable-search-engine-choice-screen",
146
204
  "--disable-backgrounding-occluded-windows",
147
205
  "--disable-client-side-phishing-detection",
206
+ "--disable-device-discovery-notifications",
148
207
  "--disable-top-sites",
149
208
  "--disable-translate",
209
+ "--dns-prefetch-disable",
150
210
  "--disable-renderer-backgrounding",
151
- "--disable-background-networking",
152
211
  "--disable-dev-shm-usage",
153
212
  ]
213
+ if mock_keychain:
214
+ self._default_browser_args.append("--use-mock-keychain")
154
215
 
155
216
  @property
156
217
  def browser_args(self):
@@ -203,11 +264,10 @@ class Config:
203
264
  "OptimizationTargetPrediction,OptimizationGuideModelDownloading,"
204
265
  "SidePanelPinning,UserAgentClientHint,PrivacySandboxSettings4,"
205
266
  "OptimizationHintsFetching,InterestFeedContentSuggestions,"
206
- "DisableLoadExtensionCommandLineSwitch"
267
+ "Bluetooth,WebBluetooth,UnifiedWebBluetooth,ComponentUpdater,"
268
+ "DisableLoadExtensionCommandLineSwitch,"
269
+ "WebAuthentication,PasskeyAuth"
207
270
  ]
208
- if self.proxy:
209
- args += ["--test-type"]
210
- args += ["--disable-session-crashed-bubble"]
211
271
  if self.expert:
212
272
  args += [
213
273
  "--disable-web-security",
@@ -315,10 +375,13 @@ def find_chrome_executable(return_all=False):
315
375
  for item in os.environ.get("PATH").split(os.pathsep):
316
376
  for subitem in (
317
377
  "google-chrome",
378
+ "google-chrome-stable",
379
+ "google-chrome-beta",
380
+ "google-chrome-dev",
381
+ "google-chrome-unstable",
382
+ "chrome",
318
383
  "chromium",
319
384
  "chromium-browser",
320
- "chrome",
321
- "google-chrome-stable",
322
385
  ):
323
386
  candidates.append(os.sep.join((item, subitem)))
324
387
  if "darwin" in sys.platform:
@@ -347,7 +410,11 @@ def find_chrome_executable(return_all=False):
347
410
  )
348
411
  rv = []
349
412
  for candidate in candidates:
350
- if os.path.exists(candidate) and os.access(candidate, os.X_OK):
413
+ if (
414
+ os.path.exists(candidate)
415
+ and os.access(candidate, os.R_OK)
416
+ and os.access(candidate, os.X_OK)
417
+ ):
351
418
  logger.debug("%s is a valid candidate... " % candidate)
352
419
  rv.append(candidate)
353
420
  else:
@@ -184,13 +184,13 @@ class Connection(metaclass=CantTouchThis):
184
184
  self,
185
185
  websocket_url=None,
186
186
  target=None,
187
- _owner=None,
187
+ browser=None,
188
188
  **kwargs,
189
189
  ):
190
190
  super().__init__()
191
191
  self._target = target
192
192
  self.__count__ = itertools.count(0)
193
- self._owner = _owner
193
+ self.browser = browser
194
194
  self.websocket_url: str = websocket_url
195
195
  self.websocket = None
196
196
  self.mapper = {}
@@ -370,8 +370,8 @@ class Connection(metaclass=CantTouchThis):
370
370
 
371
371
  async def set_geolocation(self, geolocation: Optional[tuple] = None):
372
372
  """Sets the User Agent via set_geolocation_override."""
373
- await self.send(cdp.browser.grant_permissions(
374
- permissions=["geolocation"],
373
+ await self.send(cdp.browser.set_permission(
374
+ permission={"name": "geolocation"}, setting="granted"
375
375
  ))
376
376
  await self.send(cdp.emulation.set_geolocation_override(
377
377
  latitude=geolocation[0],
@@ -379,36 +379,6 @@ class Connection(metaclass=CantTouchThis):
379
379
  accuracy=100,
380
380
  ))
381
381
 
382
- async def set_auth(self, username, password, tab):
383
- async def auth_challenge_handler(event: cdp.fetch.AuthRequired):
384
- await tab.send(
385
- cdp.fetch.continue_with_auth(
386
- request_id=event.request_id,
387
- auth_challenge_response=cdp.fetch.AuthChallengeResponse(
388
- response="ProvideCredentials",
389
- username=username,
390
- password=password,
391
- ),
392
- )
393
- )
394
-
395
- async def req_paused(event: cdp.fetch.RequestPaused):
396
- await tab.send(
397
- cdp.fetch.continue_request(request_id=event.request_id)
398
- )
399
-
400
- tab.add_handler(
401
- cdp.fetch.RequestPaused,
402
- lambda event: asyncio.create_task(req_paused(event)),
403
- )
404
-
405
- tab.add_handler(
406
- cdp.fetch.AuthRequired,
407
- lambda event: asyncio.create_task(auth_challenge_handler(event)),
408
- )
409
-
410
- await tab.send(cdp.fetch.enable(handle_auth_requests=True))
411
-
412
382
  def __getattr__(self, item):
413
383
  """:meta private:"""
414
384
  try:
@@ -456,8 +426,8 @@ class Connection(metaclass=CantTouchThis):
456
426
  await self.aopen()
457
427
  if not self.websocket or self.websocket.state is State.CLOSED:
458
428
  return
459
- if self._owner:
460
- browser = self._owner
429
+ if self.browser:
430
+ browser = self.browser
461
431
  if browser.config:
462
432
  if browser.config.expert:
463
433
  await self._prepare_expert()
@@ -640,11 +610,11 @@ class Listener:
640
610
  # Probably an event
641
611
  try:
642
612
  event = cdp.util.parse_json_event(message)
643
- event_tx = EventTransaction(event)
644
- if not self.connection.mapper:
645
- self.connection.__count__ = itertools.count(0)
646
- event_tx.id = next(self.connection.__count__)
647
- self.connection.mapper[event_tx.id] = event_tx
613
+ # event_tx = EventTransaction(event)
614
+ # if not self.connection.mapper:
615
+ # self.connection.__count__ = itertools.count(0)
616
+ # event_tx.id = next(self.connection.__count__)
617
+ # self.connection.mapper[event_tx.id] = event_tx
648
618
  except Exception as e:
649
619
  logger.info(
650
620
  "%s: %s during parsing of json from event : %s"
@@ -669,9 +639,11 @@ class Listener:
669
639
  or inspect.iscoroutine(callback)
670
640
  ):
671
641
  try:
672
- await callback(event, self.connection)
642
+ asyncio.create_task(
643
+ callback(event, self.connection)
644
+ )
673
645
  except TypeError:
674
- await callback(event)
646
+ asyncio.create_task(callback(event))
675
647
  else:
676
648
  try:
677
649
  callback(event, self.connection)
@@ -372,7 +372,10 @@ class Element:
372
372
  arguments = [cdp.runtime.CallArgument(
373
373
  object_id=self._remote_object.object_id
374
374
  )]
375
- await self.flash_async(0.25)
375
+ script = 'sessionStorage.getItem("pxsid") !== null;'
376
+ using_px = await self.tab.evaluate(script)
377
+ if not using_px:
378
+ await self.flash_async(0.25)
376
379
  await self._tab.send(
377
380
  cdp.runtime.call_function_on(
378
381
  "(el) => el.click()",
@@ -501,8 +504,11 @@ class Element:
501
504
  logger.warning("Could not calculate box model for %s", self)
502
505
  return
503
506
  logger.debug("Clicking on location: %.2f, %.2f" % center)
504
- await asyncio.gather(
505
- self.flash_async(0.25),
507
+ script = 'sessionStorage.getItem("pxsid") !== null;'
508
+ using_px = await self.tab.evaluate(script)
509
+ if not using_px:
510
+ asyncio.create_task(self.flash_async(0.25))
511
+ asyncio.create_task(
506
512
  self._tab.send(
507
513
  cdp.input_.dispatch_mouse_event(
508
514
  "mousePressed",
@@ -514,6 +520,8 @@ class Element:
514
520
  click_count=1,
515
521
  )
516
522
  ),
523
+ )
524
+ asyncio.create_task(
517
525
  self._tab.send(
518
526
  cdp.input_.dispatch_mouse_event(
519
527
  "mouseReleased",
@@ -558,11 +566,16 @@ class Element:
558
566
  logger.debug("Clicking on location: %.2f, %.2f" % center_pos)
559
567
  else:
560
568
  logger.debug("Clicking on location: %.2f, %.2f" % (x_pos, y_pos))
561
- await asyncio.gather(
562
- self.flash_async(
563
- x_offset=x_offset - int(width / 2),
564
- y_offset=y_offset - int(height / 2),
565
- ),
569
+ script = 'sessionStorage.getItem("pxsid") !== null;'
570
+ using_px = await self.tab.evaluate(script)
571
+ if not using_px:
572
+ asyncio.create_task(
573
+ self.flash_async(
574
+ x_offset=x_offset - (width / 2),
575
+ y_offset=y_offset - (height / 2),
576
+ ),
577
+ )
578
+ asyncio.create_task(
566
579
  self._tab.send(
567
580
  cdp.input_.dispatch_mouse_event(
568
581
  "mousePressed",
@@ -573,7 +586,9 @@ class Element:
573
586
  buttons=buttons,
574
587
  click_count=1,
575
588
  )
576
- ),
589
+ )
590
+ )
591
+ asyncio.create_task(
577
592
  self._tab.send(
578
593
  cdp.input_.dispatch_mouse_event(
579
594
  "mouseReleased",
@@ -602,16 +617,13 @@ class Element:
602
617
  *center,
603
618
  self,
604
619
  )
605
- await self._tab.send(
606
- cdp.input_.dispatch_mouse_event(
607
- "mouseMoved", x=center[0], y=center[1]
608
- )
609
- )
610
- await self._tab.sleep(0.05)
611
- await self._tab.send(
612
- cdp.input_.dispatch_mouse_event(
613
- "mouseReleased", x=center[0], y=center[1]
614
- )
620
+ await asyncio.gather(
621
+ self._tab.send(
622
+ cdp.input_.dispatch_mouse_event(
623
+ "mouseMoved", x=center[0], y=center[1]
624
+ )
625
+ ),
626
+ self._tab.sleep(0.05),
615
627
  )
616
628
 
617
629
  async def mouse_drag_async(
@@ -806,7 +818,10 @@ class Element:
806
818
 
807
819
  async def get_html_async(self):
808
820
  return await self._tab.send(
809
- cdp.dom.get_outer_html(backend_node_id=self.backend_node_id)
821
+ cdp.dom.get_outer_html(
822
+ backend_node_id=self.backend_node_id,
823
+ include_shadow_dom=True,
824
+ )
810
825
  )
811
826
 
812
827
  @property
@@ -825,7 +840,7 @@ class Element:
825
840
  """
826
841
  with suppress(Exception):
827
842
  if self.node.node_name.lower() in ["input", "textarea"]:
828
- input_node = self.node.shadow_roots[0].children[0].children[0]
843
+ input_node = self.node.shadow_roots[0].children[-1].children[0]
829
844
  if input_node:
830
845
  return input_node.node_value
831
846
  text_nodes = util.filter_recurse_all(
@@ -838,7 +853,7 @@ class Element:
838
853
  """Same as text(). Kept for backwards compatibility."""
839
854
  with suppress(Exception):
840
855
  if self.node.node_name.lower() in ["input", "textarea"]:
841
- input_node = self.node.shadow_roots[0].children[0].children[0]
856
+ input_node = self.node.shadow_roots[0].children[-1].children[0]
842
857
  if input_node:
843
858
  return input_node.node_value
844
859
  text_nodes = util.filter_recurse_all(