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
@@ -2,6 +2,7 @@
2
2
  from __future__ import annotations
3
3
  import asyncio
4
4
  import atexit
5
+ import fasteners
5
6
  import http.cookiejar
6
7
  import json
7
8
  import logging
@@ -15,7 +16,10 @@ import urllib.parse
15
16
  import urllib.request
16
17
  import warnings
17
18
  from collections import defaultdict
19
+ from contextlib import suppress
18
20
  from seleniumbase import config as sb_config
21
+ from seleniumbase.fixtures import constants
22
+ from seleniumbase.fixtures import shared_utils
19
23
  from typing import List, Optional, Set, Tuple, Union
20
24
  import mycdp as cdp
21
25
  from . import cdp_util as util
@@ -34,7 +38,7 @@ def get_registered_instances():
34
38
  def deconstruct_browser():
35
39
  for _ in __registered__instances__:
36
40
  if not _.stopped:
37
- _.stop()
41
+ _.stop(deconstruct=True)
38
42
  for attempt in range(5):
39
43
  try:
40
44
  if _.config and not _.config.uses_custom_data_dir:
@@ -251,6 +255,53 @@ class Browser:
251
255
  )
252
256
  self.targets.remove(current_tab)
253
257
 
258
+ def get_rd_host(self):
259
+ return self.config.host
260
+
261
+ def get_rd_port(self):
262
+ return self.config.port
263
+
264
+ def get_rd_url(self):
265
+ host = self.config.host
266
+ port = self.config.port
267
+ return f"http://{host}:{port}"
268
+
269
+ def get_endpoint_url(self):
270
+ return self.get_rd_url()
271
+
272
+ def get_port(self):
273
+ return self.get_rd_port()
274
+
275
+ async def set_auth(self, username, password, tab):
276
+ async def auth_challenge_handler(event: cdp.fetch.AuthRequired):
277
+ await tab.send(
278
+ cdp.fetch.continue_with_auth(
279
+ request_id=event.request_id,
280
+ auth_challenge_response=cdp.fetch.AuthChallengeResponse(
281
+ response="ProvideCredentials",
282
+ username=username,
283
+ password=password,
284
+ ),
285
+ )
286
+ )
287
+
288
+ async def req_paused(event: cdp.fetch.RequestPaused):
289
+ await tab.send(
290
+ cdp.fetch.continue_request(request_id=event.request_id)
291
+ )
292
+
293
+ tab.add_handler(
294
+ cdp.fetch.RequestPaused,
295
+ lambda event: asyncio.create_task(req_paused(event)),
296
+ )
297
+
298
+ tab.add_handler(
299
+ cdp.fetch.AuthRequired,
300
+ lambda event: asyncio.create_task(auth_challenge_handler(event)),
301
+ )
302
+
303
+ await tab.send(cdp.fetch.enable(handle_auth_requests=True))
304
+
254
305
  async def get(
255
306
  self,
256
307
  url="about:blank",
@@ -266,6 +317,7 @@ class Browser:
266
317
  :param new_window: Open new window
267
318
  :return: Page
268
319
  """
320
+ await asyncio.sleep(0.005)
269
321
  if url and ":" not in url:
270
322
  url = "https://" + url
271
323
  if new_tab or new_window:
@@ -277,115 +329,210 @@ class Browser:
277
329
  )
278
330
  connection: tab.Tab = next(
279
331
  filter(
280
- lambda item: item.type_ == "page" and item.target_id == target_id, # noqa
332
+ lambda item: (
333
+ item.type_ == "page" and item.target_id == target_id
334
+ ),
281
335
  self.targets,
282
336
  )
283
337
  )
284
338
  connection.browser = self
285
339
  else:
286
- # First tab from browser.tabs
287
- connection: tab.Tab = next(
288
- filter(lambda item: item.type_ == "page", self.targets)
340
+ try:
341
+ # Most recently opened tab
342
+ connection = self.targets[-1]
343
+ await connection.sleep(0.005)
344
+ except Exception:
345
+ # First tab from browser.tabs
346
+ connection: tab.Tab = next(
347
+ filter(lambda item: item.type_ == "page", self.targets)
348
+ )
349
+ await connection.sleep(0.005)
350
+ _cdp_timezone = None
351
+ _cdp_user_agent = ""
352
+ _cdp_locale = None
353
+ _cdp_platform = None
354
+ _cdp_disable_csp = None
355
+ _cdp_geolocation = None
356
+ _cdp_mobile_mode = None
357
+ _cdp_recorder = None
358
+ _cdp_ad_block = None
359
+ if getattr(sb_config, "_cdp_timezone", None):
360
+ _cdp_timezone = sb_config._cdp_timezone
361
+ if getattr(sb_config, "_cdp_user_agent", None):
362
+ _cdp_user_agent = sb_config._cdp_user_agent
363
+ if getattr(sb_config, "_cdp_locale", None):
364
+ _cdp_locale = sb_config._cdp_locale
365
+ if getattr(sb_config, "_cdp_platform", None):
366
+ _cdp_platform = sb_config._cdp_platform
367
+ if getattr(sb_config, "_cdp_geolocation", None):
368
+ _cdp_geolocation = sb_config._cdp_geolocation
369
+ if getattr(sb_config, "_cdp_mobile_mode", None):
370
+ _cdp_mobile_mode = sb_config._cdp_mobile_mode
371
+ if getattr(sb_config, "ad_block_on", None):
372
+ _cdp_ad_block = sb_config.ad_block_on
373
+ if getattr(sb_config, "disable_csp", None):
374
+ _cdp_disable_csp = sb_config.disable_csp
375
+ if "timezone" in kwargs:
376
+ _cdp_timezone = kwargs["timezone"]
377
+ elif "tzone" in kwargs:
378
+ _cdp_timezone = kwargs["tzone"]
379
+ if "user_agent" in kwargs:
380
+ _cdp_user_agent = kwargs["user_agent"]
381
+ elif "agent" in kwargs:
382
+ _cdp_user_agent = kwargs["agent"]
383
+ if "locale" in kwargs:
384
+ _cdp_locale = kwargs["locale"]
385
+ elif "lang" in kwargs:
386
+ _cdp_locale = kwargs["lang"]
387
+ elif "locale_code" in kwargs:
388
+ _cdp_locale = kwargs["locale_code"]
389
+ if "platform" in kwargs:
390
+ _cdp_platform = kwargs["platform"]
391
+ elif "plat" in kwargs:
392
+ _cdp_platform = kwargs["plat"]
393
+ if "disable_csp" in kwargs:
394
+ _cdp_disable_csp = kwargs["disable_csp"]
395
+ if "geolocation" in kwargs:
396
+ _cdp_geolocation = kwargs["geolocation"]
397
+ elif "geoloc" in kwargs:
398
+ _cdp_geolocation = kwargs["geoloc"]
399
+ if "ad_block" in kwargs:
400
+ _cdp_ad_block = kwargs["ad_block"]
401
+ if "mobile" in kwargs:
402
+ _cdp_mobile_mode = kwargs["mobile"]
403
+ if "recorder" in kwargs:
404
+ _cdp_recorder = kwargs["recorder"]
405
+ await connection.sleep(0.01)
406
+ await connection.send(cdp.network.enable())
407
+ await connection.sleep(0.01)
408
+ if _cdp_timezone:
409
+ await connection.set_timezone(_cdp_timezone)
410
+ if _cdp_locale:
411
+ await connection.set_locale(_cdp_locale)
412
+ if _cdp_user_agent or _cdp_locale or _cdp_platform:
413
+ await connection.set_user_agent(
414
+ user_agent=_cdp_user_agent,
415
+ accept_language=_cdp_locale,
416
+ platform=_cdp_platform,
289
417
  )
290
- _cdp_timezone = None
291
- _cdp_user_agent = ""
292
- _cdp_locale = None
293
- _cdp_platform = None
294
- _cdp_geolocation = None
295
- _cdp_recorder = None
296
- if (
297
- hasattr(sb_config, "_cdp_timezone") and sb_config._cdp_timezone
298
- ):
299
- _cdp_timezone = sb_config._cdp_timezone
300
- if (
301
- hasattr(sb_config, "_cdp_user_agent")
302
- and sb_config._cdp_user_agent
303
- ):
304
- _cdp_user_agent = sb_config._cdp_user_agent
305
- if hasattr(sb_config, "_cdp_locale") and sb_config._cdp_locale:
306
- _cdp_locale = sb_config._cdp_locale
307
- if hasattr(sb_config, "_cdp_platform") and sb_config._cdp_platform:
308
- _cdp_platform = sb_config._cdp_platform
309
- if (
310
- hasattr(sb_config, "_cdp_geolocation")
311
- and sb_config._cdp_geolocation
312
- ):
313
- _cdp_geolocation = sb_config._cdp_geolocation
314
- if "timezone" in kwargs:
315
- _cdp_timezone = kwargs["timezone"]
316
- elif "tzone" in kwargs:
317
- _cdp_timezone = kwargs["tzone"]
318
- if "user_agent" in kwargs:
319
- _cdp_user_agent = kwargs["user_agent"]
320
- elif "agent" in kwargs:
321
- _cdp_user_agent = kwargs["agent"]
322
- if "locale" in kwargs:
323
- _cdp_locale = kwargs["locale"]
324
- elif "lang" in kwargs:
325
- _cdp_locale = kwargs["lang"]
326
- elif "locale_code" in kwargs:
327
- _cdp_locale = kwargs["locale_code"]
328
- if "platform" in kwargs:
329
- _cdp_platform = kwargs["platform"]
330
- elif "plat" in kwargs:
331
- _cdp_platform = kwargs["plat"]
332
- if "geolocation" in kwargs:
333
- _cdp_geolocation = kwargs["geolocation"]
334
- elif "geoloc" in kwargs:
335
- _cdp_geolocation = kwargs["geoloc"]
336
- if "recorder" in kwargs:
337
- _cdp_recorder = kwargs["recorder"]
338
- if _cdp_timezone:
339
- await connection.send(cdp.page.navigate("about:blank"))
340
- await connection.set_timezone(_cdp_timezone)
341
- if _cdp_locale:
342
- await connection.set_locale(_cdp_locale)
343
- if _cdp_user_agent or _cdp_locale or _cdp_platform:
344
- await connection.send(cdp.page.navigate("about:blank"))
345
- await connection.set_user_agent(
346
- user_agent=_cdp_user_agent,
347
- accept_language=_cdp_locale,
348
- platform=_cdp_platform,
418
+ if _cdp_ad_block:
419
+ await connection.send(cdp.network.set_blocked_urls(
420
+ urls=[
421
+ "*.cloudflareinsights.com*",
422
+ "*.googlesyndication.com*",
423
+ "*.googletagmanager.com*",
424
+ "*.google-analytics.com*",
425
+ "*.amazon-adsystem.com*",
426
+ "*.adsafeprotected.com*",
427
+ "*.ads.linkedin.com*",
428
+ "*.casalemedia.com*",
429
+ "*.doubleclick.net*",
430
+ "*.admanmedia.com*",
431
+ "*.quantserve.com*",
432
+ "*.fastclick.net*",
433
+ "*.snigelweb.com*",
434
+ "*.bidswitch.net*",
435
+ "*.360yield.com*",
436
+ "*.adthrive.com*",
437
+ "*.pubmatic.com*",
438
+ "*.id5-sync.com*",
439
+ "*.dotomi.com*",
440
+ "*.adsrvr.org*",
441
+ "*.atmtd.com*",
442
+ "*.liadm.com*",
443
+ "*.loopme.me*",
444
+ "*.adnxs.com*",
445
+ "*.openx.net*",
446
+ "*.tapad.com*",
447
+ "*.3lift.com*",
448
+ "*.turn.com*",
449
+ "*.2mdn.net*",
450
+ "*.cpx.to*",
451
+ "*.ad.gt*",
452
+ ]
453
+ ))
454
+ if _cdp_geolocation:
455
+ await connection.set_geolocation(_cdp_geolocation)
456
+ if _cdp_disable_csp:
457
+ await connection.send(cdp.page.set_bypass_csp(enabled=True))
458
+ if _cdp_mobile_mode:
459
+ await connection.send(
460
+ cdp.emulation.set_device_metrics_override(
461
+ width=412, height=732, device_scale_factor=3, mobile=True
349
462
  )
350
- if _cdp_geolocation:
351
- await connection.send(cdp.page.navigate("about:blank"))
352
- await connection.set_geolocation(_cdp_geolocation)
353
- # Use the tab to navigate to new url
463
+ )
464
+ # (The code below is for the Chrome 142 extension fix)
465
+ if (
466
+ getattr(sb_config, "_cdp_proxy", None)
467
+ and "@" in sb_config._cdp_proxy
468
+ and "auth" not in kwargs
469
+ ):
470
+ username_and_password = sb_config._cdp_proxy.split("@")[0]
471
+ proxy_user = username_and_password.split(":")[0]
472
+ proxy_pass = username_and_password.split(":")[1]
354
473
  if (
355
- hasattr(sb_config, "_cdp_proxy")
356
- and "@" in sb_config._cdp_proxy
357
- and sb_config._cdp_proxy
358
- and "auth" not in kwargs
474
+ hasattr(self.main_tab, "_last_auth")
475
+ and self.main_tab._last_auth == username_and_password
359
476
  ):
360
- username_and_password = sb_config._cdp_proxy.split("@")[0]
361
- proxy_user = username_and_password.split(":")[0]
362
- proxy_pass = username_and_password.split(":")[1]
363
- await connection.set_auth(
364
- proxy_user, proxy_pass, self.tabs[0]
365
- )
477
+ pass # Auth was already set
478
+ else:
479
+ self.main_tab._last_auth = username_and_password
480
+ await self.set_auth(proxy_user, proxy_pass, self.tabs[0])
366
481
  time.sleep(0.25)
367
- elif "auth" in kwargs and kwargs["auth"] and ":" in kwargs["auth"]:
368
- username_and_password = kwargs["auth"]
369
- proxy_user = username_and_password.split(":")[0]
370
- proxy_pass = username_and_password.split(":")[1]
371
- await connection.set_auth(
372
- proxy_user, proxy_pass, self.tabs[0]
373
- )
482
+ if "auth" in kwargs and kwargs["auth"] and ":" in kwargs["auth"]:
483
+ username_and_password = kwargs["auth"]
484
+ proxy_user = username_and_password.split(":")[0]
485
+ proxy_pass = username_and_password.split(":")[1]
486
+ if (
487
+ hasattr(self.main_tab, "_last_auth")
488
+ and self.main_tab._last_auth == username_and_password
489
+ ):
490
+ pass # Auth was already set
491
+ else:
492
+ self.main_tab._last_auth = username_and_password
493
+ await self.set_auth(proxy_user, proxy_pass, self.tabs[0])
374
494
  time.sleep(0.25)
375
- frame_id, loader_id, *_ = await connection.send(
376
- cdp.page.navigate(url)
495
+ await connection.sleep(0.15)
496
+ frame_id, loader_id, *_ = await connection.send(
497
+ cdp.page.navigate(url)
498
+ )
499
+ major_browser_version = None
500
+ try:
501
+ major_browser_version = (
502
+ int(self.info["Browser"].split("/")[-1].split(".")[0])
377
503
  )
378
- if _cdp_recorder:
379
- pass # (The code below was for the Chrome 137 extension fix)
380
- '''from seleniumbase.js_code.recorder_js import recorder_js
381
- recorder_code = (
382
- """window.onload = function() { %s };""" % recorder_js
504
+ except Exception:
505
+ pass
506
+ if (
507
+ _cdp_recorder
508
+ and (
509
+ not hasattr(sb_config, "browser")
510
+ or (
511
+ sb_config.browser == "chrome"
512
+ and (
513
+ not major_browser_version
514
+ or major_browser_version >= 142
515
+ )
383
516
  )
384
- await connection.send(cdp.runtime.evaluate(recorder_code))'''
385
- # Update the frame_id on the tab
386
- connection.frame_id = frame_id
387
- connection.browser = self
517
+ )
518
+ ):
519
+ # (The code below is for the Chrome 142 extension fix)
520
+ from seleniumbase.js_code.recorder_js import recorder_js
521
+ recorder_code = (
522
+ """window.onload = function() { %s };""" % recorder_js
523
+ )
524
+ await connection.send(
525
+ cdp.page.add_script_to_evaluate_on_new_document(recorder_code)
526
+ )
527
+ await connection.sleep(0.25)
528
+ await self.wait(0.05)
529
+ await connection.send(cdp.runtime.evaluate(recorder_js))
530
+ # Update the frame_id on the tab
531
+ connection.frame_id = frame_id
532
+ connection.browser = self
533
+ # Give settings time to take effect
388
534
  await connection.sleep(0.25)
535
+ await self.wait(0.05)
389
536
  return connection
390
537
 
391
538
  async def start(self=None) -> Browser:
@@ -437,7 +584,7 @@ class Browser:
437
584
  ) # noqa
438
585
  exe = self.config.browser_executable_path
439
586
  params = self.config()
440
- logger.info(
587
+ logger.debug(
441
588
  "Starting\n\texecutable :%s\n\narguments:\n%s",
442
589
  exe,
443
590
  "\n\t".join(params),
@@ -471,20 +618,29 @@ class Browser:
471
618
  else:
472
619
  break
473
620
  if not self.info:
474
- raise Exception(
475
- (
476
- """
477
- --------------------------------
478
- Failed to connect to the browser
479
- --------------------------------
480
- """
621
+ chromium = "Chromium"
622
+ if getattr(sb_config, "_cdp_browser", None):
623
+ chromium = sb_config._cdp_browser
624
+ chromium = chromium[0].upper() + chromium[1:]
625
+ message = "Failed to connect to the browser"
626
+ if not exe or not os.path.exists(exe):
627
+ message = (
628
+ "%s executable not found. Is it installed?" % chromium
481
629
  )
630
+ dash_len = len(message)
631
+ dashes = "-" * dash_len
632
+ raise Exception(
633
+ """
634
+ %s
635
+ %s
636
+ %s
637
+ """ % (dashes, message, dashes)
482
638
  )
483
639
  self.connection = Connection(
484
- self.info.webSocketDebuggerUrl, _owner=self
640
+ self.info.webSocketDebuggerUrl, browser=self
485
641
  )
486
642
  if self.config.autodiscover_targets:
487
- logger.info("Enabling autodiscover targets")
643
+ logger.debug("Enabling autodiscover targets")
488
644
  self.connection.handlers[cdp.target.TargetInfoChanged] = [
489
645
  self._handle_target_update
490
646
  ]
@@ -580,8 +736,11 @@ class Browser:
580
736
  try:
581
737
  import mss
582
738
  except Exception:
583
- from seleniumbase.fixtures import shared_utils
584
- shared_utils.pip_install("mss")
739
+ pip_find_lock = fasteners.InterProcessLock(
740
+ constants.PipInstall.FINDLOCK
741
+ )
742
+ with pip_find_lock: # Prevent issues with multiple processes
743
+ shared_utils.pip_install("mss")
585
744
  import mss
586
745
  m = mss.mss()
587
746
  screen, screen_width, screen_height = 3 * (None,)
@@ -655,7 +814,7 @@ class Browser:
655
814
  f"/{t.target_id}"
656
815
  ),
657
816
  target=t,
658
- _owner=self,
817
+ browser=self,
659
818
  )
660
819
  )
661
820
  await asyncio.sleep(0)
@@ -690,11 +849,17 @@ class Browser:
690
849
  else:
691
850
  del self._i
692
851
 
693
- def stop(self):
852
+ def stop(self, deconstruct=False):
853
+ if (
854
+ not hasattr(sb_config, "_closed_connection_ids")
855
+ or not isinstance(sb_config._closed_connection_ids, list)
856
+ ):
857
+ sb_config._closed_connection_ids = []
858
+ connection_id = None
859
+ with suppress(Exception):
860
+ connection_id = self.connection.websocket.id.hex
861
+ close_success = False
694
862
  try:
695
- # asyncio.get_running_loop().create_task(
696
- # self.connection.send(cdp.browser.close())
697
- # )
698
863
  if self.connection:
699
864
  asyncio.get_event_loop().create_task(self.connection.aclose())
700
865
  logger.debug(
@@ -703,23 +868,26 @@ class Browser:
703
868
  except RuntimeError:
704
869
  if self.connection:
705
870
  try:
706
- # asyncio.run(self.connection.send(cdp.browser.close()))
707
871
  asyncio.run(self.connection.aclose())
708
872
  logger.debug("Closed the connection using asyncio.run()")
709
873
  except Exception:
710
874
  pass
711
875
  for _ in range(3):
712
876
  try:
713
- self._process.terminate()
714
- logger.info(
715
- "Terminated browser with pid %d successfully."
716
- % self._process.pid
717
- )
718
- break
877
+ if connection_id not in sb_config._closed_connection_ids:
878
+ self._process.terminate()
879
+ logger.debug(
880
+ "Terminated browser with pid %d successfully."
881
+ % self._process.pid
882
+ )
883
+ if connection_id:
884
+ sb_config._closed_connection_ids.append(connection_id)
885
+ close_success = True
886
+ break
719
887
  except (Exception,):
720
888
  try:
721
889
  self._process.kill()
722
- logger.info(
890
+ logger.debug(
723
891
  "Killed browser with pid %d successfully."
724
892
  % self._process.pid
725
893
  )
@@ -728,14 +896,14 @@ class Browser:
728
896
  try:
729
897
  if hasattr(self, "browser_process_pid"):
730
898
  os.kill(self._process_pid, 15)
731
- logger.info(
899
+ logger.debug(
732
900
  "Killed browser with pid %d "
733
901
  "using signal 15 successfully."
734
902
  % self._process.pid
735
903
  )
736
904
  break
737
905
  except (TypeError,):
738
- logger.info("typerror", exc_info=True)
906
+ logger.info("TypeError", exc_info=True)
739
907
  pass
740
908
  except (PermissionError,):
741
909
  logger.info(
@@ -744,12 +912,45 @@ class Browser:
744
912
  )
745
913
  pass
746
914
  except (ProcessLookupError,):
747
- logger.info("Process lookup failure!")
915
+ logger.info("ProcessLookupError")
748
916
  pass
749
917
  except (Exception,):
750
918
  raise
751
919
  self._process = None
752
920
  self._process_pid = None
921
+ if (
922
+ hasattr(sb_config, "_xvfb_users")
923
+ and isinstance(sb_config._xvfb_users, int)
924
+ and close_success
925
+ and hasattr(sb_config, "_virtual_display")
926
+ and sb_config._virtual_display
927
+ ):
928
+ sb_config._xvfb_users -= 1
929
+ if sb_config._xvfb_users < 0:
930
+ sb_config._xvfb_users = 0
931
+ if (
932
+ shared_utils.is_linux()
933
+ and (
934
+ hasattr(sb_config, "_virtual_display")
935
+ and sb_config._virtual_display
936
+ and hasattr(sb_config._virtual_display, "stop")
937
+ )
938
+ and sb_config._xvfb_users == 0
939
+ ):
940
+ try:
941
+ sb_config._virtual_display.stop()
942
+ sb_config._virtual_display = None
943
+ sb_config.headless_active = False
944
+ except AttributeError:
945
+ pass
946
+ except Exception:
947
+ pass
948
+ if (
949
+ deconstruct
950
+ and connection_id
951
+ and connection_id in sb_config._closed_connection_ids
952
+ ):
953
+ sb_config._closed_connection_ids.remove(connection_id)
753
954
 
754
955
  def quit(self):
755
956
  self.stop()
@@ -781,7 +982,7 @@ class CookieJar:
781
982
  """
782
983
  connection = None
783
984
  for _tab in self._browser.tabs:
784
- if hasattr(_tab, "closed") and _tab.closed:
985
+ if getattr(_tab, "closed", None):
785
986
  continue
786
987
  connection = _tab
787
988
  break
@@ -811,7 +1012,7 @@ class CookieJar:
811
1012
  """
812
1013
  connection = None
813
1014
  for _tab in self._browser.tabs:
814
- if hasattr(_tab, "closed") and _tab.closed:
1015
+ if getattr(_tab, "closed", None):
815
1016
  continue
816
1017
  connection = _tab
817
1018
  break
@@ -838,7 +1039,7 @@ class CookieJar:
838
1039
  save_path = pathlib.Path(file).resolve()
839
1040
  connection = None
840
1041
  for _tab in self._browser.tabs:
841
- if hasattr(_tab, "closed") and _tab.closed:
1042
+ if getattr(_tab, "closed", None):
842
1043
  continue
843
1044
  connection = _tab
844
1045
  break
@@ -884,7 +1085,7 @@ class CookieJar:
884
1085
  included_cookies = []
885
1086
  connection = None
886
1087
  for _tab in self._browser.tabs:
887
- if hasattr(_tab, "closed") and _tab.closed:
1088
+ if getattr(_tab, "closed", None):
888
1089
  continue
889
1090
  connection = _tab
890
1091
  break
@@ -907,7 +1108,7 @@ class CookieJar:
907
1108
  """
908
1109
  connection = None
909
1110
  for _tab in self._browser.tabs:
910
- if hasattr(_tab, "closed") and _tab.closed:
1111
+ if getattr(_tab, "closed", None):
911
1112
  continue
912
1113
  connection = _tab
913
1114
  break